从零学 spring cloud第5-1课:中间件之消息队列ActiveMQ

    项目的DEMO代码:https://github.com/heyu52/-spring-cloud
消息队列中间件当下系统中无处不在,并且已经成为了系统中内部通信的核心手段。它有以下特点:

  • 松耦合
  • 异步消息
  • 流量削峰
  • 可靠投递
  • 广播
  • 流量控制
  • 最终一致性
    我使用最多的场景就是异步处理。
        目前在业界使用比较多的消息队列有ActiveMQ、RabbitMQ、ZeroMQ、Kafka、MetaMQ、RocketMQ。这些消息队列都是现成的系统,我们安装即可使用。这一节我们就来讲讲ActiveMQ是如何使用的。

什么是JMS规范
    JMS即Java消息服务(Java Message Service)应用程序接口,是一个Java平台中关于面向消息中间件(MOM)的API,用于在两个应用程序之间,或分布式系统中发送消息,进行异步通信。Java消息服务是一个与具体平台无关的API,绝大多数MOM提供商都对JMS提供支持。
    JMS 的消息机制有 2 种模型,一种是 Point to Point 即点对点(一对一),表现为队列的形式,发送的消息,只能被一个接收者取走;另一种是 Topic即发布订阅模式(一对多),可以被多个订阅者订阅,类似于群发。

ActiveMQ 介绍
    ActiveMQ是Apache软件基金会所研发的开放源代码消息中间件;由于ActiveMQ是一个纯Java程序,因此只需要操作系统支持Java虚拟机,ActiveMQ便可执行。它支持Java,C,C++,C#,Python,Ruby,Perl。同时支持windows,linux。支持OpenWire、REST、STOMP、WS-Notification、MQTT、XMPP以及AMQP协议。我们之前所有的例子都是使用RESTFUL。它的官网地址如下:https://activemq.apache.org/

ActiveMQ 安装
目前ActiveMQ 的最新版本是5.5.9,下载地址为:https://activemq.apache.org/components/classic/download/
本打算使用Docker来进行部署的,但是官网上找不到官方的镜像,所以直接部署。我们直接下载linux的版本进行部署就可以了。
解压:tar -zxvf apache-activemq-5.15.9-bin.tar.gz
进入目录:cd bin
启动:./activemq start
在这里插入图片描述
看到以上就表示,ActiveMQ已启动了。
开启防火墙端口
1、如果使用了云服务器需要先开启8161(web管理页面端口)、61616(activemq服务监控端口) 两个端口
2、打开linux防火墙端口
/sbin/iptables -I INPUT -p tcp --dport 8161 -j ACCEPT&&/etc/init.d/iptables save&&service iptables restart&&/etc/init.d/iptables status
/sbin/iptables -I INPUT -p tcp --dport 61616 -j ACCEPT&&/etc/init.d/iptables save&&service iptables restart&&/etc/init.d/iptables status

后台管理:http://192.168.0.123:8161/
在这里插入图片描述
点击Manage ActiveMQ broker,会弹出登录,默认账号密码:admin/admin
在这里插入图片描述
ActiveMQ 是起来了,但有一个很现实的问题,它都现在都是存放在内存中,它并没有连接数据库,企业中没有人这么用的,真正用起来,必然是将消息都存到数据库中,要不然消息在系统故障时,就会产生丢失了。接下来,我们配置它与数据库相连。

1、下载数据库驱动包
    因为我的mysql是8.0的版本,所以我下载了Mysql8.0的mysql-connector-java-8.0.16.jar包,我们将这个包复制到activemq的\lib\目录下。

2、下载 commons-dbcp2-2.1.1.jar,下载地址为:https://mvnrepository.com/artifact/org.apache.commons/commons-dbcp2/2.1.1
在这里插入图片描述
如果你的mysql是5.0的版本,那这个就不用,也同样复制到activemq的\lib\目录下。

3、修改activemq.xml
打开目录conf,找到activemq.xml进行编辑,修改persistenceAdapter

        <persistenceAdapter>
             <!--  <kahaDB directory="${activemq.data}/kahadb"/> -->
			<jdbcPersistenceAdapter  dataSource="#mysql-ds"/>
        </persistenceAdapter>

增加bean

 <!-- 用于持久化数据到Mysql数据库 -->
	       <bean id="mysql-ds" class="org.apache.commons.dbcp2.BasicDataSource" destroy-method="close">
                <property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://192.168.0.148:3306/activemq?serverTimezone=UTC&amp;relaxAutoCommit=true"/>
                <property name="username" value="root"/>
                <property name="password" value="csdn"/>
                <property name="poolPreparedStatements" value="true"/>
        </bean>

我们还要注意新增这个代码的位置,如下图
在这里插入图片描述
4、验证
为了验证启动是否有错,我们使用控制台的方式进行启动,打开终端,在bin 目录下执行

./activemq console

在这里插入图片描述
如果有报错,这里会有显示,再解决报错就好了。我们再查看数据库
在这里插入图片描述
数据库中自动新增了三个表。
我们再打开http://192.168.0.123:8161 ,正常显示。表明数据库方式已成功。

SpringBoot连接ActiveMQ
我们新建一个项目,选择webstart及activemq 5
在这里插入图片描述
项目创建后,注意一下pom文件,自动增加了

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

创建队列与配置

package com.csdn.demo.Config;

import org.apache.activemq.command.ActiveMQQueue;
import org.apache.activemq.command.ActiveMQTopic;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.jms.annotation.EnableJms;
import org.springframework.jms.config.DefaultJmsListenerContainerFactory;
import org.springframework.jms.config.JmsListenerContainerFactory;

import javax.jms.ConnectionFactory;
import javax.jms.Queue;
import javax.jms.Topic;

@Configuration
public class MqConfig {

