【Java】最新版本SpringCloudStream整合RocketMQ实现单项目中事件的发布与监听

news2025/1/24 18:03:06

文章目录

  • 前言
  • 依赖配置
  • 代码

参考

前言

SpringCloud项目中整合RocketMQ是为了削峰填谷。
这里我使用RocketMQ的作用用于接收项目中产生的消息,然后异步的发送邮件给客户,这是这个项目的产生的背景。

依赖配置

<dependencies>
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <!-- 引入基于 RocketMQSpring Cloud Bus 的实现的依赖,并实现对其的自动配置 -->
            <artifactId>spring-cloud-starter-bus-rocketmq</artifactId>
        </dependency>
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-stream-rocketmq</artifactId>
            <exclusions>
                <exclusion>
                    <groupId>org.apache.rocketmq</groupId>
                    <artifactId>rocketmq-client</artifactId>
                </exclusion>
                <exclusion>
                    <groupId>org.apache.rocketmq</groupId>
                    <artifactId>rocketmq-acl</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <!-- 引入基于 RocketMQSpring Cloud Bus 的实现的依赖,并实现对其的自动配置 -->
            <artifactId>spring-cloud-starter-bus-rocketmq</artifactId>
        </dependency>
        <dependency>
            <groupId>org.apache.rocketmq</groupId>
            <artifactId>rocketmq-client</artifactId>
            <version>4.9.4</version>
        </dependency>
        <dependency>
            <groupId>org.apache.rocketmq</groupId>
            <artifactId>rocketmq-acl</artifactId>
            <version>4.9.4</version>
        </dependency>
    </dependencies>

项目导入上面依赖之后即可开始代码的编写

代码

然后让我们先看一眼配置文件

# Tomcat
server:
  port: 9201

# Spring
spring:
  application:
    # 应用名称
    name: towelove-system
  profiles:
    # 环境配置
    active: dev
  cloud:
    nacos:
      discovery:
        # 服务注册地址
        server-addr: localhost:8848
      config:
        # 配置中心地址
        server-addr: localhost:8848
        # 配置文件格式
        file-extension: yaml
        # 共享配置
        shared-configs[0]:
          data-id: towelove-base-dev.yaml
          refresh: true
        shared-configs[1]:
          data-id: towelove-mysql-dev.yaml
          refresh: true
        shared-configs[2]:
          data-id: towelove-redis-dev.yaml
          refresh: true
    # Spring Cloud Stream 配置项,对应 BindingServiceProperties 类
    stream:
      function:
        definition: mailSendConsumer;sendSmsToAdmin;sendSmsToUser; # 需要确保消费者类的名称和这里一样
      # Binding 配置项,对应 BindingProperties Map
      bindings:
        sendSmsToAdmin-out-0: # 配置生产者
          destination: admin_sms_send
        sendSmsToAdmin-in-0:
          destination: admin_sms_send
          group: system_sms_send_consumer_group
        sendSmsToUser-out-0: # 配置生产者
          destination: admin_sms_send
        sendSmsToUser-in-0:
          destination: admin_sms_send
          group: system_sms_send_consumer_group
#        smsSendConsumer-in-0: # 配置消费者
#          destination: admin_sms_send
#          group: system_sms_send_consumer_group

