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配置的名称一样;

相关推荐
Grey_fantasy4 小时前
高级编程之结构化代码
java·spring boot·spring cloud
.生产的驴6 小时前
SpringCloud OpenFeign用户转发在请求头中添加用户信息 微服务内部调用
spring boot·后端·spring·spring cloud·微服务·架构
BestandW1shEs9 小时前
彻底理解消息队列的作用及如何选择
java·kafka·rabbitmq·rocketmq
一元咖啡15 小时前
SpringCloud Gateway转发请求到同一个服务的不同端口
spring·spring cloud·gateway
天天扭码15 小时前
五天SpringCloud计划——DAY2之单体架构和微服务架构的选择和转换原则
java·spring cloud·微服务·架构
跳跳的向阳花16 小时前
03-03、SpringCloud第三章,负载均衡Ribbon和Feign
spring cloud·ribbon·负载均衡
懒洋洋大魔王1 天前
RocketMQ的使⽤
java·rocketmq·java-rocketmq
天天扭码1 天前
五天SpringCloud计划——DAY1之mybatis-plus的使用
java·spring cloud·mybatis
尽兴-2 天前
Redis模拟延时队列 实现日程提醒
java·redis·java-rocketmq·mq