前言
该文档不会为读者提供中间件的安装和部署教学,仅作为我在这大半年从零开始建立日志收集系统的整个过程。整个日志收集系统目前已迭代三次,从第二版开始已经稳定运行半年,第三版的升级是锦上添花,继续深挖日志收集的可能性。
读者能从本文看到一条从零开始的日志收集系统的建立通路,能看到我选择和处理各个中间件的思考和碎碎念。该方案是基于EFK的一个通用框架,由于数据处理服务的存在,使得该方案能兼容绝大部分奇奇怪怪的日志,只要能收集过来,那处理就是多几行代码的问题。我个人推荐有条件的读者可以尝试自己去搭建这样一套完整的日志收集系统,整个做下来会让你对中间件和日志收集的思考更加深刻。
日志收集第一版
设计方案
基于注解切面+Kafka+数据处理服务+ES
通过手动填写注解的字段信息来填充需要的日志信息,在切面异步通过Kafka传递消息,然后数据处理服务消费消息,经过处理后批量插入到ES。后来觉得这样收集太冗余,很少有同事愿意配置些东西或者写个注解。于是排除了公司的默认日志切面,自己写了切面来传递有默认值的数据
思考
当时写这第一版方案是因为公司原本有一套老的ELK来收集日志,以Log4j2+Kafka+ELK方式实现。我当时这个基于注解和切面的想法原本只是想做个补充,类似于行为收集的入口。后来发现给同事用的时候,注解参数基本都是用默认,也不会写,毕竟每个接口上来个注解,写一堆参数,换我我也不太想写。但是这个思路是可以的,作为补充是合适的
不足
1.注解参数配置太多,不想配
2.耦合性高,注解一旦变动涉及修改太多
3.Log4j2的Kafka相关配置全部默认,没有做高并发配置
4.ES索引没有优化,单索引过大问题等等
代码实现
切面如下
@Aspect
@Component
@EnableConfigurationProperties(value = {TransferProperties.class})
@Slf4j
public class LogTransferHandler {
public static final String UAT_DEFAULT_TOPIC = "ptm-uat-request";
public static final String PRO_DEFAULT_TOPIC = "ptm-pro-request";
@Autowired
//从配置文件种配置的一些默认数据
private TransferProperties transferProperties;
@Autowired
private KafkaTemplate<String, String> kafkaTemplate;
/**
* 带有注解@LogCollector和controller层组成切点
*/
@Pointcut("@annotation(com.ruijie.transfer.annotation.LogCollector)||execution(* *.ruijie..*.controller..*.*(..))")
public void pointCut() {
}
@Resource
private BaseEnvironmentConfigration baseEnvironmentConfigration;
@Around("pointCut()")
public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
//proceed方法用于启动目标方法执行,并能获取返回值信息
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
assert attributes != null;
HttpServletRequest request = attributes.getRequest();
Instant start = Instant.now();
Object result = joinPoint.proceed();
Instant end = Instant.now();
//从threadlocal中获取用户信息
String userId = RequestContext.getCurrentContext().getUserId();
String userName = RequestContext.getCurrentContext().getUserName();
String methodName = methodSignature.getName();
//log4j2日志信息录入
if (StringUtils.isEmpty(MDC.get("user_id")) && !StringUtils.isEmpty(userId) && !"null".equalsIgnoreCase(userId)) {
MDC.put("user_id", userId);
}
//...省略代码--业务代码
//通过log4j2直接传递日志,该处通过AOP收集稍显鸡肋,不过暂时保留
if (isEsCollect) {
CompletableFuture.runAsync(() -> {
//...省略代码--组装日志参数
String msg = JSON.toJSONString(provider);
String topic;
if ("pro".equals(baseEnvironmentConfigration.getCurrentEnv())) {
topic = PRO_DEFAULT_TOPIC;
} else {
topic = UAT_DEFAULT_TOPIC;
}
ListenableFuture<SendResult<String, String>> sendListener = kafkaTemplate.send(topic, msg);
sendListener.addCallback(success -> {
log.info("日志传递成功,方法={},操作人={}", provider.getRequestUrl(), provider.getUserId());
}, err -> {
log.error("日志传递失败,方法={},操作人={}", provider.getRequestUrl(), provider.getUserId());
});
}).handle((res, e) -> {
if (e != null) {
log.error("日志传递出现异常", e);
}
return res;
});
}
return result;
}
//...省略代码--入参处理方法
复制代码
日志收集第二版
设计方案
Log4j2+Kafka+数据处理服务+Elasticsearch+Kibana
在保留第一版行为日志收集方案雏形的基础上进行了默认日志上的改造,首先是干掉了公司的日志收集模块,因为公司是以组件方式提供,所以排除也很简单,依赖去掉就行了。重写Log4j2的配置文件,根据环境进行了日志的隔离,优化了Kafka的配置 ,ES部分索引进行了优化,该方案已支持千万级的日志收集,时延保持在5s内,运行半年暂未发现日志丢失情况,最重要的是以组件方式加载,无侵入。
思考
公司提供的日志收集,实在是不好用,全公司的项目都混在一块,不分开发测试正式环境,索引也没有优化,字段类型全是text,也没分词,总的来说就是不如linux命令舒服。在我推动我们组领导去搞一套日志收集系统之前,所有同事都不用公司提供的elk查询日志,都是linux命令查询,这不废话嘛,没分词怎么查日志,索引的字段设置也有点奇怪,有些冗余字段也不知道干啥的。跟架构组反馈了很多次,一直说要改,方案变动了两版,就是一直没东西出来。半年过去我自己的日志收集系统都迭代了三版了,第二版也正常运行了小半年了,果然指望别人不如自己。
做全套的日志收集嘛,第一时间也是迷茫的,怎么做,之前就听说了一个词叫ELK,关键是怎么做这个ELK才能变成日志啊。然后就上网搜一下,大致分为两种,第一种就是传统的ELK或者EFK,第二种是相对于ELK轻量的日志框架,Loki+Promtail+Grafana。因为我有现成的ElasticSearch,并且做第一版方案的时候已经学习了相关知识,所以我还是倾向于第一种,毕竟公司给的服务器配置不差,三节点的Kafka集群也是现成的。
做的时候,由于第一版方案用Kafka+数据处理服务+ES是一条通路,所以为了尽快做出成品给领导看看效果,我还是准备在这条路上拓宽加深。时间上确实存在问题,因为我个人是组里的开发主力,新项目总是跑不了,还有几个老项目要运维,同时我还有别的组件(比如前文提到的工具组件,业务组件,单点登录组件等)需要维护,同事遇到问题也要问我,所以我都是尽量在非工作时间来做这个。
由于时间的问题,首先干掉了Logstash,原因一是我不想花时间研究Logstash的安装与配置,原因二是ElasticSearch的动态索引以及批量插入等在第一版方案中已经积累了代码了,所以我直接复用了,并且我对Kafka和ElasticSearch做了配置上的优化,从时效上来说秒级的对于日志来说肯定是没什么问题的。日志收集的第一步收集肯定不能用切面了,局限性太大,我翻了下Log4j2的官方文档,发现直接就支持推送Kafka,于是乎,收集这一步也定了,最后展示这一步,之前第一版是通过接口查询的,后来发现Kibana实在是太香了,建个索引模板就好了,展示也挺好看的。所以整体方案就下来了,Log4j2+Kafka+数据处理服务+Elasticsearch+Kibana。
代码实现
Log4j2优化
Log4j2的优化点主要是减少无用日志的输出,少传点少占地方。原本是想用markfilter匹配字符串的模式排除掉环境变量为开发环境的数据,结果自己上手试了下,发现不好使,用了正则匹配啥的也不好使,整了两三天,官方文档看遍了,网上例子找遍了最后还是排不了。索性采取终极方案,log4j2.xml这个配置文件我也分环境,搞了两,一个log4j2-dev.xml一个log4j2.xml,通过配置文件application-dev.properties开发环境中配置logging.config=classpath:log4j2-dev.xml来避免输出开发环境的日志。
<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="FATAL" monitorInterval="300">
<properties>
取配置文件中的参数
<property name="APPLICATION">${bundle:application:spring.application.name}</property>
Kafka配置
<property name="KAFKA_SERVERS">xxx,xxx,xxx</property>
取系统环境变量env,这里注意容器环境配置
<property name="KAFKA_TOPIC">log-zero-${sys:env}</property>
日志文件存放地点
<property name="LOG_HOME">/data/logs</property>
<property name="LOG_FILE_NAME">${APPLICATION}</property>
<property name="CHARSET">UTF-8</property>
控制台打印简略信息,日志文件和推送Kafka消息采用全字段,%X{xxx}是通过切面或者拦截器使用MDC注入的
<property name="CONSOLE_PATTERN">%d{yyyy-MM-dd HH:mm:ss.SSS}{GMT+8}|%p|${APPLICATION}|${sys:env}|%X{request_ip}|%traceId|%X{request_url}|%c%X{real_method}|%m%n</property>
<property name="LOG_PATTERN">%d{yyyy-MM-dd HH:mm:ss.SSS}{GMT+0}|%p|${APPLICATION}|${sys:env}|${sys:local-ip}|%X{request_ip }|%traceId|%X{request_url}|%X{request_method}|%c%X{real_method}|%X{user_id}|%X{user_name}|%t|%m%n</property>
<property name="KAFKA_PATTERN">%d{yyyy-MM-dd HH:mm:ss.SSS}{GMT+8}|%p|%m%n</property>
</properties>
<Appenders>
<Console name="console" target="SYSTEM_OUT">
<PatternLayout pattern="${CONSOLE_PATTERN}" charset="${CHARSET}"/>
</Console>
滚动错误日志,额外记录一份专门的错误日志
<RollingRandomAccessFile name="error" fileName="${LOG_HOME}/${APPLICATION}-error.log"
filePattern="${LOG_HOME}/${LOG_FILE_NAME}.%d{yyyy-MM-dd}-%i-error.log.bak"
immediateFlush="false" append="true">
<PatternLayout pattern="${LOG_PATTERN}" charset="${CHARSET}"/>
<Filters>
<ThresholdFilter level="fatal" onMatch="DENY" onMismatch="NEUTRAL"/>
<ThresholdFilter level="error" onMatch="ACCEPT" onMismatch="DENY"/>
</Filters>
<Policies>
<TimeBasedTriggeringPolicy modulate="true" interval="1"/>
<SizeBasedTriggeringPolicy size="100 MB"/>
</Policies>
<DefaultRolloverStrategy max="10"/>
</RollingRandomAccessFile>
<RollingRandomAccessFile name="file" fileName="${LOG_HOME}/${LOG_FILE_NAME}.log"
filePattern="${LOG_HOME}/${LOG_FILE_NAME}.%d{yyyy-MM-dd-HH}-%i.log.bak"
immediateFlush="false" append="true">
<PatternLayout pattern="${LOG_PATTERN}" charset="${CHARSET}"/>
<Policies>
<TimeBasedTriggeringPolicy modulate="true" interval="24"/>
<SizeBasedTriggeringPolicy size="100 MB"/>
</Policies>
<DefaultRolloverStrategy max="10"/>
</RollingRandomAccessFile>
Kafka推送日志,这里是高并发情况的配置,最大消息要配置,不然堆栈信息传不了
<Kafka name="ZeroKafka" topic="${KAFKA_TOPIC}" syncSend="true" ignoreExceptions="false">
<PatternLayout pattern="${LOG_PATTERN}" charset="${CHARSET}"/>
<Property name="bootstrap.servers">${KAFKA_SERVERS}</Property>
<Property name="max.block.ms">2000</Property>
<Property name="batch.size">163840</Property>
<Property name="linger.ms">20</Property>
<Property name="buffer.memory">67108864</Property>
<Property name="max.request.size">10485760</Property>
<Property name="request.timeout.ms">60000</Property>
<Property name="compression.type">lz4</Property>
</Kafka>
Kafka的错误日志也打印一下
<Failover name="failover" primary="ZeroKafka" retryIntervalSeconds="600">
<Failovers>
<AppenderRef ref="failoverKafkaLog"/>
</Failovers>
</Failover>
<RollingRandomAccessFile name="failoverKafkaLog" fileName="${LOG_HOME}/${APPLICATION}-kafka.log"
filePattern="${LOG_HOME}/${APPLICATION}-kafka-%d{yyyy-MM-dd-HH}-%i.log.bak"
immediateFlush="false" append="true">
<PatternLayout pattern="${KAFKA_PATTERN}" charset="${CHARSET}"/>
<Policies>
<TimeBasedTriggeringPolicy modulate="true" interval="24"/>
<SizeBasedTriggeringPolicy size="100 MB"/>
</Policies>
<DefaultRolloverStrategy max="10"/>
</RollingRandomAccessFile>
skywalking推送日志信息和链路追踪配置,需要在jvm参数配置skywalking探针
<GRPCLogClientAppender name="grpc-log">
<PatternLayout pattern="${LOG_PATTERN}" charset="${CHARSET}"/>
</GRPCLogClientAppender>
</Appenders>
<Loggers>
<asyncRoot level="info" includeLocation="false">
<AppenderRef ref="file"/>
<AppenderRef ref="error"/>
<AppenderRef ref="console"/>
<AppenderRef ref="failover"/>
<AppenderRef ref="grpc-log"/>
</asyncRoot>
<logger name="org.springframework" level="info"/>
<logger name="org.mybatis.spring.SqlSessionUtils" level="info"/>
<logger name="org.apache.kafka" level="info"/>
</Loggers>
</Configuration>
复制代码
Kafka优化
Kafka这部分的优化存在于两个地方,一个是Log4j2的生产侧,一个是数据处理服务的消费侧。
生产者的配置位于Log4j2.xml配置文件中,这个看log4j2官网,可以看哪些参数可以配置。
数据处理服务消费者的配置,我用的是spring boot环境进行开发,使用的也是spring提供的kafkaTemplat。但是我没有直接在配置文件配置Kafka相关的参数,因为那就定死了,我是在代码里面自定义了几种类型的消费者,比如高性能,高可用,低时延等,我在另一个组件里面已经封装了,所以这里直接用就可以了。
这里选择的是高吞吐量的配置类型。因为日志的话不考虑低时延和丢失问题只需要保持高吞吐量,所以首先批量大小增加,时延也增加,减少发送次数,批量大小上去了对应缓冲区大小也要增加,消息大小考虑到部分堆栈信息会超过1MB,也调大一点,acks默认1也可以,不怕丢失改为0也行,最后加上压缩lz4,这是生产者配置。消费者的配置是,单次拉取数据调大,默认自动提交偏移量,批量接收信息,选择并发消费。
topic提前要建好,分区数等于broker数就行,副本1或3,其他不需要特别配置,总的来说日志的场景对于Kafka来说是个经典的高吞吐量场景。
Kafka主题配置是动态的,这里插入spEl表达式取系统环境变量,消费者选择高性能类型
Kafka主题配置是动态的,这里插入spEl表达式取系统环境变量,消费者选择高性能类型
@KafkaListener(topics = {"log-zero-#{systemProperties['env']}"}, containerFactory = "performanceFactory")
public void kafkaLogListen(List<ConsumerRecord<String, String>> records) {
List<LogMsgDTO> logMsgList = new ArrayList<>(1024);
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyyMM");
records.forEach(record -> {
String value = record.value();
String[] split = value.split("\\|");
String system = split[2];
按照月份动态的创建索引,这里根据日志量情况自行选择
String topic = record.topic() + "-" + formatter.format(LocalDate.now());
boolean existIndex = esService.existIndex(topic + "-alias1");
if (!existIndex) {
esService.createIndex(topic + "-alias1", topic, false);
}
按照约定好的日志格式进行数据处理
if (split.length > 13) {
String env = split[3];
LogInfoDTO info = new LogInfoDTO();
info.setCreateTime(split[0]);
//...省略代码--填充ES文档数据
logMsgList.add(new LogMsgDTO(topic + "-alias1", JSON.toJSONString(info)));
}
});
if (!CollectionUtils.isEmpty(logMsgList)) {
esService.bulkAddRequest(logMsgList);
}
}
复制代码
ElasticSearch优化
优化的点主要是,索引分片数跟es集群节点数一致,副本给1,考虑到日志量比较大,我是按月来建索引,一个月目前接入的几个关键系统产生的日志量大概是几十G,做了一个定时任务删除2个月以上的日志,防止硬盘爆满。插入日志的时候,选择批量插入,设置超时时间长点,防止数据没传完。在索引的字段属性选择上,尽量选择keyword,能够更好的利用倒排索引进行查询优化,最关键的是分词器不能默认,用ik中文分词器,默认的太难用。
/**
* 是否存在索引
* @param index 索引
*/
public boolean existIndex(String index) {
List<String> allZeroIndex = findAllZeroIndex();
if (!CollectionUtils.isEmpty(allZeroIndex)) {
return allZeroIndex.contains(index);
} else {
GetIndexRequest request = new GetIndexRequest(index);
try {
return zeroClient.indices().exists(request, RequestOptions.DEFAULT);
} catch (IOException e) {
log.error("ES查询是否存在索引{}失败", index, e);
return false;
}
}
}
根据环境做不同的索引缓存,这里主要是为了做动态索引
private List<String> findAllZeroIndex() {
RBucket<String> allTopicCache = redissonClient.getBucket("allTopic" + envConfig.getCurrentEnv());
String cacheStr = allTopicCache.get();
if (StringUtils.isNotBlank(cacheStr)) {
String[] split = cacheStr.split(",");
return Arrays.asList(split);
}
List<String> indexList = new ArrayList<>();
try {
GetIndexRequest request = new GetIndexRequest("*");
GetIndexResponse getIndexResponse = zeroClient.indices().get(request, RequestOptions.DEFAULT);
String[] indices = getIndexResponse.getIndices();
indexList = Arrays.asList(indices);
allTopicCache.set(String.join(",", indexList), 24, TimeUnit.HOURS);
} catch (IOException e) {
log.error("查询所有索引数据", e);
}
return indexList;
}
/**
* 批量新增日志
*/
public void bulkAddRequest(List<LogMsgDTO> msgList) {
BulkRequest request = new BulkRequest();
//等待批量请求作为执行的超时设置为2分钟
request.timeout(TimeValue.timeValueMinutes(2));
msgList.forEach(msg -> {
request.add(new IndexRequest(msg.getTopic()).source(msg.getMsg(), XContentType.JSON));
});
zeroClient.bulkAsync(request, RequestOptions.DEFAULT, new ActionListener<BulkResponse>() {
@Override
public void onResponse(BulkResponse bulkResponse) {
for (BulkItemResponse bulkItemResponse : bulkResponse) {
boolean failed = bulkItemResponse.isFailed();
if (failed) {
log.error("批量插入消息失败,详细信息={}", bulkItemResponse.getFailureMessage());
}
DocWriteResponse itemResponse = bulkItemResponse.getResponse();
switch (bulkItemResponse.getOpType()) {
case INDEX:
case CREATE:
IndexResponse indexResponse = (IndexResponse) itemResponse;
handleAddDocSuccess(indexResponse);
break;
case UPDATE:
UpdateResponse updateResponse = (UpdateResponse) itemResponse;
break;
case DELETE:
DeleteResponse deleteResponse = (DeleteResponse) itemResponse;
break;
default:
break;
}
}
}
@Override
public void onFailure(Exception e) {
log.error("批量插入失败", e);
}
});
}
创建索引JSON
{
"properties": {
"userId": {
"type": "text"
},
"userName": {
"type": "text",
"analyzer": "ik_smart",
"search_analyzer": "ik_smart"
},
"requestUrl": {
"type": "text"
},
"requestType": {
"type": "keyword"
},
"logType": {
"type": "keyword"
},
"requestMethod": {
"type": "text"
},
"env": {
"type": "keyword"
},
"localIp": {
"type": "keyword"
},
"requestIp": {
"type": "keyword"
},
"triceId": {
"type": "keyword"
},
"createTime": {
"type": "date",
"format": "yyyy-MM-dd HH:mm:ss.SSS"
},
"system": {
"type": "keyword"
},
"threadName": {
"type": "text"
},
"msg": {
"type": "text",
"analyzer": "ik_smart",
"search_analyzer": "ik_smart"
}
}
}
复制代码
注意事项
- 切记kafka的topic要提前创建好,不然打不出日志来
- 数据处理服务的日志不抓,避免套娃
- 注意ES转换日期时默认是0时区
日志收集第三版
设计方案
Filebeat+Kafka+数据处理服务+Elasticsearch+Kibana+Skywalking
依旧是基于第二版,收集日志的话采用Filebeat,相比Log42直接推送可以收集更多类型的日志,比如容器,中间件的日志,不局限于spring boot项目。然后引入了Skywalking做一个链路追踪的补充,其他部分保持不变
思考
第二版已经很好用了,为啥还要做第三版呢?折腾嗷,不停地折腾。其实个人而言,服务日志相对来说第二版已经完全没问题了,但是对于团队来说,单一的服务收集还是不够的,尽管我为某些项目定制了行为日志收集,场景依然是偏少的。于是引入了Filebeat,beat系列组件的引入主要是考虑到我自己部署的一些中间件日志想要进行管理,比如Seata,Mysql,Redis,Nacos之类的。对于这种中间件我都是上Linux敲命令查日志,没有集成起来,毕竟总不能说我把源码改了,把自己这套Log4j2的给嵌进去吧,那不合适还挺傻,所以Filebeat,给爷上。
Skywalking的接入也是链路追踪的实现方案之一吧,之所以选择这个也是因为有别的同事有弄过,现成的直接能用,对接上Log4j2就带上链路ID了,查日志也很方便
这几天和架构组的同事聊了聊,他们现在是基于K8s环境来做的日志收集,还是ELK的体系,实现和我的方案类似,用的收集工具不太一样。他们需要解决的问题更多,多地数据聚合,老项目的日志格式需要兼容,多种类型语言服务的日志处理等等。他们现在的方案已初具雏形,解决了部分问题,也能启动了。后续更新的话,我会朝该方向更新,兼容更多的场景,解决更多的问题。
代码实现
Skywalking配置
<dependency>
<groupId>org.apache.skywalking</groupId>
<artifactId>apm-toolkit-log4j-2.x</artifactId>
<version>8.7.0</version>
</dependency>
探针配置,以下是jvm参数 ${skywalkingIp}是容器配置环境用的自定义参数
-javaagent:/usr/local/app/skywalking_agent_zy/skywalking-agent.jar -Dskywalking.agent.service_name=${appName} -Dskywalking.collector.backend_service=${skywalkingIp}:${skywalkingPort} -Dskywalking.plugin.toolkit.log.grpc.reporter.server_host=${skywalkingIp}
复制代码
Filebeat配置
filebeat.inputs:
- type: log
enabled: true
配置log4j2中日志存放的地址
paths:
- /data/myLog/*.log
正则表达式匹配行,这里排除开发环境日志
exclude_lines: [ '.*\|dev\|.*' ]
滚动备份日志和额外的错误日志不重复收集
exclude_files: [ '.bak$','-error.log$' ]
#禁止递归文件夹读取文件
recursive_glob:
enabled: false
#单个日志消息可以拥有的最大字节数,之后的数据被抛弃,默认值为 10MB (10485760),这里设置为200MB
max_bytes: 209715200
#Filebeat 将忽略在指定时间跨度之前修改的所有文件.您可以使用时间字符串,例如 2h(2 小时)和 5m(5 分钟)。默认值为 0,即禁用该设置。
#这里设置为2h,即排除掉容器修改时间为2h之前以.log为结尾的日志,防止传输无用历史数据,但是今天的日志文件还是包含在内
ignore_older: 2h
#如果一个文件在某个时间段内没有发生过更新,则关闭监控的文件handle。默认5m。
close_inactive: 5m
#如果此选项设置为true,Filebeat 会在每个文件的末尾而不是开头开始读取新文件。当此选项与日志轮换结合使用时,可能会跳过新文件中的第一个日志条目。默认设置为false。
tail_files: false
# ============================== Filebeat modules ==============================
filebeat.config.modules:
path: ${path.config}/modules.d/*.yml
reload.enabled: false
# ================================== Processors ===================================
processors:
排除filebeat自带的字段
- drop_fields:
fields: [ "host", "agent", "log", "input", "ecs" ]
# ---------------------------- Kafka Output ----------------------------
output.kafka:
hosts: [ xxx ]
根据环境选择不同的topic,需提前创建好topic
topics:
- topic: "filebeat-pro"
这里是包含,不是正则,很好针不戳
when.contains:
message: "|pro|"
- topic: "filebeat-uat"
when.contains:
message: "|uat|"
#Kafka 输出代理事件分区策略。必须是random、 round_robin或之一hash。默认情况下使用hash分区程序。
#reachable_only: true 设置为true时事件将仅发布到Kafka可用分区
partition.round_robin:
reachable_only: true
#单个 Kafka 请求中要批量处理的最大事件数。默认值为 2048。
bulk_max_size: 2048
# 发送批量 Kafka 请求之前的等待时间。0 是没有延迟。默认值为 0。
bulk_flush_frequency: 0
# 在超时之前等待来自 Kafka 代理的响应的秒数。默认值为 30(秒)。
timeout: 30
# 代理将等待所需 ACK 数的最长时间。默认值为 10 秒。
broker_timeout: 10
# 每个 Kafka 代理在输出管道中缓冲的消息数。默认值为 256。
channel_buffer_size: 256
# 活动网络连接的保持活动期。如果为 0,则禁用保活。默认值为 0 秒。
keep_alive: 0
# 设置输出压缩编解码器。必须是none,snappy和lz4之一gzip。默认值为gzip.
compression: gzip
# 设置 gzip 使用的压缩级别。将此值设置为 0 将禁用压缩。压缩级别必须在 1(最佳速度)到 9(最佳压缩)的范围内。
# 增加压缩级别会降低网络使用率,但会增加 CPU 使用率。 默认值为 4。
compression_level: 4
# JSON 编码消息的最大允许大小。更大的消息将被丢弃。默认值为 1000000(字节)。此值应等于或小于经纪人的message.max.bytes.
max_message_bytes: 10485760
# 代理要求的 ACK 可靠性级别。0=无响应,1=等待本地提交,-1=等待所有副本提交。默认值为 1。
# 注意:如果设置为 0,Kafka 不会返回任何 ACK。消息可能会因错误而静默丢失。
required_acks: 1
复制代码
写在最后
完整代码没发也没传GIT,因为有些公司的东西,我也懒得去挨个删了,于是就发了一些关键部分的代码。如果有感兴趣或者想要和我讨论的,可以站内信或者评论一下。
更希望大家能够帮我看看我这套方案还有没有什么地方可以做的更好,求指教。
该文会持续更新,原文是在我的有道云笔记里,有修改的话我会同步过来。距离上一篇已经过了一个月了,淦,最近是真的忙。