#        smsSend-out-1:
#          destination: user_sms_send
#        smsSendConsumer-in-1:
#          destination: user_sms_send
#          group: system_sms_send_consumer_group

        mailSend-out-0:
          destination: system_mail_send
        mailSendConsumer-in-0: # 需要确保消费者类的名称和这里一样
          destination: system_mail_send
          group: system_mail_send_consumer_group
      # Spring Cloud Stream RocketMQ 配置项
      rocketmq:
        # RocketMQ Binder 配置项,对应 RocketMQBinderConfigurationProperties 类
        binder:
          name-server: 192.168.146.115:9876 # RocketMQ Namesrv 地址
        #          access-key: # 用户名
        #          secret-key:  # 密码
        default: # 默认 bindings 全局配置
          producer: # RocketMQ Producer 配置项,对应 RocketMQProducerProperties 类
            group: system_producer_group # 生产者分组
            send-type: SYNC # 发送模式,SYNC 同步

              # 如果你项目里只对接一个中间件,那么不用定义binders
              # 当系统要定义多个不同消息中间件的时候,使用binders定义
              #      binders:
              #        my-rocketmq:
              #          type: rocketmq
              #          environment:
              #            rocketmq:
              #              name-server: 192.168.146.115:9876
              #          access-key: # 用户名
            #          secret-key:  # 密码

    # Spring Cloud Bus 配置项,对应 BusProperties 类
    bus:
      enabled: true # 是否开启,默认为 true
      id: ${spring.application.name}:${server.port} # 编号,Spring Cloud Alibaba 建议使用“应用:端口”的格式
      destination: springCloudBus # 目标消息队列,默认为 springCloudBus

这里我截取了比较重要的配置,然后下面进行配置的讲解
在这里插入图片描述
首先就是我写了特别多注释的一个spring.cloud.stream.function.definition
这个东西是什么作用呢?
我的理解是,它用来声明你当前项目中的消费者,以及消费者类中的方法。
然后就是spring.cloud.stream.bindings中的好多个xxx-out-0和xxx-in-0
其中out对应的项目的输出,也就是消息的产生,对应的就是项目中的生产者,生产者发送消息的需要指定对应的信道,也就是你要告诉他往哪里发,其实就是对应的broker(再RocketMQ里面是这样子的),并且设定你发往的这个broker对应的topic,也就是destination。
那么同理,当生产者吧消息发送到broker中对应的topic后,我们就需要消费者去消费这个消息了。
那么此时就是使用in标签。
in标签里面的destination表示的也就是当前消费者需要去消费哪一个topic里面的消息。
你可能有一个疑问就是,那么为什么不用去指定对应的broker呢?
下面就是讲解这个in和out标签的声明的规则。
其实这也是一种约定优于配置的思想。
其中functionName就是你的消费者的类名或者你要提供消费的方法。
在命名规则的最后还有一个 index,它是 input 和 output 的序列,如果同一个 function name 只有一个 output 和一个 input,那么这个 index 永远都是 0。而如果你需要为一个 function 添加多个 input 和 output,就需要使用 index 变量来区分每个生产者消费者了。
Input 信道(消费者):< functionName > - in - < index >;

Output 信道(生产者):< functionName > - out - < index >。

讲解完这些,你大概就理解了这里的代码是为什么这么编写了。
那么下面我引入具体的业务代码。
我们从底层向上。
首先是消息的实体类。


@Data
public class SmsSendMessage {

    /**
     * 邮件日志编号
     */
    @NotNull(message = "邮件日志编号不能为空")
    private Long logId;
    /**
     * 接收邮件地址
     */
    @NotNull(message = "电话号码不能为空")
    private String phonenumber;
    /**
     * 邮件账号编号
     */
    @NotNull(message = "邮件账号编号不能为空")
    private Long accountId;

    /**
     * 邮件发件人
     */
    private String nickname;
    /**
     * 邮件标题
     */
    @NotEmpty(message = "邮件标题不能为空")
    private String title;
    /**
     * 邮件内容
     */
    @NotEmpty(message = "邮件内容不能为空")
    private String content;

    private Boolean isHtml;

    private File[] files;

}

这个是消息的生产者

@Slf4j
@Service
public class SmsProducer {
    @Autowired
    private StreamBridge streamBridge;

    public void sendSmsToAdmin(SmsSendMessage message) {
        log.info("要发送的短信内容为: {}", message);
        streamBridge.send("sendSmsToAdmin-out-0", message);
    }

