消息队列/消息中间件(Message Queue):
在消息的传输过程中保存消息的容器(本质是暂存数据的容器)。
主要用途:
- 解耦
A系统发送数据到BCD三个系统,如果通过接口调用发送,那么他们之间的耦合度非常高,A会变得非常难以维护,如果BCD中的某个故障,A也会受影响。
可以引入MQ进行解耦------>A系统发送数据到MQ,DCD订阅MQ的数据进行处理,当B系统不需要再处理数据的时候只需要取消MQ的订阅即可,当新增的E系统需要处理数据的时候只需要订阅MQ即可,无论哪种方式都不需要再对A系统进行修改,如果BCD中的某个故障,A也不会受影响。
- 异步
解耦前A系统消耗时间是A+B+C+D。
解耦后 A 系统销毁时间是 A + 投放数据到消息队列的时间,BCD 再后台同时执行。 - 削峰
高峰期的大量请求先放入 MQ 暂存,再由 A 系统慢慢读取处理。
常见的消息队列:Kafka、RabbitMQ、RocketMQ
kafka:发布式发布-订阅消息传递系统(消息队列/消息中间件)
特点:
1,高吞吐量,低延时:每秒可以处理几十万条数据,延迟在几毫秒内。
2,可扩展(扩容),支持热扩展。
3,可靠性高,有备份机制保证数据不丢失。
4,高并发,支持数千个客户端同时读写操作。
概念:
broker:kafka集群中的每一个节点。
topic:主题,区分不同来源的数据。
partition:分区,topic中用于存储数据的地方。
offset:偏移量,数据在partition中的索引。
producer:生产者,向MQ中发布数据的程序。
customer:消费者,从MQ中读取数据的程序。
windows 版本的使用:
1, 启动 zookeeper
1,以管理员身份打开命令行窗口
2,切换到 D 盘
3, 进入 kafka 安装目录
4, 启动 zookeeper
bin\windows\zookeeper-server-start.bat config\zookeeper.properties
2,启动 kafka
1,以管理员身份打开命令行窗口
2, 切换到 D 盘
3,进入 kafka 安装目录
4,启动 kafka
bin\windows\kafka-server-start.bat config\server.properties
3, 创建 topic
1,以管理员身份打开命令行窗口
2, 切换到 D 盘
3,进入 kafka 安装目录
4,创建 test topic
bin\windows\kafka-topics.bat --create --bootstrap-server localhost:9092 --replication-factor 1 --partitions 1 --topic test
5,查看所有的 topic
bin\windows\kafka-topics.bat --list --bootstrap-server localhost:9092
4,启动自带的控制台生产者向 kafka 的 test topic 写数据
1,以管理员身份打开命令行窗口
2,切换到 D 盘
3,进入 kafka 安装目录
4,启动生产者
bin\windows\kafka-console-producer.bat --broker-list localhost:9092 --topic test
5,启动自带的控制台消费者从 kafka 的 test topic 读数据
1,以管理员身份打开命令行窗口
2,切换到 D 盘
3,进入 kafka 安装目录
4,启动消费者
bin\windows\kafka-console-consumer.bat --bootstrap-server localhost:9092 --topic test --from-beginning
在 spring 中操纵 kafka
1,引入依赖
1,确定依赖的版本号
https://spring.io/projects/spring-kafka#overview
2,添加依赖
<!--spring中使用kafka-->
<dependency>
<groupId>org.springframework.kafka</groupId>
<artifactId>spring-kafka</artifactId>
<version>2.2.10.RELEASE</version>
</dependency>
2,生产者
1,配置
#配置kafka生产者信息
#bootstrap-servers配置kafka集群地址
spring.kafka.producer.bootstrap-servers=localhost:9092
2, 在 service 中注入 KafkaTemplate
@Autowired
private KafkaTemplate kafkaTemplate;
3,使用 KafkaTemplate 的 send 方法向指定 topic 发送数据
@Override
public void sendMessage(String message) {
//向test topic 发送数据
kafkaTemplate.send("test",message);
}
3. 消费者
1,配置
#配置kafka消费者信息
#设置消费组编号
spring.kafka.consumer.group-id=users
#自动提交已读取数据的offset
spring.kafka.consumer.enable-auto-commit=true
#消费者只读取启动后产生的数据
spring.kafka.consumer.auto-offset-reset=latest
#kafka集群地址
spring.kafka.consumer.bootstrap-servers=localhost:9092
2,在 serviceImpl 添加 receiveMessage(String message) 方法接收 kafka 数据
3,为方法增加 @KafkaListener(topics = “test”) 注解
读取 test topic 的数据
@KafkaListener(topics = "test")
public void receiveMessage(String message){
System.out.println(message);
}
4,向 kafka 中写对象:
把对象转换为 json 字符串,然后发送即可
@Override
public void sendUser(User user) {
try {
//1.User转json
String json = new ObjectMapper().writeValueAsString(user);
//2.把json放入kafka
kafkaTemplate.send("test2",json);
} catch (JsonProcessingException e) {
e.printStackTrace();
}
}
5,从 kafka 中取对象:
把读到的 json 字符串还原为对象
对象如果没有泛型:
new ObjectMapper().readValue(json, Xxx.class);
对象如果有泛型:
new ObjectMapper().readValue(json, new TypeReference<Xxx(){});
@KafkaListener(topics = "test2")
public void receiveUser(String json){
try {
User user = new ObjectMapper().readValue(json, User.class);
System.out.println(user);
} catch (IOException e) {
e.printStackTrace();
}
}