一、安装JAVA JDK
1、下载安装包
http://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.html
注意:根据32/64位操作系统下载对应的安装包
2、添加系统变量:JAVA_HOME=C:\Program Files (x86)\Java\jdk1.8.0_144
二、安装ZooKeeper
1、 下载安装包
http://zookeeper.apache.org/releases.html#download
2、 解压并进入ZooKeeper目录,进入目录中的conf文件夹
3、 将“zoo_sample.cfg”重命名为“zoo.cfg”
4、 打开“zoo.cfg”找到并编辑dataDir=D:\Kafka\zookeeper-3.4.9\tmp(必须以\分割)
5、 添加系统变量:ZOOKEEPER_HOME=D:\Kafka\zookeeper-3.4.9
6、 编辑path系统变量,添加路径:%ZOOKEEPER_HOME%\bin
7、 在zoo.cfg文件中修改默认的Zookeeper端口(默认端口2181)
8、 打开新的cmd,输入“zkServer“,运行Zookeeper
9、 命令行提示如下:说明本地Zookeeper启动成功
注意:不要关了这个窗口
当然你如果不确定有没有启动成功
在命令行中使用netstat -ano | findstr 2181(zookeeper默认是2181端口)
三、安装Kafka
1、 下载安装包
http://kafka.apache.org/downloads
注意要下载二进制版本
2、 解压并进入Kafka目录,笔者:D:\Kafka\kafka_2.12-0.11.0.0
3、 进入config目录找到文件server.properties并打开
4、 找到并编辑log.dirs=D:\Kafka\kafka_2.12-0.11.0.0\kafka-logs
5、 找到并编辑zookeeper.connect=localhost:2181
6、 Kafka会按照默认,在9092端口上运行,并连接zookeeper的默认端口:2181
7、 进入Kafka安装目录D:\Kafka\kafka_2.12-0.11.0.0,按下Shift+右键,选择“打开命令窗口”选项,打开命令行,输入:
.\bin\windows\kafka-server-start.bat .\config\server.properties
或bin\kafka-server-start.sh config\server.properties
注意:注意:不要关了这个窗口,启用Kafka前请确保ZooKeeper实例已经准备好并开始运行
四、创建SpringBoot项目
1、pom.xml中添加依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.kafka</groupId>
<artifactId>spring-kafka</artifactId>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>2.7.0</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>2.7.0</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>joda-time</groupId>
<artifactId>joda-time</artifactId>
<version>2.9.7</version>
</dependency>
2、配置application.yml文件
spring:
kafka:
producer:
# 发生错误后,消息重发的次数。
retries: 0
#当有多个消息需要被发送到同一个分区时,生产者会把它们放在同一个批次里。该参数指定了一个批次可以使用的内存大小,按照字节数计算。
batch-size: 16384
# 设置生产者内存缓冲区的大
buffer-memory: 33554432
# 键的序列化方式
key-serializer: org.apache.kafka.common.serialization.StringSerializer
# 值的序列化方式
value-serializer: org.apache.kafka.common.serialization.StringSerializer
# acks=0 : 生产者在成功写入消息之前不会等待任何来自服务器的响应。
# acks=1 : 只要集群的首领节点收到消息,生产者就会收到一个来自服务器成功响应。
# acks=all :只有当所有参与复制的节点全部收到消息时,生产者才会收到一个来自服务器的成功响应。
acks: 1
consumer:
# 自动提交的时间间隔 在spring boot 2.X 版本中这里采用的是值的类型为Duration 需要符合特定的格式,如1S,1M,2H,5D
auto-commit-interval: 1S
# 该属性指定了消费者在读取一个没有偏移量的分区或者偏移量无效的情况下该作何处理:
# latest(默认值)在偏移量无效的情况下,消费者将从最新的记录开始读取数据(在消费者启动之后生成的记录)
# earliest :在偏移量无效的情况下,消费者将从起始位置读取分区的记录
auto-offset-reset: earliest
# 是否自动提交偏移量,默认值是true,为了避免出现重复数据和数据丢失,可以把它设置为false,然后手动提交偏移量
enable-auto-commit: false
# 键的反序列化方式
key-deserializer: org.apache.kafka.common.serialization.StringDeserializer
# 值的反序列化方式
value-deserializer: org.apache.kafka.common.serialization.StringDeserializer
listener:
# 在侦听器容器中运行的线程数。
concurrency: 5
#listner负责ack,每调用一次,就立即commit
ack-mode: manual_immediate
missing-topics-fatal: false
server:
port: 8888
3、生产者Producer
package com.baba.kafka.controller;
@Slf4j
@RestController
@Api(value = "kafka操作类",description = "kafka操作类")
public class KafkaProducer {
@Autowired
private KafkaTemplate kafkaTemplate;
/**
* 自定义topic
*/
public static final String TOPIC_TEST = "topic.test";
/**
* 组别1
*/
public static final String TOPIC_GROUP1 = "topic.group1";
/**
* 组别2
*/
public static final String TOPIC_GROUP2 = "topic.group2";
@PostMapping(value = "/pushMessage")
@ApiOperation(notes = "kafka推送消息",value = "kafka推送消息")
public void pushMessage(String message){
log.info("准备发送消息信息:{}",message);
//发送消息
ListenableFuture<SendResult<String, Object>> send = kafkaTemplate.send(TOPIC_TEST, message);
DateTime time= new DateTime();
String dateTime = time.toString("yyyy-MM-dd hh:mm:ss");
log.info("topic:{}发送消息完成,完成时间:{}",TOPIC_TEST,dateTime);
send.addCallback(new ListenableFutureCallback<SendResult<String, Object>>() {
@Override
public void onFailure(Throwable throwable) {
log.error(TOPIC_TEST+"-生产者发送消息失败"+throwable.getMessage());
}
@Override
public void onSuccess(@Nullable SendResult<String, Object> stringObjectSendResult) {
DateTime time= new DateTime();
String dateTime = time.toString("yyyy-MM-dd hh:mm:ss");
log.info(TOPIC_TEST+"-生产者发送消息成功"+stringObjectSendResult.toString()+"时间:"+dateTime);
}
});
}
}
4、消费者Consumer
package com.baba.kafka.controller;
@Slf4j
@Component
public class KafkaConsumer {
@KafkaListener(topics = KafkaProducer.TOPIC_TEST,groupId = KafkaProducer.TOPIC_GROUP1)
public void consumerGroup1(ConsumerRecord<?, ?> record, Acknowledgment ack, @Header(KafkaHeaders.RECEIVED_TOPIC) String topic){
DateTime time= new DateTime();
String dateTime = time.toString("yyyy-MM-dd hh:mm:ss");
log.info("group1接收到消息时间:{}",dateTime);
Optional message = Optional.ofNullable(record.value());
if(message.isPresent()){
Object msg = message.get();
log.info("consumerGroup1 消费了: Topic:" + topic + ",Message:" + msg);
//手动提交偏移量
ack.acknowledge();
}
}
@KafkaListener(topics = KafkaProducer.TOPIC_TEST,groupId = KafkaProducer.TOPIC_GROUP2)
public void consumerGroup2(ConsumerRecord<?, ?> record, Acknowledgment ack, @Header(KafkaHeaders.RECEIVED_TOPIC) String topic){
DateTime time= new DateTime();
String dateTime = time.toString("yyyy-MM-dd hh:mm:ss");
log.info("group2接收到消息时间:{}",dateTime);
Optional message = Optional.ofNullable(record.value());
if(message.isPresent()){
Object msg = message.get();
log.info("consumerGroup2 消费了:Topic:" + topic + ",Message:" + msg);
//手动提交偏移量
ack.acknowledge();
}
}
}
5、Swagger配置
package com.baba.kafka.swagger;
@Configuration
@EnableSwagger2
public class Swagger2 {
@Bean
public Docket createRestApi() {
return new Docket(DocumentationType.SWAGGER_2)
.apiInfo(apiInfo())
.select()
.apis(RequestHandlerSelectors.basePackage("com.baba.kafka.controller"))
.paths(PathSelectors.any())
.build();
}
private ApiInfo apiInfo() {
return new ApiInfoBuilder()
.title("KafKa测试接口")
.version("1.0")
.build();
}
}