    public void sendSmsToUser(Long userId,Long accountId) {
        log.info("要发送的短信内容为: {}", "userId:"+userId+"accountId:"+accountId);
        streamBridge.send("sendSmsToUser-out-0",  "userId:"+userId+"  accountId:"+accountId);
    }

}

然后就是控制层

@RestController
@RequestMapping("/sys/sms")
public class SmsController {
    @Autowired
    private SmsProducer smsProducer;
    @PostMapping("/send/admin")
    public R<Boolean> sendSmsToAdmin(@RequestBody @Valid SmsSendMessage message){
        smsProducer.sendSmsToAdmin(message);
        return R.ok();
    }

    @PostMapping("/send/user")
    public R<Boolean> sendSmsToUser(@RequestParam("userId")Long userId,
                                    @RequestParam("accountId")Long accountId){
        smsProducer.sendSmsToUser(userId,accountId);
        return R.ok();
    }

}

然后下面是事件消费者的第一种写法

@Component
@Slf4j
public class SmsSendConsumer //implements Consumer<SmsSendMessage>
{
    //@Override
    //public void accept(SmsSendMessage message) {
    //    System.out.println(message);
    //}
    @Bean
    public Consumer<String> sendSmsToAdmin() {
        return reqest -> {
            log.info("received: {} ", reqest);
        };
    }

    @Bean
    public Consumer<String> sendSmsToUser(){
        return request -> {
            log.info("received: {}", request);
            List<Long> params = Arrays.stream(request.split(","))
                    .map(Long::valueOf)
                    .collect(Collectors.toList());
            System.out.println(params);
        };
    }

}

简单的介绍一下代码的逻辑,
其实就是我们向控制层发送一个请求并且携带上一些参数之后,控制层让生产者发送一个消息到对应的消息队列中。
发现了吗,这里消息的生产者发送的消息的目的地,就是我们设定的out标签。
在这里插入图片描述
那么消费者如何知道要去消费消息呢?
这就是为什么上面我说function.definition和in标签的作用了。
in标签这里的前缀就是我们的方法名,也就是对应的broker中的topic有消息后,对应的消费者会把消息拉过来,然后进行消费,而他之所以能知道要去消费哪一个消息也就是因为这里的绑定好的原因。
所以如果你一个类中声明了多个的消费方法,只需要再function.definition这个地方声明出你方法的名称,并且再代码里面使用@Bean的方式去声明出对应的方法即可
也就是如下图一样。
在这里插入图片描述
那么好奇的你可能会发现,这样子可以定义多个方法,还挺不错的,就是好像有点麻烦欸,要写的东西一下子就多了。
所以,如果你的消费者类只有一个方法,也就是你当前要消费的消费者只需要提供唯一的方法,那么我们可以把function.definition这里的方法名编写为消费者类的名称。
也就是下面这种代码的方式
在这里插入图片描述
而我们的生产者还是一样,只要确保其发送消息的信道是确定的即可
在这里插入图片描述
那么以这两种方式,如果你的消费者需要提供多个方法,那么就使用第一种方式,而如果你的消费者是单一的,只需要提供某一种方法,那么直接使用第二种方法去实现某个类即可。

当然,两种方式可以混合在一起实现
如果你在你的代码中出现了下图的问题
在这里插入图片描述
可以查看我下面这篇文章

解决上图的问题

类似的springcloudstream整合rocketmq的问题可以私信我一起研究

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/396833.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

开发钉钉微应用,实现免登+调试

1.创建h5微应用 https://open.dingtalk.com/document/orgapp/develop-org-h5-micro-applications 根据里面的三个步骤,创建h5微应用 2.免登之前必须要先进行JSAPI的授权 文档说明: https://open.dingtalk.com/document/orgapp/jsapi-authentication 根据文档中的说明 步骤…

spring事物源码分析

今天的任务是剖析源码&#xff0c;看看Spring 是怎么运行事务的&#xff0c;并且是基于当前最流行的SpringBoot。还有&#xff0c;我们之前剖析Mybatis 的时候&#xff0c;也知道&#xff0c;Mybatis 也有事务&#xff0c;那么&#xff0c;他俩融合之后&#xff0c;事务是交给谁…

