新手入门之Kafka的并发实现

Kafka是一种高吞吐量的分布式发布订阅消息系统。那么Kafka的高并发究竟如何实现呢,对于一个新手来说是一脸的茫然,感觉一点都不好用,还不如activemq好用。

经过一番实践,终于搞清楚了kafka的并发,这里分享给大家,欢迎批评指正。

1、搭建Kafka环境。

下载windows版本或linux版本的kafka,我这里的版本是kafka_2.12-2.2.0。这里以windows环境下的为例:

下载后直接解压到磁盘,直接启动即可。注意启动时先启动zookeeper,再启动kafka。

命令具体如下:

$ bin\windows\zookeeper-server-start.bat config\zookeeper.properties
$ bin\windows\kafka-server-start.bat config\server.properties

2、创建主题

bin\windows\kafka-topics.bat --create --zookeeper localhost:2181 --partitions 1 --replication-factor 1 --topic demo

如果不在这里创建,也可以在项目中配置主题名称,主题将在项目启动时自动创建。但此时的分区数量和副本数量将是默认的。分区默认值为1,副本数量也为1.,此配置在kafka的配置文件中,具体如下:

创建成功后,可通过命令查看到已创建主题的信息。

3、创建微服务项目,在pom文件中引入如下的jar包:

<dependency>
			<groupId>org.apache.kafka</groupId>
			<artifactId>kafka-streams</artifactId>
			<version>2.3.1</version>
		</dependency>
		<dependency>
			<groupId>org.springframework.kafka</groupId>
			<artifactId>spring-kafka</artifactId>
		</dependency>

在该项目中创建一个测试类,代码如下:

package com.demo.zjjg.kafka.stream;

import java.util.Properties;

import org.apache.kafka.clients.producer.Callback;
import org.apache.kafka.clients.producer.KafkaProducer;
import org.apache.kafka.clients.producer.Producer;
import org.apache.kafka.clients.producer.ProducerRecord;
import org.apache.kafka.clients.producer.RecordMetadata;

public class HelloProducer {
	public static void main(String[] args) {
		Properties props = new Properties();
		props.put("bootstrap.servers", "localhost:9092");
		props.put("acks", "all");
		props.put("retries", 0);
		props.put("batch.size", 16384);
		props.put("linger.ms", 1);
		props.put("buffer.memory", 33554432);
		props.put("key.serializer", "org.apache.kafka.common.serialization.StringSerializer");
		props.put("value.serializer", "org.apache.kafka.common.serialization.StringSerializer");
		
		Producer<String,String> producer = new KafkaProducer<String,String>(props);
		for(int i = 0;i < 12; i++) {
			producer.send(new ProducerRecord<String,String>("demo",null,Integer.toString(1)),new Callback() {
				@Override
				public void onCompletion(RecordMetadata metadata, Exception exception) {
					if(null != exception) {
						exception.printStackTrace();
					}else {
						System.err.println("callback: " + metadata.topic() + " " + metadata.partition() + " " + metadata.offset());
					}
				}
			});
		}
		producer.close();
		
	}
	
}

运行该main方法,打印结果如下:

说明这些消息发送到了demo主题中,第0个分区,也就是唯一的一个分区。

4、新建一个消费者项目,本demo依旧使用springboot框架创建,yml文件配置如下:

spring: 
  kafka:
    listener: 
      concurrency: 1 
    consumer: 
      group-id: abc
      bootstrap-servers:
      - localhost:9092
      key-deserializer: org.apache.kafka.common.serialization.StringDeserializer
      value-deserializer: org.apache.kafka.common.serialization.StringDeserializer

消费者类源码如下:

package com.example.demo.kafka;

import org.apache.kafka.clients.consumer.ConsumerRecord;
import org.springframework.kafka.annotation.KafkaListener;
import org.springframework.stereotype.Component;
import lombok.extern.slf4j.Slf4j;
@Component 
/**
 * @author xingzhej
 *
 */
