• SpringBoot实用开发篇第六章(整合第三方技术,ActiveMQ,RabbitMQ,RocketMQ,Kafka)


    一、邮件

    springboot整合第三方技术第三部分我们来说说邮件系统,发邮件是java程序的基本操作,springboot整合javamail其实就是简化开发。不熟悉邮件的小伙伴可以先学习完javamail的基础操作,再来看这一部分内容才能感触到springboot整合javamail究竟简化了哪些操作。简化的多码?其实不多,差别不大,只是还个格式而已。

    ​ 学习邮件发送之前先了解3个概念,这些概念规范了邮件操作过程中的标准。

    SMTP(Simple Mail Transfer Protocol):简单邮件传输协议,用于发送电子邮件的传输协议
    POP3(Post Office Protocol - Version 3):用于接收电子邮件的标准协议
    IMAP(Internet Mail Access Protocol):互联网消息协议,是POP3的替代协议
    

    ​ 简单说就是SMPT是发邮件的标准,POP3是收邮件的标准,IMAP是对POP3的升级。我们制作程序中操作邮件,通常是发邮件,所以SMTP是使用的重点,收邮件大部分都是通过邮件客户端完成,所以开发收邮件的代码极少。除非你要读取邮件内容,然后解析,做邮件功能的统一处理。例如HR的邮箱收到求职者的简历,可以读取后统一处理。但是为什么不制作独立的投递简历的系统呢?所以说,好奇怪的需求,因为要想收邮件就要规范发邮件的人的书写格式,这个未免有点强人所难,并且极易收到外部攻击,你不可能使用白名单来收邮件。如果能使用白名单来收邮件然后解析邮件,还不如开发个系统给白名单中的人专用呢,更安全,总之就是鸡肋了。下面就开始学习springboot如何整合javamail发送邮件。

    发送简单邮件
    步骤①:导入springboot整合javamail的starter

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-mail</artifactId>
    </dependency>
    

    步骤②:配置邮箱的登录信息

    spring:
      mail:
        host: smtp.126.com
        username: test@126.com
        password: test
    

    ​ java程序仅用于发送邮件,邮件的功能还是邮件供应商提供的,所以这里是用别人的邮件服务,要配置对应信息。

    ​ host配置的是提供邮件服务的主机协议,当前程序仅用于发送邮件,因此配置的是smtp的协议。

    ​ password并不是邮箱账号的登录密码,是邮件供应商提供的一个加密后的密码,也是为了保障系统安全性。不然外部人员通过地址访问下载了配置文件,直接获取到了邮件密码就会有极大的安全隐患。有关该密码的获取每个邮件供应商提供的方式都不一样,此处略过。可以到邮件供应商的设置页面找POP3或IMAP这些关键词找到对应的获取位置。下例仅供参考:

    image20220228111251036

    步骤③:使用JavaMailSender接口发送邮件

    @Service
    public class SendMailServiceImpl implements SendMailService {
        @Autowired
        private JavaMailSender javaMailSender;
    
        //发送人
        private String from = "test@qq.com";
        //接收人
        private String to = "test@126.com";
        //标题
        private String subject = "测试邮件";
        //正文
        private String context = "测试邮件正文内容";
    
        @Override
        public void sendMail() {
            SimpleMailMessage message = new SimpleMailMessage();
            message.setFrom(from+"(小甜甜)");
            message.setTo(to);
            message.setSubject(subject);
            message.setText(context);
            javaMailSender.send(message);
        }
    }
    

    ​ 将发送邮件的必要信息(发件人、收件人、标题、正文)封装到SimpleMailMessage对象中,可以根据规则设置发送人昵称等。

    发送多组件邮件(附件、复杂正文)
    ​ 发送简单邮件仅需要提供对应的4个基本信息就可以了,如果想发送复杂的邮件,需要更换邮件对象。使用MimeMessage可以发送特殊的邮件。

    发送网页正文邮件

    @Service
    public class SendMailServiceImpl2 implements SendMailService {
        @Autowired
        private JavaMailSender javaMailSender;
    
        //发送人
        private String from = "test@qq.com";
        //接收人
        private String to = "test@126.com";
        //标题
        private String subject = "测试邮件";
        //正文
        private String context = "点开有惊喜";
    
        public void sendMail() {
            try {
                MimeMessage message = javaMailSender.createMimeMessage();
                MimeMessageHelper helper = new MimeMessageHelper(message);
                helper.setFrom(to+"(小甜甜)");
                helper.setTo(from);
                helper.setSubject(subject);
                helper.setText(context,true);		//此处设置正文支持html解析
    
                javaMailSender.send(message);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
    

    发送带有附件的邮件

    @Service
    public class SendMailServiceImpl2 implements SendMailService {
        @Autowired
        private JavaMailSender javaMailSender;
    
        //发送人
        private String from = "test@qq.com";
        //接收人
        private String to = "test@126.com";
        //标题
        private String subject = "测试邮件";
        //正文
        private String context = "测试邮件正文";
    
        public void sendMail() {
            try {
                MimeMessage message = javaMailSender.createMimeMessage();
                MimeMessageHelper helper = new MimeMessageHelper(message,true);		//此处设置支持附件
                helper.setFrom(to+"(小甜甜)");
                helper.setTo(from);
                helper.setSubject(subject);
                helper.setText(context);
    
                //添加附件
                File f1 = new File("springboot_23_mail-0.0.1-SNAPSHOT.jar");
                File f2 = new File("resources\\logo.png");
    
                helper.addAttachment(f1.getName(),f1);
                helper.addAttachment("最靠谱的培训结构.png",f2);
    
                javaMailSender.send(message);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
    

    二、消息

    ​ springboot整合第三方技术最后一部分我们来说说消息中间件,首先先介绍一下消息的应用。
    Java处理消息的标准规范
    ​ 目前企业级开发中广泛使用的消息处理技术共三大类,具体如下:

    JMS
    AMQP
    MQTT
    

    ​ 为什么是三大类,而不是三个技术呢?因为这些都是规范,就想JDBC技术,是个规范,开发针对规范开发,运行还要靠实现类,例如MySQL提供了JDBC的实现,最终运行靠的还是实现。并且这三类规范都是针对异步消息进行处理的,也符合消息的设计本质,处理异步的业务。对以上三种消息规范做一下普及

    JMS
    ​ JMS(Java Message Service),这是一个规范,作用等同于JDBC规范,提供了与消息服务相关的API接口。

    JMS消息模型

    ​ JMS规范中规范了消息有两种模型。分别是点对点模型和发布订阅模型。

    **​ 点对点模型:**peer-2-peer,生产者会将消息发送到一个保存消息的容器中,通常使用队列模型,使用队列保存消息。一个队列的消息只能被一个消费者消费,或未被及时消费导致超时。这种模型下,生产者和消费者是一对一绑定的。

    **​ 发布订阅模型:**publish-subscribe,生产者将消息发送到一个保存消息的容器中,也是使用队列模型来保存。但是消息可以被多个消费者消费,生产者和消费者完全独立,相互不需要感知对方的存在。

    ​ 以上这种分类是从消息的生产和消费过程来进行区分,针对消息所包含的信息不同,还可以进行不同类别的划分。

    JMS消息种类

    ​ 根据消息中包含的数据种类划分,可以将消息划分成6种消息。

    TextMessage
    MapMessage
    BytesMessage
    StreamMessage
    ObjectMessage
    Message (只有消息头和属性)
    

    ​ JMS主张不同种类的消息,消费方式不同,可以根据使用需要选择不同种类的消息。但是这一点也成为其诟病之处,后面再说。整体上来说,JMS就是典型的保守派,什么都按照J2EE的规范来,做一套规范,定义若干个标准,每个标准下又提供一大批API。目前对JMS规范实现的消息中间件技术还是挺多的,毕竟是皇家御用,肯定有人舔,例如ActiveMQ、Redis、HornetMQ。但是也有一些不太规范的实现,参考JMS的标准设计,但是又不完全满足其规范,例如:RabbitMQ、RocketMQ。

    AMQP
    ​ JMS的问世为消息中间件提供了很强大的规范性支撑,但是使用的过程中就开始被人诟病,比如JMS设置的极其复杂的多种类消息处理机制。本来分门别类处理挺好的,为什么会被诟病呢?原因就在于JMS的设计是J2EE规范,站在Java开发的角度思考问题。但是现实往往是复杂度很高的。比如我有一个.NET开发的系统A,有一个Java开发的系统B,现在要从A系统给B系统发业务消息,结果两边数据格式不统一,没法操作。JMS不是可以统一数据格式吗?提供了6种数据种类,总有一款适合你啊。NO,一个都不能用。因为A系统的底层语言不是Java语言开发的,根本不支持那些对象。这就意味着如果想使用现有的业务系统A继续开发已经不可能了,必须推翻重新做使用Java语言开发的A系统。

    ​ 这时候有人就提出说,你搞那么复杂,整那么多种类干什么?找一种大家都支持的消息数据类型不就解决这个跨平台的问题了吗?大家一想,对啊,于是AMQP孕育而生。

    ​ 单从上面的说明中其实可以明确感知到,AMQP的出现解决的是消息传递时使用的消息种类的问题,化繁为简,但是其并没有完全推翻JMS的操作API,所以说AMQP仅仅是一种协议,规范了数据传输的格式而已。

    ​ AMQP(advanced message queuing protocol):一种协议(高级消息队列协议,也是消息代理规范),规范了网络交换的数据格式,兼容JMS操作。 优点

    ​ 具有跨平台性,服务器供应商,生产者,消费者可以使用不同的语言来实现

    JMS消息种类

    ​ AMQP消息种类:byte[]

    ​ AMQP在JMS的消息模型基础上又进行了进一步的扩展,除了点对点和发布订阅的模型,开发了几种全新的消息模型,适应各种各样的消息发送。

    AMQP消息模型

    direct exchange
    fanout exchange
    topic exchange
    headers exchange
    system exchange
    

    ​ 目前实现了AMQP协议的消息中间件技术也很多,而且都是较为流行的技术,例如:RabbitMQ、StormMQ、RocketMQ

    MQTT
    ​ MQTT(Message Queueing Telemetry Transport)消息队列遥测传输,专为小设备设计,是物联网(IOT)生态系统中主要成分之一。由于与JavaEE企业级开发没有交集,此处不作过多的说明。

    ​ 除了上述3种J2EE企业级应用中广泛使用的三种异步消息传递技术,还有一种技术也不能忽略,Kafka。

    KafKa
    ​ Kafka,一种高吞吐量的分布式发布订阅消息系统,提供实时消息功能。Kafka技术并不是作为消息中间件为主要功能的产品,但是其拥有发布订阅的工作模式,也可以充当消息中间件来使用,而且目前企业级开发中其身影也不少见。

    ​ 本节内容讲围绕着上述内容中的几种实现方案讲解springboot整合各种各样的消息中间件。由于各种消息中间件必须先安装再使用,下面的内容采用Windows系统安装,降低各位学习者的学习难度,基本套路和之前学习NoSQL解决方案一样,先安装再整合。

    三、购物订单发送手机短信案例

    ​ 为了便于下面演示各种各样的消息中间件技术,我们创建一个购物过程生成订单时为用户发送短信的案例环境,模拟使用消息中间件实现发送手机短信的过程。

    ​ 手机验证码案例需求如下:

    执行下单业务时(模拟此过程),调用消息服务,将要发送短信的订单id传递给消息中间件
    
    消息处理服务接收到要发送的订单id后输出订单id(模拟发短信)
    
    由于不涉及数据读写,仅开发业务层与表现层,其中短信处理的业务代码独立开发,代码如下:
    

    订单业务

    ​ 业务层接口

    public interface OrderService {
        void order(String id);
    }
    

    ​ 模拟传入订单id,执行下订单业务,参数为虚拟设定,实际应为订单对应的实体类

    ​ 业务层实现

    @Service
    public class OrderServiceImpl implements OrderService {
        @Autowired
        private MessageService messageService;
        
        @Override
        public void order(String id) {
            //一系列操作,包含各种服务调用,处理各种业务
            System.out.println("订单处理开始");
            //短信消息处理
            messageService.sendMessage(id);
            System.out.println("订单处理结束");
            System.out.println();
        }
    }
    

    ​ 业务层转调短信处理的服务MessageService

    ​ 表现层服务

    @RestController
    @RequestMapping("/orders")
    public class OrderController {
    
        @Autowired
        private OrderService orderService;
    
        @PostMapping("{id}")
        public void order(@PathVariable String id){
            orderService.order(id);
        }
    }
    

    ​ 表现层对外开发接口,传入订单id即可(模拟)

    短信处理业务

    ​ 业务层接口

    public interface MessageService {
        void sendMessage(String id);
        String doMessage();
    }
    

    ​ 短信处理业务层接口提供两个操作,发送要处理的订单id到消息中间件,另一个操作目前暂且设计成处理消息,实际消息的处理过程不应该是手动执行,应该是自动执行,到具体实现时再进行设计

    ​ 业务层实现

    @Service
    public class MessageServiceImpl implements MessageService {
        private ArrayList<String> msgList = new ArrayList<String>();
    
        @Override
        public void sendMessage(String id) {
            System.out.println("待发送短信的订单已纳入处理队列,id:"+id);
            msgList.add(id);
        }
    
        @Override
        public String doMessage() {
            String id = msgList.remove(0);
            System.out.println("已完成短信发送业务,id:"+id);
            return id;
        }
    }
    

    ​ 短信处理业务层实现中使用集合先模拟消息队列,观察效果

    ​ 表现层服务

    @RestController
    @RequestMapping("/msgs")
    public class MessageController {
    
        @Autowired
        private MessageService messageService;
    
        @GetMapping
        public String doMessage(){
            String id = messageService.doMessage();
            return id;
        }
    }
    

    ​ 短信处理表现层接口暂且开发出一个处理消息的入口,但是此业务是对应业务层中设计的模拟接口,实际业务不需要设计此接口。

    ​ 下面开启springboot整合各种各样的消息中间件,从严格满足JMS规范的ActiveMQ开始

    四、SpringBoot整合ActiveMQ

    ActiveMQ是MQ产品中的元老级产品,早期标准MQ产品之一,在AMQP协议没有出现之前,占据了消息中间件市场的绝大部分份额,后期因为AMQP系列产品的出现,迅速走弱,目前仅在一些线上运行的产品中出现,新产品开发较少采用。

    安装
    ​ windows版安装包下载地址:https://activemq.apache.org/components/classic/download/

    ​ 下载的安装包是解压缩就能使用的zip文件,解压缩完毕后会得到如下文件

    image20220228160001620

    启动服务器

    activemq.bat
    

    ​ 运行bin目录下的win32或win64目录下的activemq.bat命令即可,根据自己的操作系统选择即可,默认对外服务端口61616。

    访问web管理服务

    ​ ActiveMQ启动后会启动一个Web控制台服务,可以通过该服务管理ActiveMQ。

    http://127.0.0.1:8161/
    

    ​ web管理服务默认端口8161,访问后可以打开ActiveMQ的管理界面,如下:

    image-20220228160844972

    ​ 首先输入访问用户名和密码,初始化用户名和密码相同,均为:admin,成功登录后进入管理后台界面,如下:

    image20220228161010401

    ​ 看到上述界面视为启动ActiveMQ服务成功。

    启动失败
    其中占用的端口有:61616、5672、61613、1883、61614,如果启动失败,请先管理对应端口即可。以下就是某个端口占用的报错信息,可以从抛出异常的位置看出,启动5672端口时端口被占用,显示java.net.BindException: Address already in use: JVM_Bind。Windows系统中终止端口运行的操作参看【含命令行启动常见问题及解决方案的博客

    整合
    ​ 做了这么多springboot整合第三方技术,已经摸到门路了,加坐标,做配置,调接口,直接开工

    步骤①:导入springboot整合ActiveMQ的starter

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-activemq</artifactId>
    </dependency>
    

    步骤②:配置ActiveMQ的服务器地址

    spring:
      activemq:
        broker-url: tcp://localhost:61616
    

    步骤③:使用JmsMessagingTemplate操作ActiveMQ

    @Service
    public class MessageServiceActivemqImpl implements MessageService {
        @Autowired
        private JmsMessagingTemplate messagingTemplate;
    
        @Override
        public void sendMessage(String id) {
            System.out.println("待发送短信的订单已纳入处理队列,id:"+id);
            messagingTemplate.convertAndSend("order.queue.id",id);
        }
    
        @Override
        public String doMessage() {
            String id = messagingTemplate.receiveAndConvert("order.queue.id",String.class);
            System.out.println("已完成短信发送业务,id:"+id);
            return id;
        }
    }
    

    ​ 发送消息需要先将消息的类型转换成字符串,然后再发送,所以是convertAndSend,定义消息发送的位置,和具体的消息内容,此处使用id作为消息内容。

    ​ 接收消息需要先将消息接收到,然后再转换成指定的数据类型,所以是receiveAndConvert,接收消息除了提供读取的位置,还要给出转换后的数据的具体类型。

    步骤④:使用消息监听器在服务器启动后,监听指定位置,当消息出现后,立即消费消息

    @Component
    public class MessageListener {
        @JmsListener(destination = "order.queue.id")
        @SendTo("order.other.queue.id")
        public String receive(String id){
            System.out.println("已完成短信发送业务,id:"+id);
            return "new:"+id;
        }
    }
    

    ​ 使用注解@JmsListener定义当前方法监听ActiveMQ中指定名称的消息队列。

    ​ 如果当前消息队列处理完还需要继续向下传递当前消息到另一个队列中使用注解@SendTo即可,这样即可构造连续执行的顺序消息队列。

    步骤⑤:切换消息模型由点对点模型到发布订阅模型,修改jms配置即可

    spring:
      activemq:
        broker-url: tcp://localhost:61616
      jms:
        pub-sub-domain: true
    ​
    

    pub-sub-domain默认值为false,即点对点模型,修改为true后就是发布订阅模型。

    五、SpringBoot整合RabbitMQ

    ​ RabbitMQ是MQ产品中的目前较为流行的产品之一,它遵从AMQP协议。RabbitMQ的底层实现语言使用的是Erlang,所以安装RabbitMQ需要先安装Erlang。

    Erlang安装

    ​ windows版安装包下载地址:https://www.erlang.org/downloads

    ​ 下载完毕后得到exe安装文件,一键傻瓜式安装,安装完毕需要重启,需要重启,需要重启。

    ​ 安装的过程中可能会出现依赖Windows组件的提示,根据提示下载安装即可,都是自动执行的,如下:

    image20220228164851551

    ​ Erlang安装后需要配置环境变量,否则RabbitMQ将无法找到安装的Erlang。需要配置项如下,作用等同JDK配置环境变量的作用。

    ERLANG_HOME
    PATH
    

    安装
    ​ windows版安装包下载地址:https://rabbitmq.com/install-windows.html

    ​ 下载完毕后得到exe安装文件,一键傻瓜式安装,安装完毕后会得到如下文件

    image-20220228165151524

    启动服务器

    rabbitmq-service.bat start		# 启动服务
    rabbitmq-service.bat stop		# 停止服务
    rabbitmqctl status				# 查看服务状态
    

    ​ 运行sbin目录下的rabbitmq-service.bat命令即可,start参数表示启动,stop参数表示退出,默认对外服务端口5672。

    ​ 注意:启动rabbitmq的过程实际上是开启rabbitmq对应的系统服务,需要管理员权限方可执行。

    ​ 说明:有没有感觉5672的服务端口很熟悉?activemq与rabbitmq有一个端口冲突问题,学习阶段无论操作哪一个?请确保另一个处于关闭状态。

    ​ 说明:不喜欢命令行的小伙伴可以使用任务管理器中的服务页,找到RabbitMQ服务,使用鼠标右键菜单控制服务的启停。

    image-20220228170147193

    访问web管理服务

    ​ RabbitMQ也提供有web控制台服务,但是此功能是一个插件,需要先启用才可以使用。

    rabbitmq-plugins.bat list							# 查看当前所有插件的运行状态
    rabbitmq-plugins.bat enable rabbitmq_management		# 启动rabbitmq_management插件
    

    ​ 启动插件后可以在插件运行状态中查看是否运行,运行后通过浏览器即可打开服务后台管理界面

    http://localhost:15672
    

    ​ web管理服务默认端口15672,访问后可以打开RabbitMQ的管理界面,如下:

    image20220228170504793

    ​ 首先输入访问用户名和密码,初始化用户名和密码相同,均为:guest,成功登录后进入管理后台界面,如下:

    image20220228170535261

    整合(direct模型)
    ​ RabbitMQ满足AMQP协议,因此不同的消息模型对应的制作不同,先使用最简单的direct模型开发。

    步骤①:导入springboot整合amqp的starter,amqp协议默认实现为rabbitmq方案

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-amqp</artifactId>
    </dependency>
    

    步骤②:配置RabbitMQ的服务器地址

    spring:
      rabbitmq:
        host: localhost
        port: 5672
    

    步骤③:初始化直连模式系统设置

    ​ 由于RabbitMQ不同模型要使用不同的交换机,因此需要先初始化RabbitMQ相关的对象,例如队列,交换机等

    @Configuration
    public class RabbitConfigDirect {
        @Bean
        public Queue directQueue(){
            return new Queue("direct_queue");
        }
        @Bean
        public Queue directQueue2(){
            return new Queue("direct_queue2");
        }
        @Bean
        public DirectExchange directExchange(){
            return new DirectExchange("directExchange");
        }
        @Bean
        public Binding bindingDirect(){
            return BindingBuilder.bind(directQueue()).to(directExchange()).with("direct");
        }
        @Bean
        public Binding bindingDirect2(){
            return BindingBuilder.bind(directQueue2()).to(directExchange()).with("direct2");
        }
    }
    

    ​ 队列Queue与直连交换机DirectExchange创建后,还需要绑定他们之间的关系Binding,这样就可以通过交换机操作对应队列。

    步骤④:使用AmqpTemplate操作RabbitMQ

    @Service
    public class MessageServiceRabbitmqDirectImpl implements MessageService {
        @Autowired
        private AmqpTemplate amqpTemplate;
    
        @Override
        public void sendMessage(String id) {
            System.out.println("待发送短信的订单已纳入处理队列(rabbitmq direct),id:"+id);
            amqpTemplate.convertAndSend("directExchange","direct",id);
        }
    }
    

    ​ amqp协议中的操作API接口名称看上去和jms规范的操作API接口很相似,但是传递参数差异很大。

    步骤⑤:使用消息监听器在服务器启动后,监听指定位置,当消息出现后,立即消费消息

    @Component
    public class MessageListener {
        @RabbitListener(queues = "direct_queue")
        public void receive(String id){
            System.out.println("已完成短信发送业务(rabbitmq direct),id:"+id);
        }
    }
    

    ​ 使用注解@RabbitListener定义当前方法监听RabbitMQ中指定名称的消息队列。

    整合(topic模型)
    步骤①:同上
    步骤②:同上
    步骤③:初始化主题模式系统设置

    @Configuration
    public class RabbitConfigTopic {
        @Bean
        public Queue topicQueue(){
            return new Queue("topic_queue");
        }
        @Bean
        public Queue topicQueue2(){
            return new Queue("topic_queue2");
        }
        @Bean
        public TopicExchange topicExchange(){
            return new TopicExchange("topicExchange");
        }
        @Bean
        public Binding bindingTopic(){
            return BindingBuilder.bind(topicQueue()).to(topicExchange()).with("topic.*.id");
        }
        @Bean
        public Binding bindingTopic2(){
            return BindingBuilder.bind(topicQueue2()).to(topicExchange()).with("topic.orders.*");
        }
    }
    

    ​ 主题模式支持routingKey匹配模式,*表示匹配一个单词,#表示匹配任意内容,这样就可以通过主题交换机将消息分发到不同的队列中,详细内容请参看RabbitMQ系列课程。

    匹配键					**topic.*.***	      		topic.#
    topic.order.id			true						true
    order.topic.id			false						false
    topic.sm.order.id		false						true
    topic.sm.id				false						true
    topic.id.order			true						true
    topic.id				false						true
    topic.order				false						true
    

    步骤④:使用AmqpTemplate操作RabbitMQ

    @Service
    public class MessageServiceRabbitmqTopicImpl implements MessageService {
        @Autowired
        private AmqpTemplate amqpTemplate;
    
        @Override
        public void sendMessage(String id) {
            System.out.println("待发送短信的订单已纳入处理队列(rabbitmq topic),id:"+id);
            amqpTemplate.convertAndSend("topicExchange","topic.orders.id",id);
        }
    }
    

    ​ 发送消息后,根据当前提供的routingKey与绑定交换机时设定的routingKey进行匹配,规则匹配成功消息才会进入到对应的队列中。

    步骤⑤:使用消息监听器在服务器启动后,监听指定队列

    @Component
    public class MessageListener {
        @RabbitListener(queues = "topic_queue")
        public void receive(String id){
            System.out.println("已完成短信发送业务(rabbitmq topic 1),id:"+id);
        }
        @RabbitListener(queues = "topic_queue2")
        public void receive2(String id){
            System.out.println("已完成短信发送业务(rabbitmq topic 22222222),id:"+id);
        }
    }
    

    ​ 使用注解@RabbitListener定义当前方法监听RabbitMQ中指定名称的消息队列。

    六、SpringBoot整合RocketMQ

    RocketMQ由阿里研发,后捐赠给apache基金会,目前是apache基金会顶级项目之一,也是目前市面上的MQ产品中较为流行的产品之一,它遵从AMQP协议。

    安装
    ​ windows版安装包下载地址:https://rocketmq.apache.org/

    ​ 下载完毕后得到zip压缩文件,解压缩即可使用,解压后得到如下文件

    image20220228174453471

    ​ RocketMQ安装后需要配置环境变量,具体如下:

    ROCKETMQ_HOME
    PATH
    NAMESRV_ADDR (建议): 127.0.0.1:9876
    

    ​ 关于NAMESRV_ADDR对于初学者来说建议配置此项,也可以通过命令设置对应值,操作略显繁琐,建议配置。系统学习RocketMQ知识后即可灵活控制该项。

    RocketMQ工作模式

    ​ 在RocketMQ中,处理业务的服务器称为broker,生产者与消费者不是直接与broker联系的,而是通过命名服务器进行通信。broker启动后会通知命名服务器自己已经上线,这样命名服务器中就保存有所有的broker信息。当生产者与消费者需要连接broker时,通过命名服务器找到对应的处理业务的broker,因此命名服务器在整套结构中起到一个信息中心的作用。并且broker启动前必须保障命名服务器先启动。

    image-20220228175123790

    启动服务器

    mqnamesrv		# 启动命名服务器
    mqbroker		# 启动broker
    

    ​ 运行bin目录下的mqnamesrv命令即可启动命名服务器,默认对外服务端口9876。

    ​ 运行bin目录下的mqbroker命令即可启动broker服务器,如果环境变量中没有设置NAMESRV_ADDR则需要在运行mqbroker指令前通过set指令设置NAMESRV_ADDR的值,并且每次开启均需要设置此项。

    测试服务器启动状态

    ​ RocketMQ提供有一套测试服务器功能的测试程序,运行bin目录下的tools命令即可使用。

    tools org.apache.rocketmq.example.quickstart.Producer		# 生产消息
    tools org.apache.rocketmq.example.quickstart.Consumer		# 消费消息
    

    整合(异步消息)
    步骤①:导入springboot整合RocketMQ的starter,此坐标不由springboot维护版本

    <dependency>
        <groupId>org.apache.rocketmq</groupId>
        <artifactId>rocketmq-spring-boot-starter</artifactId>
        <version>2.2.1</version>
    </dependency>
    

    步骤②:配置RocketMQ的服务器地址

    rocketmq:
      name-server: localhost:9876
      producer:
        group: group_rocketmq
    

    ​ 设置默认的生产者消费者所属组group。

    步骤③:使用RocketMQTemplate操作RocketMQ

    @Service
    public class MessageServiceRocketmqImpl implements MessageService {
        @Autowired
        private RocketMQTemplate rocketMQTemplate;
    
        @Override
        public void sendMessage(String id) {
            System.out.println("待发送短信的订单已纳入处理队列(rocketmq),id:"+id);
            SendCallback callback = new SendCallback() {
                @Override
                public void onSuccess(SendResult sendResult) {
                    System.out.println("消息发送成功");
                }
                @Override
                public void onException(Throwable e) {
                    System.out.println("消息发送失败!!!!!");
                }
            };
            rocketMQTemplate.asyncSend("order_id",id,callback);
        }
    }
    

    ​ 使用asyncSend方法发送异步消息。

    步骤④:使用消息监听器在服务器启动后,监听指定位置,当消息出现后,立即消费消息

    @Component
    @RocketMQMessageListener(topic = "order_id",consumerGroup = "group_rocketmq")
    public class MessageListener implements RocketMQListener<String> {
        @Override
        public void onMessage(String id) {
            System.out.println("已完成短信发送业务(rocketmq),id:"+id);
        }
    }
    

    ​ RocketMQ的监听器必须按照标准格式开发,实现RocketMQListener接口,泛型为消息类型。

    ​ 使用注解@RocketMQMessageListener定义当前类监听RabbitMQ中指定组、指定名称的消息队列。

    七、SpringBoot整合Kafka

    安装
    ​ windows版安装包下载地址:https://kafka.apache.org/downloads

    ​ 下载完毕后得到tgz压缩文件,使用解压缩软件解压缩即可使用,解压后得到如下文件

    image20220228181442155

    ​ 建议使用windows版2.8.1版本。

    启动服务器

    ​ kafka服务器的功能相当于RocketMQ中的broker,kafka运行还需要一个类似于命名服务器的服务。在kafka安装目录中自带一个类似于命名服务器的工具,叫做zookeeper,它的作用是注册中心,相关知识请到对应课程中学习。

    zookeeper-server-start.bat ..\..\config\zookeeper.properties		# 启动zookeeper
    kafka-server-start.bat ..\..\config\server.properties				# 启动kafka
    

    ​ 运行bin目录下的windows目录下的zookeeper-server-start命令即可启动注册中心,默认对外服务端口2181。

    ​ 运行bin目录下的windows目录下的kafka-server-start命令即可启动kafka服务器,默认对外服务端口9092。

    创建主题

    ​ 和之前操作其他MQ产品相似,kakfa也是基于主题操作,操作之前需要先初始化topic。

    # 创建topic
    kafka-topics.bat --create --zookeeper localhost:2181 --replication-factor 1 --partitions 1 --topic itheima
    # 查询topic
    kafka-topics.bat --zookeeper 127.0.0.1:2181 --list					
    # 删除topic
    kafka-topics.bat --delete --zookeeper localhost:2181 --topic itheima
    

    测试服务器启动状态

    ​ Kafka提供有一套测试服务器功能的测试程序,运行bin目录下的windows目录下的命令即可使用。

    kafka-console-producer.bat --broker-list localhost:9092 --topic itheima							# 测试生产消息
    kafka-console-consumer.bat --bootstrap-server localhost:9092 --topic itheima --from-beginning	# 测试消息消费
    

    整合
    步骤①:导入springboot整合Kafka的starter,此坐标由springboot维护版本

    <dependency>
        <groupId>org.springframework.kafka</groupId>
        <artifactId>spring-kafka</artifactId>
    </dependency>
    

    步骤②:配置Kafka的服务器地址

    spring:
      kafka:
        bootstrap-servers: localhost:9092
        consumer:
          group-id: order
    

    ​ 设置默认的生产者消费者所属组id。

    步骤③:使用KafkaTemplate操作Kafka

    @Service
    public class MessageServiceKafkaImpl implements MessageService {
        @Autowired
        private KafkaTemplate<String,String> kafkaTemplate;
    
        @Override
        public void sendMessage(String id) {
            System.out.println("待发送短信的订单已纳入处理队列(kafka),id:"+id);
            kafkaTemplate.send("itheima2022",id);
        }
    }
    

    ​ 使用send方法发送消息,需要传入topic名称。

    步骤④:使用消息监听器在服务器启动后,监听指定位置,当消息出现后,立即消费消息

    @Component
    public class MessageListener {
        @KafkaListener(topics = "itheima2022")
        public void onMessage(ConsumerRecord<String,String> record){
            System.out.println("已完成短信发送业务(kafka),id:"+record.value());
        }
    }
    

    ​ 使用注解@KafkaListener定义当前方法监听Kafka中指定topic的消息,接收到的消息封装在对象ConsumerRecord中,获取数据从ConsumerRecord对象中获取即可。

  • 相关阅读:
    平台化设计产品存在的问题
    体系结构26_输入输出系统(3)
    37.轮播图
    STM32中断与事件的理解
    构建高效实时数据流水线:Flink、Kafka 和 CnosDB 的完美组合
    警告:新版Outlook会向微软发送密码、邮件和其他数据
    售前是什么一
    大数据-玩转数据-Python几种数据采集
    C++ 小游戏 视频及资料集(9)
    前端基础向~从项目出手封装工具函数
  • 原文地址:https://blog.csdn.net/Du_XiaoNan/article/details/139339799