高并发高性能接口中,异步打印并采集业务日志的实现方案

一、背景

高并发接口中,为了提高接口的高性能,在需要保存审计及操作记录的时候,往往有以下常见方案:

  • 保存到redis数据库
  • 异步保存到mysql/mongodb/es等数据库
  • logger打印业务日志,采集与展示则交由elk模块

对于第一种方案,接口的高性能依赖于redis的性能;第二种方案的关键在于异步,可以是基于事件驱动机制,常见的CQRS设计就是例子;
本文则是介绍第三种方案,不同的是,我们的数据展示是在业务管理后台,而非kibana。

另外,生产环境,我们的应用程序是部署在k8s容器。

二、设计方案

在这里插入图片描述

  • 1、服务打印日志,持久化到nfs
  • 2、filebeat先挂载nfs,再配置采集日志
  • 3、kafka作为数据采集的削峰填谷的角色
  • 4、Logstash读取kafka的数据,解析并存储到指定es数据库
  • 5、管理后台连接并读取es数据库,展示数据

三、打印业务日志

使用logback使用不同的Logger对象,区分普通的jvm日志,把业务日志输出到指定的日志文件。

  • logback-spring.xml
<?xml version="1.0" encoding="UTF-8"?>
<!--该日志将日志级别不同的log信息保存到不同的文件中 -->
<configuration>
    <include resource="org/springframework/boot/logging/logback/defaults.xml"/>

    <springProperty scope="context" name="springAppName"
                    source="spring.application.name"/>

    <springProperty scope="context" name="log_dir" source="logging.login.path" defaultValue="../logs"/>

    <!-- 日志输出位置 -->
    <property name="LOG_FILE" value="${log_dir}/${springAppName}-login"/>
    
    <!-- 文件的日志输出样式 -->
    <property name="FILE_LOG_PATTERN"
              value="%d{${LOG_DATEFORMAT_PATTERN:-yyyy-MM-dd HH:mm:ss.SSS}} ${LOG_LEVEL_PATTERN:-%5p} ${PID:- } --- [%t] %-40.40logger{39} : %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}"/>
              
    <!-- 为logstash输出的JSON格式的Appender -->
    <appender name="logstash"
              class="ch.qos.logback.core.rolling.RollingFileAppender">
        <file>${LOG_FILE}.log</file>
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <!--日志文件输出的文件名 -->
            <fileNamePattern>${LOG_FILE}.log.%d{yyyy-MM-dd}.%i.gz</fileNamePattern>
            <timeBasedFileNamingAndTriggeringPolicy
                    class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
                <maxFileSize>20MB</maxFileSize>
            </timeBasedFileNamingAndTriggeringPolicy>
            <!--日志文件保留天数 -->
            <MaxHistory>15</MaxHistory>
        </rollingPolicy>
        <!-- 日志输出编码 -->
        <encoder>
            <pattern>${FILE_LOG_PATTERN}</pattern>
            <charset>utf8</charset>
        </encoder>
    </appender>

    <logger name="com.xxx.event.handler.LoginLogEventHandler" level="INFO" additivity="false">
        <appender-ref ref="logstash"/>
    </logger>
</configuration>

普通的jvm日志选择输出在console控制台,然后将流重定向到指定的日志文件里。

  • console

    <!-- 控制台的日志输出样式 -->
    <property name="CONSOLE_LOG_PATTERN"
              value="%clr(%d{yyyy-MM-dd HH:mm:ss.SSS}){faint} %clr(${LOG_LEVEL_PATTERN:-%5p}) %clr(${PID:- }){magenta} %clr(---){faint} %clr([%15.15t]){faint} %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}}"/>
             
    <!-- 控制台输出 -->
    <appender name="console" class="ch.qos.logback.core.ConsoleAppender">
        <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
            <level>INFO</level>
        </filter>
        <!-- 日志输出编码 -->
        <encoder>
            <pattern>${CONSOLE_LOG_PATTERN}</pattern>
            <charset>utf8</charset>
        </encoder>
    </appender>
    
    <!-- 日志输出级别 -->
    <root level="INFO">
        <appender-ref ref="console"/>
    </root>

对于业务应用程序来说,所做的事情只需要打印一条日志即可,其高性能可想而知。

四、flebeat采集日志

申请一台机器,专门采集日志,而非在每个Pod容器里使用sidecar边车模式部署filebeat容器。

当然,这台机器必须要先挂载nfs。(nfs是购买的阿里云“文件存储NAS”服务)

