项目场景:
最近公司做了一个小服务,说的是需求能够支持2万人使用,然后并发的话我们这边要做到1000。有些地方就使用了kafka。
问题描述
项目集成了kafka,上了生产环境之后。我们在测并发的时候,运维反馈只有一个服务节点在打印这个日志(kafka消费数据的日志)。 环境是这样的kafka是集群三个节点,应用部署了三个节点,按照理想情况三个服务节点都应该在打印消费消息的日志。
下面是我集成kafka的配置和代码,除了下面的代码和pom项目里面就没有其他的代码了
配置如下
spring.kafka.bootstrap-servers=192.168.XXXX
#=============== provider =========
spring.kafka.producer.key-serializer=org.apache.kafka.common.serialization.StringSerializer
spring.kafka.producer.value-serializer=org.apache.kafka.common.serialization.StringSerializer
spring.kafka.producer.acks=all
#=============== consumer ========
spring.kafka.consumer.group-id=test-01
spring.kafka.consumer.enable-auto-commit=true
spring.kafka.consumer.auto-commit-interval=100
spring.kafka.listener.concurrency=10
spring.kafka.consumer.key-deserializer=org.apache.kafka.common.serialization.StringDeserializer
spring.kafka.consumer.value-deserializer=org.apache.kafka.common.serialization.StringDeserializer
生产者代码
@Component
public class KafkaPrducer {
private static final Logger log = LoggerFactory.getLogger(KafkaPrducer.class);
public static final String TOPIC_TEST = "tj_test_1";
@Autowired
private KafkaTemplate<String,String> kafkaTemplate;
@Autowired
AdminClient adminClient;
public void send(Object obj) {
String obj2String = JSONObject.toJSONString(obj);
log.info("准备发送消息为:{}", obj2String);
//发送消息
String key = UUID.randomUUID().toString();
ListenableFuture<SendResult<String, String>> future = kafkaTemplate.send(TOPIC_TEST, obj2String);
future.addCallback(new ListenableFutureCallback<SendResult<String, String>>() {
@Override
public void onFailure(Throwable throwable) {
//发送失败的处理
log.info(TOPIC_TEST + " - 生产者 发送消息失败:" + throwable.getMessage());
}
@Override
public void onSuccess(SendResult<String, String> stringObjectSendResult) {
//成功的处理
log.info(TOPIC_TEST + " - 生产者 发送消息成功:" + stringObjectSendResult.toString());
}
});
DescribeTopicsResult describeTopicsResult = adminClient.describeTopics(Arrays.asList(TOPIC_TEST));
System.out.println(describeTopicsResult);
}
消费者代码
public class KafkaConsumer {
private static final Logger log = LoggerFactory.getLogger(KafkaConsumer.class);
@KafkaListener(topics = "tj_test_1")
public void consumerMsg(ConsumerRecord<String, String> record){
log.info("key:{}",record.key());
log.info("value:{}",record.value());
String value = record.value();
record.headers();
Person p = JSONObject.parseObject(value, Person.class);
record.partition();
log.info(p.toString());
}
}
各位铁汁看到这儿有没有看到啥问题捏!!!
原因分析:
然后我就按照以下几点依次做了分析(我也不熟悉kafka,但是也用过)
-
服务器有三个节点,按照我的惯性思维就有三个消费者,难道请求都打到一个生产上去了?
在生产者上加了日志,发现三个服务节点都在生产消息,排除掉该问题 -
确认生产消息的时候是否只写到一个分区了。
看了生产消息代码,发现没有传key,并且写入分区使用的也是默认分区,看一下写默认写入分区的策略:public int partition(String topic, Object key, byte[] keyBytes, Object value, byte[] valueBytes, Cluster cluster, int numPartitions) { return keyBytes == null ? this.stickyPartitionCache.partition(topic, cluster) : Utils.toPositive(Utils.murmur2(keyBytes)) % numPartitions; }
可以确认默认的分区策略不是一直写入到同一个分区(前提是有多个分区) -
是不是可利用的分区只有一个
加了打印当前可利用的分区数量,发现只有一个分区。。。。。。。。。 -
代码问题,配置问题
spring.kafka.bootstrap-servers=
spring.kafka.producer.key-serializer= org.apache.kafka.common.serialization.StringSerializer
spring.kafka.producer.value-serializer= org.apache.kafka.common.serialization.StringSerializer
spring.kafka.producer.properties.partitioner.class = org.apache.kafka.clients.producer.internals.DefaultPartitioner
spring.kafka.consumer.group-id= zhtj
spring.kafka.consumer.enable-auto-commit= true
spring.kafka.consumer.auto-commit-interval= 1000
spring.kafka.consumer.key-deserializer= org.apache.kafka.common.serialization.StringDeserializer
spring.kafka.consumer.value-deserializer= org.apache.kafka.common.serialization.StringDeserializer
#当前节点消费者数量
spring.kafka.listener.concurrency=10
也没发现配置里面有配置分区的,然后就百度如何配置分区数量,代码如下@Bean public NewTopic topicinfo() { // 创建topic,需要指定创建的topic的"名称"、"分区数"、"副本数量(副本数数目的值要小于等于Broker数量)" return new NewTopic(KafkaPrducer.TOPIC_TEST.toString(), 3, (short) 1); }
再次测试各服务器节点的消费者就正常消费数据了
解决方案:设置了主题的分区数量,就生效了
public NewTopic topicinfo() {
// 创建topic,需要指定创建的topic的"名称"、“分区数”、“副本数量(副本数数目的值要小于等于Broker数量)”
return new NewTopic(KafkaPrducer.TOPIC_TEST.toString(), 3, (short) 1);
}