MySQL (六)------MySQL的常用函数、 事务(TCL)、DCL用户操作语句、常见环境、编码问题

第一章 MySQL的常用函数 1.1 字符串函数 1.1.1 字符串函数列表概览 函数用法CONCAT(S1,S2,......,Sn)连接S1,S2,......,Sn为一个字符串CONCAT_WS(separator, S1,S2,......,Sn)连接S1一直到Sn&#xff0c;并且中间以separator作为分隔符CHAR_LENGTH(s)返回字符串s的字符数LENGTH…

深入理解性能压测工具原理

如果没有性能测试工具如何通过手工实现 如果没有性能测试工具&#xff0c;通过手工进行性能测试&#xff0c;这是一个值得我们思考的问题。这时候需要一个协调员发送指令&#xff0c;一个操作员进行操作&#xff0c;对系统施加压力&#xff0c;多个操作员代表着多个用户进行并…

面向对象设计模式:创建型模式之原型模式

文章目录一、引入二、代理模式&#xff0c;Prototype Pattern2.1 Intent 意图2.2 Applicability 适用性2.3 类图2.4 应用实例&#xff1a;使用下划线或消息框展示字符串2.4 应用实例&#xff1a;JDK java.lang.Object java.lang.Cloneable一、引入 二、代理模式&#xff0c;Pr…

Python—看我分析下已经退市的 可转债 都有什么特点

分析 需求分析 可转债退市原因的种类与占比是多少 强赎与非强赎导致的退市可转债 存续时间 维度占比 强赎与非强赎导致的退市可转债 发行资金 规模占比 强赎与非强赎导致的退市可转债 各个评级 的占比 强赎与非强赎导致的退市可转债 各个行业&#xff08;一级行业&#xf…

互相关延时估计 Matlab仿真

文章目录互相关延时估计什么是互相关延时估计&#xff1f;原理代码实现总结互相关延时估计 互相关延时估计是一种信号处理技术&#xff0c;用于计算两个信号之间的时间延迟。在本篇博客中&#xff0c;我们将使用MATLAB来实现互相关延时估计&#xff0c;并提供多个例子和代码&a…

TypeScript深度剖析: TypeScript 中函数的理解?与 JavaScript 函数的区别?

一、是什么 函数是 JavaScript 应用程序的基础&#xff0c;帮助我们实现抽象层、模拟类、信息隐藏和模块 在 TypeScript 里&#xff0c;虽然已经支持类、命名空间和模块&#xff0c;但函数仍然是主要定义行为的方式&#xff0c;TypeScript 为 JavaScript 函数添加了额外的功能…

English Learning - L2-5 英音地道语音语调 弹力双元音 [ɪə] [ʊə] [eə] 2023.03.6 周一