在这里插入图片描述

下面是filebeat.yml示例:

# cat /etc/filebeat/filebeat.yml
filebeat.prospectors:
- input_type: log
  paths:
    - /login_output_service/auth-service.log
  multiline.pattern: '^[0-9]{4}-[0-9]{2}-[0-9]{2}'
  multiline.negate: true
  multiline.match: after
  document_type: login_out_logs

output.kafka:
  enabled: true
  hosts: ["xxx.alikafka.aliyuncs.com:9092","yyy.alikafka.aliyuncs.com:9092","zzz.alikafka.aliyuncs.com:9092"]
  topic: 'login_out_logs'

五、kafka中间件

当然,它作为数据采集的中间件,所起的作用是削峰填谷,可以说,在整个链路中不是必须的。

建议你采用mq作为数据采集的过渡。

这里插一句,我们可以通过kafka的消息内容,反查出filebeat所在的机器。

原本的思路是想通过文件存储NAS查找ECS的挂载详情,可惜没找到入口。(也就是说,无法得到哪些ECS挂载了NFS,只能选择通过Kafka)

忘记了filebeat是部署在哪个机器,好在我们知道kafka,因为购买的是阿里云服务。

进入“消息队列Kafka版”

在这里插入图片描述
查看消息体内容:

在这里插入图片描述

{“@timestamp”:“2024-04-01T20:26:24.956Z”,“beat”:{“hostname”:“iZbp12202mumv2j30jv6l8Z”,“name”:“iZbp12202mumv2j30jv6l8Z”,“version”:“5.6.9”},“input_type”:“log”,“message”:“2024-04-02 04:25:18.486 INFO [auth-service,] 8 — [AuthService_EventPool_757886445] c.x.s.a.i.e.h.LoginLogEventHandler : LoginLogEventHandler - writeLoginLog - 登录登出日志 [id=1774895838313730083\tdate=20240402042518\tosVersion= 13]”,“offset”:10016,“source”:“/opt/server/auth-service.log”,“type”:“login_out_logs”}

从这里可以看到,filebeat机器hostname是iZbp12202mumv2j30jv6l8Z,注意需要转换,实际上是“i-bp12202mumv2j30jv6l8”。

另外从source字段,可以知道日志的源文件在哪。

  • 根据hostname查找ecs所在地

进入“云服务器ECS”

在这里插入图片描述
通过这两步,我们就通过kafka消息中间件,反查得到filebeat所在机器。关于filebeat在上一步已介绍,下面将介绍logstash的配置。

六、logstash存储业务日志

Logstash主要是用来解析业务日志,先读取kafka的数据,然后存储到es数据库。

另外logstash支持多个配置文件,建议你分开配置并加载。

# 默认配置
logstash -f /usr/share/logstash/pipeline/logstash.conf

