一、初衷
因为之前学习消息队列,朋友推荐了kafka(卡夫卡),然后就入坑了一段时间,总算对kafka有一个初步的认识。现记录一下坑,和简单的使用方式。这个版本是window的操作,比较简单。希望对初学者有一定的帮助。
二、安装kafka
windows版本,卡夫卡的版本是kafka_2.11-1.1.0;依赖zookeeper,版本使用的是zookeeper-3.4.10。直接到官网下载window的压缩包解压即可。解压后的目录结构如下:
然后zookeeper如下:
因为kafka依赖zookeeper,所以先配置好zookeeper,首先是配置zookeeper的日志保存路径,打开conf,修改如下:
更改dataDir的日志路径,其他默认即可 。启动Zookeeper,进入 bin 目录,执行 zkServer.cmd。可以注册为windows服务,这里就不说了。启动成功如下:
接下来是配置kafka,打开kafka目录下的config,里面有几个重要的配置文件,producer、consumer、server配置文件,server的配置文件启动的时候会去该文件。下图是zookeeper.propertis:
下图是生产者的配置文件说明:
kafka的server.properties配置文件改下几个重要的属性:
这里说明一下:topic创建的时候指定的分区数量应该根据每个group的消费者的数量而定制,一般是partition>=goup下的消费者数量才合理。
zk的端口如果都是默认的话,,一般不需要更改,,默认就好。
接下来启动卡夫卡服务,,进入bin目录下的windows目录,,因为是windows版本,,所以需要依赖bat文件执行相应的kafka命令,启动命令:kafka-server-start.bat ..\..\config\server.properties 启动成功后,,接下来创建一个topic订阅主题和topic对应的分区数量,命令:
kafka-topics.bat --create --zookeeper localhost:2181 --replication-factor 1 --partitions 1 --topic test.topic
如果后期需要修改topic的分区数量可执行该命令:kafka-topics.bat –zookeeper localhost:2181 -alter –partitions 5 –topic test.topic
创建成功后,Java这边代码,主要是以API来实现。
三、Java集成简单的kafka-client
对应的版本为 <kafka-clients.version>1.1.0</kafka-clients.version>
编写消费者的代码如下:
package com.lmx.springbootdemo.common.entity;
import org.apache.kafka.clients.consumer.ConsumerRecord;
import org.apache.kafka.clients.consumer.ConsumerRecords;
import org.apache.kafka.clients.consumer.KafkaConsumer;
import java.util.Arrays;
import java.util.Properties;
public class MyKafkaConsumer implements Runnable{
private final KafkaConsumer<String, String> consumer;
private String topic;
private String groupId;
public MyKafkaConsumer(String topic, String groupId){
this.topic=topic;
this.groupId=groupId;
Properties props = new Properties();
/* 定义kakfa 服务的地址,不需要将所有broker指定上 */
props.put("bootstrap.servers", "localhost:9092");
/* 制定consumer group */
props.put("group.id", this.groupId);
/* 是否自动确认offset */
props.put("enable.auto.commit", "true");
/* 自动确认offset的时间间隔 */
props.put("auto.commit.interval.ms", "1000");
props.put("session.timeout.ms", "30000");
/* key的序列化类 */
props.put("key.deserializer", "org.apache.kafka.common.serialization.StringDeserializer");
/* value的序列化类 */
props.put("value.deserializer", "org.apache.kafka.common.serialization.StringDeserializer");
/* 定义consumer */
this.consumer=new KafkaConsumer<>(props);
/* 消费者订阅的topic, 可同时订阅多个 */
this.consumer.subscribe(Arrays.asList(this.topic));
}
@Override
public void run() {
try {
while (true){
System.out.println("消费者test-consumer-group"+this+"接收消息中>>>>>>>>>>>>>>>>>>>>>>>>>");
ConsumerRecords<String, String> records = this.consumer.poll(1000);
for (ConsumerRecord<String, String> record : records) {
System.out.printf("消费者test-consumer-group"+this+">>>>>>>> "+"offset = %d, key = %s, value = %s\n", record.offset(), record.key(), record.value());
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
kafka生产者的与发送消息的工具类,因为我这只想实例化一个生产者,,所以单例了。这里注释掉了Partitioner接口,这个接口目的是自定义topic分区消息分发策略,如果只有一个topic切一个分区,多个相同的group.id订阅相同的topic,,那么成了广播模式,,所有消费者会接收到相同的消息;如果topic下多个分区,消费者group下有多个消费者,,那么kafka将采用随机或者取模的方式把消息分配到topic的分区,group的消费者就会各自分配到一个对应的分区消费各自不同的消息,每个分区都存在一个log。
package com.lmx.springbootdemo.common.utils;
import com.baomidou.mybatisplus.toolkit.IdWorker;
import org.apache.kafka.clients.producer.KafkaProducer;
import org.apache.kafka.clients.producer.ProducerRecord;
import java.util.Properties;
/**
* @auther Lin_J
* @className: (卡夫卡工具类)
* @date 2018/04/11 20:07:47
*/
public class KafkaUtil /*implements Partitioner*/{
private static KafkaProducer<String, String> producer;
// private static KafkaConsumer<String, String> consumer = null;
public final static String TOPIC="xinggege";
private static Properties producerProps = null;
private static Properties initProducerProPer(){
if(null==producerProps){
producerProps = new Properties();
producerProps.put("bootstrap.servers", "localhost:9092");
producerProps.put("client.id", "DemoProducer");
producerProps.put("acks", "all");
// producerProps.put("partitioner.class", "com.lmx.springbootdemo.common.utils.KafkaUtil");
producerProps.put("retries", 0);
producerProps.put("batch.size", 16384);
producerProps.put("linger.ms", 10);
producerProps.put("request.timeout.ms", "6000");
producerProps.put("key.serializer", "org.apache.kafka.common.serialization.StringSerializer");
producerProps.put("value.serializer", "org.apache.kafka.common.serialization.StringSerializer");
}
return producerProps;
}
/**
* 单例一个生产者
* @return
*/
private static KafkaProducer<String, String> initKafkaProducer(){
if(null==producer){
producer=new KafkaProducer<>(initProducerProPer());
producer.partitionsFor(TOPIC);
}
return producer;
}
/**
* 调用生产者发送消息
* @param msg
*/
public static void prodMessage(String msg){
initKafkaProducer();
if(null != producer){
System.out.println("发送的数据>>>>>>>>>>>>>>>>>"+msg);
producer.send(new ProducerRecord<>(TOPIC, IdWorker.getIdStr(), msg));
}
}
public static void closeProducer(){
if(null!=producer){
// producer.flush();
producer.close();
}
}
/* @Override
public int partition(String topic, Object key, byte[] bytes, Object value, byte[] bytes1, Cluster cluster) {
System.err.println(">>>>>>>>>>>>>>>"+topic);
System.err.println(">>>>>>>>>>>>>>>"+key);
System.err.println(">>>>>>>>>>>>>>>"+value);
List<PartitionInfo> partitions = cluster.partitionsForTopic(topic);
int num = partitions.size();
int partNum;
try {
partNum = key.toString().hashCode();
} catch (Exception e) {
partNum = IdWorker.getIdStr().hashCode();
}
System.err.println(partNum);
System.err.println(num);
System.err.println(Math.abs(partNum % num));
return Math.abs(partNum % num);
}
@Override
public void close() {
}
@Override
public void configure(Map<String, ?> map) {
}*/
}
我这里使用了线程池跑了两个线程,创建了两个group.id相同的消费者。测试数据可以清楚的看到,,每个消费者都有分配到自己的分区,然后消费各自的消息。
什么是consumer group? consumer group是kafka提供的可扩展且具有容错性的消费者机制。既然是一个组,那么组内必然可以有多个消费者或消费者实例(consumer instance),它们共享一个公共的ID,即group ID。组内的所有消费者协调在一起来消费订阅主题(subscribed topics)的所有分区(partition)。当然,每个分区只能由同一个消费组内的一个consumer来消费。三个特性:
-
consumer group下可以有一个或多个consumer 实例,可以是一个进程,也可以是一个线程
-
group.id是一个字符串,唯一标识一个consumer group
-
consumer group下订阅的topic下的每个分区只能分配给某个group下的一个consumer(当然该分区还可以被分配给其他group)
四、总结
kafka不单单是那么简单,,其中涉及到的概念还有很多。比如:消息传播模式、消息提交方式(为了消息可靠,会有手动提交、异步提交、自动提交等等)、zero-copy、offset偏移、存储机制等等。更详细的原理请参考:https://blog.csdn.net/ychenfeng/article/details/74980531。以上是个人使用心得,有什么不对的地方,,请多多指教!