@Slf4j
public class MsgConsumer {
	public static final String KAFKA_LOG_QUENE_NAME="demo";
	@KafkaListener(topics = {MsgConsumer.KAFKA_LOG_QUENE_NAME})
	public void receive(ConsumerRecord consumerRecord){
		try {
			System.out.println("####################################begin");
			System.out.println("线程ID:"+Thread.currentThread().getId());
			System.out.println("partition"+consumerRecord.partition());
			System.out.println("####################################end");
		}catch(Exception ex) {
			ex.printStackTrace();
		}
    }
}

执行后,结果如下:

####################################begin
线程ID:21
partition0
####################################end
####################################begin
线程ID:21
partition0
####################################end
####################################begin
线程ID:21
partition0
####################################end
####################################begin
线程ID:21
partition0
####################################end
####################################begin
线程ID:21
partition0
####################################end
####################################begin
线程ID:21
partition0
####################################end
####################################begin
线程ID:21
partition0
####################################end
####################################begin
线程ID:21
partition0
####################################end
####################################begin
线程ID:21
partition0
####################################end
####################################begin
线程ID:21
partition0
####################################end
####################################begin
线程ID:21
partition0
####################################end
####################################begin
线程ID:21
partition0
####################################end

将消费者应用kafka消费者线程数改为3

spring: 
  kafka:
    listener: 
      concurrency: 3
    consumer: 
      group-id: abc
      bootstrap-servers:
      - localhost:9092
      key-deserializer: org.apache.kafka.common.serialization.StringDeserializer
      value-deserializer: org.apache.kafka.common.serialization.StringDeserializer

再次重新启动该醒目,执行结果如下:

####################################begin
线程ID:21
partition0
####################################end
####################################begin
线程ID:21
partition0
####################################end
####################################begin
线程ID:21
partition0
####################################end
####################################begin
线程ID:21
partition0
####################################end
####################################begin
线程ID:21
partition0
####################################end
####################################begin
线程ID:21
partition0
####################################end
####################################begin
线程ID:21
partition0
####################################end
####################################begin
线程ID:21
partition0
####################################end
####################################begin
线程ID:21
partition0
####################################end
####################################begin
线程ID:21
partition0
####################################end
####################################begin
线程ID:21
partition0
####################################end
####################################begin
线程ID:21
partition0
####################################end

说明多线程没起作用,为什么呢?

原来,kafka的多线程跟分区紧密相关。一个分区中的消息只能被一个进程中的一个线程消费。当然如果两个进程的groupid不一致,那么消息会全量广播给两个进程。

所以,为了使得多线程发挥作用,只能增加分区,经研究表明,分区数量和线程总量(所有进程的线程数量之和)一致是效率最高的。具体设置几个分区根据硬件配置和业务需要来确定。

好了,这里我们将分区数量修改为3.修改命令如下:

[E:\05.AppServer\kafka_2.12-2.2.0\bin\windows]$ kafka-topics.bat --alter --zookeeper localhost:2181 --topic demo --partitions 3 
WARNING: If partitions are increased for a topic that has a key, the partition logic or ordering of the messages will be affected
Adding partitions succeeded!

修改结果查看如下:

重新测试生产者数据:运行HelloProducer的main方法,运行结果如下:

说明数据已经分别发送到demo主题的3个分区中。

这时,消费者端运行结果如下:

####################################begin
####################################begin
线程ID:23
线程ID:21
partition0
partition1
####################################end
####################################end
####################################begin
线程ID:21
partition0
####################################end
####################################begin
####################################begin
线程ID:23
partition1
####################################end
线程ID:21
partition0
####################################end
####################################begin
线程ID:21
partition0
####################################end
####################################begin
线程ID:23
partition1
####################################end
####################################begin
线程ID:23
partition1
####################################end
####################################begin
线程ID:25
partition2
####################################end
####################################begin
线程ID:25
partition2
####################################end
####################################begin
线程ID:25
partition2
####################################end
####################################begin
线程ID:25
partition2
####################################end

三个线程分别获取到三个分区的数据进行处理。并发实现了。

当然你可以启动两个微服务,如果每个微服务消费者线程配置为3的话,总共是6个线程,相应的分片应该增加为6,否则有三个线程将不起作用。注意,要用两个进程实现并发效果,两个进程的groupid必须一致。否则将是广播效果。

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值