kafka是一个流式平台,官方的定义是:
A streaming platform has three key capabilities:
1,Publish and subscribe to streams of records, similar to a message queue or enterprise messaging system.
2,Store streams of records in a fault-tolerant durable way.
3,Process streams of records as they occur.
它能publish和subscribe消息,好像redis一样。它能以容错方式存储消息,因为可以有副本。它能处理消息流。
我们主要讲kafka与springboot的整合。
官网下载:
你可以选择binary版本2.13。
解压后的目录:
kafka依赖zookeeper,所以先启动zookeeper(cd到kafka解压目录):
bin/zookeeper-server-start.sh config/zookeeper.properties
然后再启动kafka:
bin/kafka-server-start.sh config/server.properties
我们可以看一下kafka自带的主题:
bin/kafka-topics.sh --bootstrap-server localhost:9092 --list
生产者写是往主题里写消息,消费者订阅也是订阅主题的,就像我在网易订阅了体育新闻,然后一旦有新的体育新闻,我就会收到消息。
一个主题会被分解成一个个partition,一个partition里面的消息是有索引的,是有序的。
我们先创建一个主题:
bin/kafka-topics.sh --create --topic firstTopic --bootstrap-server localhost:9092
9092是kafka默认的端口号。
创建之后,我们整一个生产者:
bin/kafka-console-producer.sh --bootstrap-server localhost:9092 --topic firstTopic
意思就是我们连的是kafka上的firstTopic
这个主题。
这个就是面向kafka编程,我们不用去管消费者的情况。
然后创建一个消费者:
bin/kafka-console-consumer.sh --bootstrap-server localhost:9092 --topic firstTopic
这时候生产者发送消息的话,消费者就会收到。
当然,我们使用的客户端语言是java,框架是springboot。
maven依赖:
<!-- https://mvnrepository.com/artifact/org.springframework.kafka/spring-kafka -->
<dependency>
<groupId>org.springframework.kafka</groupId>
<artifactId>spring-kafka</artifactId>
<version>2.4.5.RELEASE</version>
</dependency>
<!-- https://mvnrepository.com/artifact/com.google.code.gson/gson -->
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.8.6</version>
</dependency>
这是springboot中的关于kafka的依赖。
我们用了google的json来操作json数据。
首先自定义一个消息类:
package org.ocean.example.springboot.kafka;
import lombok.Data;
import java.time.ZonedDateTime;
@Data
public class KafkaMessage {
private Long id;
private String username;
private String password;
private ZonedDateTime zonedDateTime;
}
import com.google.gson.Gson;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.kafka.core.KafkaTemplate;
import org.springframework.stereotype.Component;
@Component
public class KafkaProducer {
@Autowired
private KafkaTemplate<String, String> kafkaTemplate;
public void sendMessage(KafkaMessage kafkaMessage) {
this.kafkaTemplate.send("firstTopic", new Gson().toJson(kafkaMessage));
}
}
我们使用kafka模版来向firstTopic
发送消息。
import lombok.extern.slf4j.Slf4j;
import org.apache.kafka.clients.consumer.ConsumerRecord;
import org.springframework.kafka.annotation.KafkaListener;
import org.springframework.stereotype.Component;
@Component
@Slf4j
public class KafkaConsumer {
@KafkaListener(topics = "firstTopic", groupId = "myGroup")
public void obtainMessage(ConsumerRecord<String, String> consumerRecord) {
String key = consumerRecord.key();
String value = consumerRecord.value();
int partition = consumerRecord.partition();
long timestamp = consumerRecord.timestamp();
String topic = consumerRecord.topic();
log.info("key: " + key + "\n" + "value: " + value + "\n" + "partition: " + partition + "\n" + "timestamp: " + timestamp + "\n" + "topic: " + topic);
}
}
我们监听firstTopic
中的消息,并且声明这个consumer是属于myGroup
这个组的。
我们打印了一些东西。
Each record consists of a key, a value, and a timestamp.
每条消息包含一个key,value和timestamp。
相同key的消息可以进入同一个partition。value就是发的string,timestamp是个时间戳,就是发消息的时间。
然后是controller:
package org.ocean.example.springboot.controller;
import org.ocean.example.springboot.kafka.KafkaMessage;
import org.ocean.example.springboot.kafka.KafkaProducer;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.time.ZonedDateTime;
import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE;
@RestController
@RequestMapping(value = "/kafka", produces = APPLICATION_JSON_VALUE)
public class KafkaController {
@Autowired
private KafkaProducer kafkaProducer;
@RequestMapping(value = "/message", method = RequestMethod.POST)
public KafkaMessage getMessageWithPost(@RequestBody KafkaMessage kafkaMessage) {
kafkaMessage.setZonedDateTime(ZonedDateTime.now());
kafkaProducer.sendMessage(kafkaMessage);
return kafkaMessage;
}
}
我们把输入的数据发送给监听的consumer,并且返回给浏览器。
最后是yml中的配置:
server:
port: 9090
spring:
kafka:
producer:
bootstrap-servers: localhost:9092
key-serializer: org.apache.kafka.common.serialization.StringSerializer
value-serializer: org.apache.kafka.common.serialization.StringSerializer
consumer:
bootstrap-servers: localhost:9092
key-deserializer: org.apache.kafka.common.serialization.StringDeserializer
value-deserializer: org.apache.kafka.common.serialization.StringDeserializer
给producer,我们配置了序列化的处理类,因为我们要把string对象序列化成字节数组传输。给consumer,我们要使用反序列化类来把字节数组还原成对象。
启动springboot。
并且在.http
文件中模拟post请求:
POST http://localhost:9090/kafka/message
Content-Type: application/json
{
"id": 1,
"username": "ocean",
"password": 1212
}
当然,你也可以用postman或者是curl。
控制台得到了正确的信息:
key: null
value: {"id":1,"username":"ocean","password":"1212","zonedDateTime":{"dateTime":{"date":{"year":2020,"month":4,"day":20},"time":{"hour":23,"minute":37,"second":19,"nano":479000000}},"offset":{"totalSeconds":28800},"zone":{"id":"Asia/Shanghai"}}}
partition: 0
timestamp: 1587397039522
topic: firstTopic