     @Bean
    ConnectionFactory connectionFactory() {
        ActiveMQConnectionFactory connectionFactory = new ActiveMQConnectionFactory();
        connectionFactory.setBrokerURL("tcp://192.168.0.123:61616");
        connectionFactory.setUserName("admin");
        connectionFactory.setPassword("admin");
        return connectionFactory;
    }

    @Bean
    public Queue queue()
    {
        return  new ActiveMQQueue("csdn.queue");
    }
}

消息生产者

package com.csdn.demo.MQProducer;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jms.core.JmsMessagingTemplate;
import org.springframework.stereotype.Component;
import javax.jms.Queue;
import javax.jms.Topic;

@Component
//消息的生产者
public class Producer {
    //Spring 提供发送消息的工具类
    @Autowired
    private JmsMessagingTemplate jmsMessagingTemplate;

    @Autowired
    private Queue query;

    public void sendQueue(String msg){
        System.out.println("send queue msg:"+msg);
        this.jmsMessagingTemplate.convertAndSend(this.query,msg);
    }
}

消息消费者

package com.csdn.demo.MQConsumer;

import org.springframework.jms.annotation.JmsListener;
import org.springframework.stereotype.Component;

@Component
public class Consumer {
    @JmsListener(destination = "csdn.queue")
    public void receiveQueue(String text) {
        System.out.println("Consumer queue msg : "+text);
    }
}

创建测试Control

package com.csdn.demo.web;

import com.csdn.demo.MQProducer.Producer;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class myControl {

    @Autowired
    private Producer producer;
    @RequestMapping("/MySendMethod")
    public void MySendMethod() {
        this.producer.sendQueue("MySendMethod queue message");
    }
}

运行测试
编译运行项目后浏览器打开地址:http://127.0.0.1:8080/MySendMethod
在这里插入图片描述
查看ActiveMQ后台,同时会看到一个队列在这里插入图片描述
接下来,我们注释消费者

@Component
public class Consumer {
  //  @JmsListener(destination = "csdn.queue")
    public void receiveQueue(String text) {
        System.out.println("Consumer queue msg : "+text);
    }
}

编译运行项目后浏览器打开地址:http://127.0.0.1:8080/MySendMethod
打开数据库查看数据:
在这里插入图片描述
这里可以看到一条未处理的消息,同时后台管理界面也能看到
在这里插入图片描述
我们不再注释代码,重新运行项目,然后刷新数据库
在这里插入图片描述
同时看后台数据
在这里插入图片描述
消息已被处理了。

广播消息
我们来增加一个发广播消息的配置

    @Bean
    public Topic topic() {
        return new ActiveMQTopic("csdn.queue");
    }

增加一个生产者

 @Autowired
    private Topic topic;

    public void sendTopic(String msg) {
        System.out.println("发送广播消息 :"+msg);
        this.jmsMessagingTemplate.convertAndSend(this.topic, msg);
    }

处理广播消息


    @JmsListener(destination = "csdn.topic")
    public void receiveTopicB(String text) {
        System.out.println("处理广播消息A : "+text);
    }

    @JmsListener(destination = "csdn.topic")
    public void receiveTopicC(String text) {
        System.out.println("处理广播消息B : "+text);
    }

增加Controller方法


    @RequestMapping("/MySendTopicMethod")
    public void MySendTopicMethod() {
        this.producer.sendTopic("MySendTopicMethod queue message");
    }

运行项目,浏览器输入:http://127.0.0.1:8080/MySendTopicMethod
很不幸你只会发现,有发送,没接收
在这里插入图片描述
后台也没有待处理的消息
在这里插入图片描述
消息明显,丢失了。原因是Spring Boot 集成 ActiveMQ 的项目默认只支持队列或者广播中的一种,通过配置项 spring.jms.pub-subdomain 的值来控制,true 为广播模式,false 为队列模式,默认情况下支持队列模式。
我们增加配置

spring.jms.pub-sub-domain=true

运行项目测试
在这里插入图片描述
广播消息处理成功了。我们现在测试队列消息:http://127.0.0.1:8080/MySendMethod
在这里插入图片描述
此时,队列不处理了。

同时支持队列和广播
为了解决同时能处理两类消息,我们要增加配置

package com.neo.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.jms.annotation.EnableJms;
import org.springframework.jms.config.DefaultJmsListenerContainerFactory;
import org.springframework.jms.config.JmsListenerContainerFactory;

import javax.jms.ConnectionFactory;

@Configuration
@EnableJms
public class ActiveMQConfig {

    @Bean("queueListenerFactory")
    public JmsListenerContainerFactory<?> queueListenerFactory(ConnectionFactory connectionFactory) {
        DefaultJmsListenerContainerFactory factory = new DefaultJmsListenerContainerFactory();
        factory.setConnectionFactory(connectionFactory);
        factory.setPubSubDomain(false);
        return factory;
    }
 
    @Bean("topicListenerFactory")
    public JmsListenerContainerFactory<?> topicListenerFactory(ConnectionFactory connectionFactory) {
        DefaultJmsListenerContainerFactory factory = new DefaultJmsListenerContainerFactory();
        factory.setConnectionFactory(connectionFactory);
        factory.setPubSubDomain(true);
        return factory;
    }
}

我们在处理消息时

  @JmsListener(destination = "csdn.queue", containerFactory = "queueListenerFactory")
    public void receiveQueueA(String text) {
        System.out.println("处理队列消息A : "+text);
    }

    @JmsListener(destination = "csdn.topic", containerFactory = "topicListenerFactory")
    public void receiveTopicD(String text) {
        System.out.println("处理广播消息D : "+text);
    }

运行项目
在这里插入图片描述
此时会发现,之前的队列消息,也一并被处理掉了。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值