English Learning - L2-5 英音地道语音语调 弹力双元音 [ɪə] [ʊə] [eə] 2023.03.6 周一朗读节奏元音的长度元音发音在清辅音和浊辅音前的区别元音发音跟后面浊辅音节数的区别元音在重读音节中复习大小元音发音对比/ʌ/ 舌中音/ɒ/ 舌后音/ʊ/ 舌后音/ɪ/ 舌前音[ɑ:] VS […

Jenkins+Docker自动化部署项目

看到了一篇文章&#xff0c;实操一下自动部署的感觉。参看地址&#xff1a;原文 首先更新docker&#xff0c;我更新到了 [rootlocalhost springboot]# docker --version Docker version 23.0.1, build a5ee5b1跟新步骤&#xff1a; yum update#卸载旧版本 yum remove dock…

Nginx支持quic协议

第一种方式&#xff1a;Nginx官方nginx-quic搭建 通过部署Nginx官方的QUIC分支来实现的浏览器和nginx-quic服务器粗略的HTTP3通信。 1、下载BoringSSL BoringSSL 是由谷歌开发,从 OpenSSL 中分离的一个分支。BoringSSL 是 Chrome/Chromium、Android&#xff08;但它不是 NDK 的…

Spark Yarn 运行环境搭建

文章目录Spark Yarn 运行环境搭建1、解压缩文件2、修改配置环境文件3、配置历史服务器Spark Yarn 运行环境搭建 1、解压缩文件 将spark3.2.3的压缩包上传到 linux /opt/software 目录下 输入命令&#xff1a; tar -zxvf spark-3.2.3-bin-hadoop3.2-scala2.13.tgz -C /opt/ 解…

究竟是谁负了谁,来自底层测试的2022年终总结

前言 说实话坐在椅子前&#xff0c;都想好了&#xff0c;该怎么去写&#xff0c;甚至感觉有好多要写的&#xff0c;但是当我坐在椅子上时&#xff0c;却不知道该怎么开头了&#xff0c;不知道是不是紧张&#xff1f;还是不舍&#xff1f;难道还没有跟过去挥手告别的勇气吗&…

lambda函数

Lambda(函数指针)lambda 是c11非常重要也是最常用的特性之一&#xff0c;他有以下优点&#xff1a;可以就地匿名定义目标函数或函数对象&#xff0c;不需要额外写一个函数lambda表达式是一个匿名的内联函数lambda表达式定义了一个匿名函数&#xff0c;语法如下&#xff1a;[cap…

Vue3手写分页在分页的基础上用到Pagination 分页组件

近期有个项目要用到分页组件&#xff0c;但是内容不是表格&#xff0c;所以自己就研究了一下在Pagination 分页组件的基础上手写了分页 效果图&#xff1a; 目录 一、先声明几个变量用来定义第几页&#xff0c;每页多少条&#xff0c;总页数。 二、然后封装一个函数方便以后…

学习 Python 之 Pygame 开发魂斗罗(十)

学习 Python 之 Pygame 开发魂斗罗&#xff08;十&#xff09;继续编写魂斗罗1. 解决敌人不开火的问题2. 创建爆炸效果类3. 为敌人跳入河中增加爆炸效果4. 玩家击中敌人继续编写魂斗罗 在上次的博客学习 Python 之 Pygame 开发魂斗罗&#xff08;九&#xff09;中&#xff0c;…

pycharm 使用方式

提示&#xff1a; pycharm 是专业版&#xff1b; 社区版目前暂不支持调试 Jupyter; 如果你是学生&#xff0c; 在读期间&#xff0c;每一年可以申请免费一年使用专业版&#xff0c; 详情到Jetbrain 去申请&#xff1b; 在申请过程中&#xff0c;需要注意&#xff0c; 不要使用…

简单给WordPress怎么添加自定义字段面板

今天一淘模板(56admin.com)WordPress怎么添加自定义字段面板&#xff1f;下面本篇文章给大家介绍一下WordPress添加自定义字段面板的方法&#xff0c;希望对大家有所帮助&#xff01; 我们在WordPress中编写文章的时候&#xff0c;经常会用到一些自定义字段&#xff0c;如网页描…

Vue3笔记01 创建项目,Composition API,新组件,其他

Vue3 创建Vue3项目 vue-cli //查看vue/cli版本&#xff0c;确保在4.5.0以上 vue --version //安装或升级vue/cli npm install -g vue/cli //创建项目 vue create new_project //启动 cd new_project npm run serve 也可以通过vue ui进入图形化界面进行创建 vite 新一代前端…

论文笔记:Positive-incentive Noise

2022 TNNLS 中心思想是&#xff1a;噪声并不一定是有害的 1 CV问题中的噪声 以图像分类为例 对图像加入适量的噪声后再训练&#xff0c;识别准确率反而上升了 再以目标检测为例&#xff1a; 从遥感影像中做飞机检测&#xff0c;一般都是把飞机紧紧框住&#xff0c;然后做…