消息队列解耦及基本使用

消息队列的解耦,就是上下游服务相互依赖,但上游服务不关心下游服务结果,同时需要降低整体响应时间,且下游即便出现问题也不需要上游来解决的场景。

解耦演示场景

一个模块A接受前端请求,收到请求之后需要调用另一个模块B,以完成后续事项。一开始需要B做完之后,才回包给前端,这是第一种流程,这种流程很明显会很依赖模块B的运作情况。

但是如果业务允许,我们还可以有第二种流程:模块A不用等模块B把事情做完,只用将信息传递到一个中转站,B从中转站感知到这件事,再自己去做就可以了,这个中转站,就是消息队列,可以起到解耦的作用。

1. CountService.java(模拟耗时业务)

java 复制代码
package org.example.kafkademo1;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Service;

@Service
public class CountService {
    @Autowired
    private JdbcTemplate jdbcTemplate;

    public void doHeavyWork() {
        // 模拟耗时操作
        for (int i = 0; i < 1000000; i++) {
            if (i % 100000 == 0) {
                jdbcTemplate.update("UPDATE t_count SET count_value = count_value + 1 WHERE id = 1");
            }
        }
    }
}

2. DecouplingController.java(对比接口)

java 复制代码
package org.example.kafkademo1;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.kafka.core.KafkaTemplate;
import org.springframework.web.bind.annotation.*;

@RestController
@RequestMapping("/demo")
public class DecouplingController {

    @Autowired
    private CountService countService;
    
    @Autowired
    private KafkaTemplate<String, String> kafkaTemplate;

    // 解耦前:同步调用,用户需要等待
    @GetMapping("/sync")
    public String sync() {
        long start = System.currentTimeMillis();
        countService.doHeavyWork();
        return "同步任务耗时:" + (System.currentTimeMillis() - start) + "ms";
    }

    // 解耦后:异步调用,秒回用户
    @GetMapping("/async")
    public String async() {
        long start = System.currentTimeMillis();
        // 丢给 Kafka 后直接返回
        kafkaTemplate.send("decoupling-topic", "start-task");
        return "解耦成功!用户感知耗时:" + (System.currentTimeMillis() - start) + "ms";
    }
}

3. KafkaConsumer.java(后台执行者)

java 复制代码
package org.example.kafkademo1;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.kafka.annotation.KafkaListener;
import org.springframework.stereotype.Component;

@Component
public class KafkaConsumer {
    @Autowired
    private CountService countService;

    @KafkaListener(topics = "decoupling-topic", groupId = "my-group")
    public void handle(String message) {
        System.out.println(">>> Kafka 收到指令,后台开始干活...");
        countService.doHeavyWork();
        System.out.println(">>> 后台活干完了!");
    }
}

对比各自返回结果可得:

最后来抽象一下,解耦其实本质就是A不再关心B的事情,以及不再受B本身变动的影响

但是,MQ用来解耦和用来异步,这两者该如何区分?

  • 目的不一样,看业务的目的是什么
  • 比如一个服务觉得他不想关心后面服务的返回,这就是解耦
  • 如果一个服务,想关心结果,但是一次调用实在太久了,就是异步,回头还要轮询或者通过回调去得到结果。

Kafka的基本使用

通过上述应用场景总结,我们大概已经知道Kafka的基本用法,现在来整体做一个总结:

1.引入Maven依赖

pom.xml 中加入 spring-kafka。它会自动把底层的 Kafka 客户端驱动也带进来。

XML 复制代码
<dependency>
    <groupId>org.springframework.kafka</groupId>
    <artifactId>spring-kafka</artifactId>
</dependency>

2. YAML/Properties 配置

这是最基础的连接配置,让你的 Java 程序能找到 Docker 里的 Kafka。

XML 复制代码
# 核心:Kafka 服务器地址
spring.kafka.bootstrap-servers=localhost:9092
# 消费者必须指定的组 ID
spring.kafka.consumer.group-id=my-group

简单来说,Group-ID 就是消费者的"团队名" :它让 Kafka 记住这个团队读到了哪一页 (Offset)以保证重启不丢进度,并让同组的多个成员分工协作 (负载均衡)处理同一个 Topic,从而实现海量数据的高效并行消费。既然你在配置里写了 my-group,Spring 就默认你所有的消费者方法都属于这个组。

3.代码

在 Java 代码里,只需要记住两个核心角色:发消息的 Template收消息的 Listener

生产者:KafkaTemplate (发件人)

它是 Spring 提供的一个工具类,你只需要 @Autowired 进来,然后调用**send**方法。

  • 特点 :异步执行。消息丢进缓冲区就立即返回,这就是你看到 0ms 的原因。
java 复制代码
@Autowired
private KafkaTemplate<String, String> kafkaTemplate;

public void sendMessage(String msg) {
    // 参数:Topic名, 消息内容
    kafkaTemplate.send("my-topic", msg); 
}
消费者:@KafkaListener (收件人)

这是一个"被动触发"的注解。你不需要写死循环去读取消息,只要 Kafka 里有了新消息,Spring 会自动调用这个方法。

  • 特点:运行在独立的线程池里,不会阻塞主线程。
java 复制代码
@KafkaListener(topics = "my-topic")
public void listen(String message) {
    // 收到消息后的逻辑处理(比如你写的耗时计数)
    System.out.println("收到消息:" + message);
}
相关推荐
indexsunny3 小时前
互联网大厂Java求职面试实战:核心技术与业务场景解析
java·spring boot·redis·微服务·kafka·互联网大厂·面试技巧
学到头秃的suhian7 小时前
消息队列削峰和分发
kafka
014-code14 小时前
kafka + springboot快速入门
java·spring boot·分布式·kafka
⑩-14 小时前
RabbitMQ与Kafka的区别?
分布式·kafka·rabbitmq
没有bug.的程序员14 小时前
支付扣款成功却未发货?Spring Boot 整合 Kafka 事务消息的物理级防丢防重生死局
spring boot·spring·kafka·linq·事务消息·支付扣款
Jackyzhe14 小时前
从零学习Kafka:副本机制
分布式·学习·kafka
后季暖15 小时前
kafka优化
数据库·分布式·kafka
future02101 天前
Kafka积压急救:根治方案全解析
分布式·kafka
飞Link1 天前
Kafka~本地Python Kafka发送数据,服务端Kafka消费不到
java·分布式·kafka