# 修改为通配符*.conf
logstash -f /usr/share/logstash/pipeline/*.conf
  • auth.conf 详情
# cat /usr/local/logstash-6.8.23/config/auth.conf

input {
     kafka {
        bootstrap_servers => "xxx.alikafka.aliyuncs.com:9092","yyy.alikafka.aliyuncs.com:9092","zzz.alikafka.aliyuncs.com:9092"
        codec => "json"
        type => "_doc"
        topics => "login_out_logs"
    }
}

filter {
          grok {
                match => {"message"=>"%{JAVA_DATE:logdate}\s+%{XHJVM_VALUE:logType}\s.*登录登出日志\s+\[id=(%{FIELD_VALUE:id}?\s+)date=(%{FIELD_VALUE:date}?\s+)osVersion=(%{FIELD_VALUE:osVersion}?)\]"}
  }

# 增加采集时间戳,去掉一些非必要的字段
  date {
       match => ["logdate", "yyyy-MM-dd HH:mm:ss.SSS"]
       target => "@timestamp"
       remove_field => [ "logdate", "logType", "host", "@version", "@timestamp", "path", "message", "beat", "source", "input_type", "offset"]
       }

 }

output{
  elasticsearch{
    # es的地址
    hosts => ["10.xxx.xxx.126:9200"]
    index => "auth_login_out_logs_write"
    codec => line { format => "%{message}"}
    document_type => "_doc"
    # es的用户名
    user => "{username}"
    # es的密码
    password => "{password}"
    document_id => "%{id}"
  }
}

还好无意中找到了logstash所在机器,要不然,要找出logstash还真是麻烦。。。

因为这里把很多“无用”的字段remove掉了,所以在es数据库中也无法得到采集的链路详情。

七、总结

本文试着通过另外一个解决方案来保证高并发接口的高性能,顺便介绍了如何找回filebeat所在机器的过程。同时提醒我们,在采集数据的时候,或者程序开发的时候,特别是当你的链路很长的时候,把每个环节的主体信息记录下来是多么的重要。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/535301.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

基于Springboot的箱包存储系统(有报告)。Javaee项目,springboot项目。

演示视频&#xff1a; 基于Springboot的箱包存储系统&#xff08;有报告&#xff09;。Javaee项目&#xff0c;springboot项目。 项目介绍&#xff1a; 采用M&#xff08;model&#xff09;V&#xff08;view&#xff09;C&#xff08;controller&#xff09;三层体系结构&…

【web开发01】前端开发HTML、CSS-新浪新闻的实现

前端开发HTML、CSS-新浪新闻的实现 1 实现标题排版1.1图片连接的三种方式1.2 具体代码1.3 总结1.< h1>到< h6>是标题从大到小 2 实现标题样式2.1 css的三种引入方式2.2 颜色的三种表示2.3 css选择器2.4 超链接 3 实现正文排版3.1 视频标签 video3.2 音频标签 audio…

Spring高手之路17——动态代理的艺术与实践

文章目录 1. 背景2. JDK动态代理2.1 定义和演示2.2 不同方法分别代理2.3 熔断限流和日志监控 3. CGLIB动态代理3.1 定义和演示3.2 不同方法分别代理&#xff08;对比JDK动态代理写法&#xff09;3.3 熔断限流和日志监控&#xff08;对比JDK动态代理写法&#xff09; 4. 动态代理…

功能强大:JMeter 常用插件全解析

JMeter 作为一个开源的接口性能测试工具&#xff0c;其本身的小巧和灵活性给了测试人员很大的帮助&#xff0c;但其本身作为一个开源工具&#xff0c;相比于一些商业工具&#xff08;比如 LoadRunner&#xff09;&#xff0c;在功能的全面性上就稍显不足。这篇博客&#xff0c;…

Java学习42-Java 流(Stream)、文件(File)和IO - FileReader\FileWriter的使用

IO流 IO流的概述&#xff0c;分类等 Java程序中&#xff0c;对于数据的输入输出以stream方式进行&#xff0c;可以看作是一种数据的流动。 IO流中的IO是Input和Output的缩写&#xff0c;是非常实用的技术&#xff0c;用于处理设备之间的数据传输。读写文件&#xff0c;网络通…

JUC并发编程2(高并发,AQS)

JUC AQS核心 当有线程想获取锁时&#xff0c;其中一个线程使用CAS的将state变为1&#xff0c;将加锁线程设为自己。当其他线程来竞争锁时会&#xff0c;判断state是不是0&#xff0c;不是自己就把自己放入阻塞队列种&#xff08;这个阻塞队列是用双向链表实现&#xff09;&am…

聚丙烯PP材料粘接方法?泰达克TADHE专用于PP材料塑料粘接的UV胶水提供了解决方案

PP&#xff08;聚丙烯&#xff09;&#xff0c;简称PP。 PP是一种疏水性的塑料&#xff0c;需要特殊的处理后再和胶水粘接&#xff0c;以确保良好的粘接效果。常用的PP材料粘接方法&#xff1a; ​1.表面处理 因PP表面的疏水性&#xff0c;可以先进行表面处理。使用酒精或丙酮…

完整的项目源码!在线考试完整系统源码(可接私活)

最近有一些读者问我有没有完整的基于SpringbootVue的项目源码&#xff0c;今天给大家整理了一下&#xff0c;并且录制了搭建的教程&#xff0c;无偿分享给大家。 一、系统运行图 1、登陆页面 2、后台管理 3、全套环境资源 ​源码文件部分截图&#xff0c;带视频教程 ​ 在实际…

【图论】Dijkstra单源最短路径-朴素方法-简单模板(迪杰斯特拉算法)

Dijkstra单源最短路径 问题描述 输入n 表示n个结点&#xff0c;m表示m条边&#xff0c;求编号1的结点到每个点的最短路径 输出从第一个点到第n个点的最短路径 思路 将图g[][]中所有的权值初始化为0x3f表示正无穷 将dist[]中所有的值初始化为0x3f表示从第一个点到所有点的距离…

linux的io的知识大全

C语言的io操作 写文件 #include<stdio.h> #include<string.h>#define FILE_NAME "log.txt" int main() {FILE * fp fopen(FILE_NAME, "w");if(fpNULL){printf("fopen error!\n");}const char* msg "hello zk\n";int c…

如何准确测量电源噪声

目录 电源噪声的特点 影响噪声测试的因素 总结 电源噪声的特点 以往电源噪声的幅度规范一般在几十mV&#xff0c;但是随着芯片电源电压的降低&#xff0c;很多芯片的电源噪声的规范已经低至mV的量级&#xff0c;某些对电源噪声敏感的芯片要求甚至到了百uV的量级。 电源上的…

基于Android studio 实现外卖(点)订餐系统-编程乐学最新原创

&#x1f345;文章末尾有获取完整项目源码方式&#x1f345; 目录 一、实现介绍 视频演示 1.1 启动页 1.2登录页 1.3注册页 1.4商家主页 1.5商家发布商品页面 1.6商家我的页面 1.7商家个人信息修改页 1.8商家商品信息修改页 1.9用户首页 1.10用户我的订单页面 1.1…

C++学习进阶:二进制与位运算

目录 1.进制与原反补码 2.位运算 2.1.按位与 2.2.按位或 2.3.异或 2.4.取反 2.5.移位 3.部分面试题 3.1.不创建新的变量&#xff0c;实现两个变量的交换 3.2.求一个整数存储在内存中二进制中1的个数 这一部分本来是C语言的内容&#xff0c;当学习位图时&#xff0c…

期货分账户软件|程序化软件|风控软件|资产管理软件开发用到哪些技术?

期货/股票资管分仓软件分账户系统APP的开发涉及多个技术领域&#xff0c;以确保软件的功能性、安全性和易用性。以下是一些在开发过程中可能需要用到的关键技术&#xff1a; 前端开发技术&#xff1a;前端部分主要负责用户界面的设计和实现。通常使用HTML、CSS和JavaScript等技…

YARN-Client 与 YARN-Cluster 区别

YARN-Client 与 YARN-Cluster 区别 理解YARN-Client和YARN-Cluster深层次的区别之前先清楚一个概念&#xff1a;Application Master。在YARN中&#xff0c;每个Application实例都有一个ApplicationMaster进程&#xff0c;它是Application启动的第一个容器。它负责和ResourceMa…

【HTML】制作一个简单的实时字体时钟

目录 前言 HTML部分 CSS部分 JS部分 效果图 总结 前言 无需多言&#xff0c;本文将详细介绍一段HTML代码&#xff0c;具体内容如下&#xff1a; 开始 首先新建文件夹&#xff0c;创建一个文本文档&#xff0c;两个文件夹&#xff0c;其中HTML的文件名改为[index.html]&am…

Jmeter杂记:测试计划参数详解

测试计划各参数详解 1&#xff0c;用户自定义变量&#xff0c;是全局变量&#xff0c;供所有线程组使用&#xff0c;可用配置元件&#xff1a;用户自定义变量替代 2&#xff0c;连续的运行线程组&#xff0c;默认不勾选&#xff0c;则随机的运行多个线程组中的取样器&#xff…

在隐私计算应用中和数链具备哪些技术特点?

在加速“可信数字化”进程的背景下&#xff0c;我国区块链产业将在打造新型平台经济&#xff0c;开启共享经济新时代的同时&#xff0c;带动数字经济“脱虚向实”服务实体经济。 和数软件在加速数字化进程的同时&#xff0c;进一步服务实体经济&#xff0c;提高实体经济的活力…

VS2019 VS2022 LNK2019 无法解析的外部符号sprintf

解决方案&#xff1a; 项目属性》配置属性》链接接-》输入》附加依赖项&#xff0c;增加 legacy_stdio_definitions.lib legacy_stdio_definitions.lib 是一个库文件&#xff0c;通常与使用 Visual Studio 编译的 C/C 项目相关。它的作用是解决在使用新版本的 Visual Studio 编…

IIS中部署netcore程序出现500错误如何处理?

500错误在IIS部署中经常出现&#xff0c;但是解决非常耗时 官方也有给出一些指引&#xff0c;但是无法解决根本问题 建议检查netcore相关组件是否正确安装&#xff0c;如下&#xff1a; aspnetcore-runtime-3.1.32-win-x64 dotnet-hosting-3.1.32-win dotnet-runtime-3.1.…