SpringCloud之Stream框架集成RocketMQ消息中间件

Spring Cloud Stream 是一个用来为微服务应用构建消息驱动能力的框架。它可以基于 Spring Boot 来创建独立的、可用于生产的 Spring 应用程序。Spring Cloud Stream 为一些供应商的消息中间件产品提供了个性化的自动化配置实现,并引入了发布-订阅、消费组、分区这三个核心概念。简单的说,Spring Cloud Stream本质上就是整合了Spring Boot和Spring Integration,实现了一套轻量级的消息驱动的微服务框架。

目前 Spring Cloud Stream只支持 RabbitMQ 和 Kafka 的自动化配置。

Spring Cloud Stream 提供了 Binder (负责与消息中间件进行交互),我们则通过 inputs 或者 outputs 这样的消息通道与 Binder 进行交互。Binder 绑定器是 Spring cloud Stream 中一个非常重要的概念,实现了应用程序和消息中间件之间的隔离,同时我们也可以通过应用程序实现,消息中间件之间的通信。在我们的项目的可以继承多种绑定器,我们可以根据不同特性的消息使用不同的消息中间件。Spring Cloud Stream 为我们实现了 RabbitMQ 和Kafka 的绑定器。如果你想使用其他的消息中间件需要自己去实现绑定器接口.

在 SpringCloudStream 3.x 版本前是通过 @StreamListener 和 @EnableBinding 进行消息的发送和消费的,springCloudStream 3.x 版本后 @StreamListener 和 @EnableBinding 都打上了@Deprecated 注解,不建议使用了;后续的版本更新中替换成函数式的方式实现。

既然通过四大函数式接口的方式替换了注解的方式 那么该如何进行绑定呢?

通过 spring.cloud.stream.function.definition:名称的方式进行绑定 公开 topic。

不管是创建 Consumer 还是 Supplier 或者是 Function Stream都会将其的 方法名称 进行 一个 topic拆封 和 绑定 假设 创建了一个 Consumer< String > myTopic 的方法,Stream 会将其 拆分成 In 和 out 两个通道:

  • 输入 - + -in- + < index >

例如:myTopic-in-0

  • 输出 - + -out- + < index >

例如:myTopic-out-0

注意:这里的 functionName需要和代码声明的函数名称还有spring.cloud.stream.function.definition下的名称保持一致(后面还会在项目实战中展示一遍)

代码示例:

----------------------------------项目实战--------------------------------------

看下我们项目中的配置,配置文件是放在nacos上面的:

消息发送:

复制代码
/**
 * @ClassName MessageParamParentDto
 * @Author zxd
 * @Version 1.0.0
 * @Description TODO
 * @CreateTime 2023/6/13 11:27 - 星期二
 */
@Data
public class MessageParamParentDto implements Serializable {

    private static final long serialVersionUID = 7963819193258646924L;


    private  String routeUrl;

}

复制代码
/**
 * @ClassName MessageParamDto
 * @Author kch
 * @Version 1.0.0
 * @Description 消息队列接收系统消息实体对象
 * @CreateTime 2022/9/18 15:16 - 星期日
 */
@Data
public class MessageParamDto  extends MessageParamParentDto implements Serializable {

    private static final long serialVersionUID = 7111819193258646924L;

    /**
     * 消息模板code
     */
    @NotNull(message = "消息模板不能为空")
    private String templateCode;

    /**
     * 可变参数,必传字段
     * 该参数匹配模板字符串中的变量和URL中的变量,所以模板和URL中的变量名不能重复
     */
    @NotNull(message = "参数不能为空")
    private Map<String, String> params;

    /**
     * 消息详情跳转路径参数(没有不传,有参数按照URL参数拼接规范拼接,不加?号)
     * 例如:userId=1&userCode=test
     */
//    private String routerParams;

    /**
     * 消息操作跳转路径参数(没有不传,有参数按照URL参数拼接规范拼接,不加?号)
     * 例如:userId=1&userCode=test
     */
//    private String contentPathParams;

    /**
     * 接收者租户
     */
    @NotNull(message = "接收者租户ID不能为空")
    private Long tenantId;

    /**
     * 接收人
     */
    @NotNull(message = "接收者用户ID不能为空")
    @Size(min = 1, message = "接收者用户ID不能为空")
    private List<RecipientUser> recipientUsers;

    @Valid
    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    public static class RecipientUser implements Serializable {

        /**
         * 接收人id
         */
        @NotNull(message = "接收者用户ID不能为空")
        private Long recipientId;

        /**
         * 接收人手机号
         */
        @Pattern(regexp = RegexPool.MOBILE, message = "手机格式错误")
        private String phone;

    }

}

复制代码
/**
 * @ClassName MessageMqBinding
 * @Author zpp
 * @Version 1.0.0
 * @Description TODO
 * @CreateTime 2023/2/10 15:37 - 星期五
 */
public interface MessageMqBinding {

    /**
     * 系统消息生产者交换机
     */
    String MESSAGE_MQ_OUTPUT = "dyzsMessageProvider-out-0";
}

复制代码
@Slf4j
@RestController
@RequestMapping("/mq")
public class MessageMqController {
    @Resource
    private StreamBridge streamBridge;
    
    /**
     * @param :
     * @Author zpp
     * @Description 发送系统消息
     * @Date 2023/2/10 15:27
     * @Return com.zysy.common.api.entity.Result<java.lang.Boolean>
     */
    @PostMapping
    public Result<Boolean> sendMessage(@RequestBody @Validated MessageParamDto dto) {
        log.info("接收到系统消息发送请求:{}", JSONObject.toJSONString(dto));
        MessageMQParamDto paramDto = new MessageMQParamDto(dto);
        paramDto.setCreateBy(UserUtil.getUserId());
        paramDto.setCreateDept(UserUtil.getDeptId());
        List<MessageMQParamDto> paramDtoList = new ArrayList<>();
        paramDtoList.add(paramDto);
        MessageBuilder builder = MessageBuilder.withPayload(paramDtoList)
                .setHeader("Content-Type", "application/json");
        return Result.success(streamBridge.send(MessageMqBinding.MESSAGE_MQ_OUTPUT, builder.build()));
    }

消息消费:

下图是在代码中配置的消息消费者,这里的函数名称要和上图中的function.definition配置的名称一样;

相关推荐
aloha_7894 小时前
从零记录搭建一个干净的mybatis环境
java·笔记·spring·spring cloud·maven·mybatis·springboot
茶馆大橘6 小时前
微服务系列五:避免雪崩问题的限流、隔离、熔断措施
java·jmeter·spring cloud·微服务·云原生·架构·sentinel
荆州克莱11 小时前
[FE] React 初窥门径(四):React 组件的加载过程(render 阶段)
spring boot·spring·spring cloud·css3·技术
Genius Kim13 小时前
SpringCloud Sentinel 服务治理详解
spring cloud·sentinel·php
为美好的生活献上中指14 小时前
Java学习Day60:微服务总结!(有经处无火,无火处无经)
java·spring boot·spring cloud·微服务·sentinel·jetty
小沈同学呀14 小时前
Mac M1 Docker创建Rocketmq集群并接入Springboot项目
macos·docker·java-rocketmq·springboot
BUG指挥官20 小时前
深度解析阿里的Sentinel
spring boot·spring·spring cloud·sentinel
WANT_如初21 小时前
SpringCloud-Eureka注册中心
java·spring cloud
不想睡觉的橘子君21 小时前
【MQ】RabbitMQ、RocketMQ、kafka特性对比
kafka·rabbitmq·rocketmq