springboot入门之九 消息异步通信RabbitMq ActiveMq

1 篇文章 0 订阅
1 篇文章 0 订阅

1 RabbitMq

1.1 rabbitmq简介

RabbitMQ于2007年发布,是一个在AMQP(高级消息队列协议)基础上完成的,可复用的企业消息系统,是当前最主流的消息中间件之一。
特点

RabbitMQ是使用Erlang语言开发的开源消息队列系统,基于AMQP协议来实现。
AMQP的主要特征是面向消息、队列、路由(包括点对点和发布/订阅)、可靠性、安全。
AMQP协议更多用在企业系统内,对数据一致性、稳定性和可靠性要求很高的场景,对性能和吞吐量的要求还在其次。
RabbitMQ的可靠性是非常好的,数据能够保证百分之百的不丢失。可以使用镜像队列,它的稳定性非常好。所以说在我们互联网的金融行业。对数据的稳定性和可靠性要求都非常高的情况下,我们都会选择RabbitMQ。当然没有kafka性能好,但是要比AvtiveMQ性能要好很多。也可以自己做一些性能的优化。
RabbitMQ可以构建异地双活架构,包括每一个节点存储方式可以采用磁盘或者内存的方式。

RabbitMQ的集群架构

在这里插入图片描述

图中说的就是,我们可以采用三个节点作为RabbitMQ的一组集群,当然可以有许多组。节点与节点之间采用mirror queue。基于这种方式,能够保证数据百分之百的不丢失。
前端可以去做负载均衡,比如负载均衡组件:HA-proxy ,进行TCP级别的负载。
如果想做一个高可用的话,就需要借助keepAlived做一个高可用的配置。
比如前端加一个虚拟的VIP,通过VIP路由到指定的负载均衡组件,再有它路由到RabbtMQ的某一个节点。
这就是整个RabbitMQ集群架构。
能够实现非常完善,高可用并且性能也十分好,稳定性超强。并且有各种集群恢复手段。

RabbitMQ是由Erlang语言编写的实现了高级消息队列协议(AMQP)的开源消息代理软件(也可称为 面向消息的中间件)。支持Windows、Linux/Unix、MAC OS X操作系统和包括JAVA在内的多种编程语言。

AMQP,即Advanced Message Queuing Protocol,一个提供统一消息服务的应用层标准高级消息队列协议,是应用层协议的一个开放标准,为面向消息的中间件设计。基于此协议的客户端与消息中间件可传递消息,并不受 客户端/中间件 不同产品,不同的开发语言等条件的限制
使用rabbitmq主要三种分发模式

1.1.1 工作队列模式(Work Queue)

避免立即做一个资源密集型任务,必须等待它完成,而是把这个任务安排到稍后再做。我们将任务封装为消息并将其发送给队列。后台运行的工作进程将弹出任务并最终执行作业。当有多个worker同时运行时,任务将在它们之间共享。

image.png

1.1.2 分发模式(Fanout Exchange)

一个生产者,多个消费者,每一个消费者都有自己的一个队列,生产者没有将消息直接发送到队列,而是发送到了交换机,每个队列绑定交换机,生产者发送的消息经过交换机,到达队列,实现一个消息被多个消费者获取的目的。需要注意的是,如果将消息发送到一个没有队列绑定的exchange上面,那么该消息将会丢失,这是因为在rabbitMQ中exchange不具备存储消息的能力,只有队列具备存储消息的能力。

在这里插入图片描述

image.png
1.1.3 通配符模式(Topic Exchange)

这种模式添加了一个路由键,生产者发布消息的时候添加路由键,消费者绑定队列到交换机时添加键值,这样就可以接收到需要接收的消息。
符号“#”匹配一个或多个词,符号“*”匹配不多不少一个词

在这里插入图片描述在这里插入图片描述

1.2 rabbitmq安装

此处不展开

1.3 springboot整合

1.3.1 添加pom依赖。

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

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

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

    </dependencies>

在yml配置rabbitmq地址

1.3.2 rabbitmq配置文件

spring:
  rabbitmq :
    addresses : 192.168.0.5:5672
    username : admin
    password : 123456
    virtual-host : /
    connection-timeout : 15000
    #发送确认 对应RabbitTemplate.ConfirmCallback接口
    publisher-confirms: true
    #发送失败回退,对应RabbitTemplate.ReturnCallback接口
    publisher-returns: true
    listener.simple.acknowledge-mode : manual
    listener.simple.concurrency : 5
    listener.simple.max-concurrency : 10
    

一、普通工作队列模式

新建RabbitConf文件,用于配置我们rabbitmq相关的资源
代码如下

@Configuration
public class RabbitConf {

    // =================== 普通工作队列模式 模式 若不先声明, 必须生成者先发消息,否则消费者启动时会报错====================
    @Bean
    public Queue helloQueue() {
        return new Queue("hello");
    }

    @Bean
    public Queue testQueue() {
        return new Queue("test1");
    } 
}

定义了名为hello test的队列。然后我们创建生产者RabbitSender

@Component
public class RabbitSender {
    Logger log = LoggerFactory.getLogger(RabbitSender.class);

    // 自动注入RabbitTemplate模板类
    @Autowired
    private RabbitTemplate rabbitTemplate;

    public void send(String queue, Object message) {
        // 消息发送失败返回到队列中, yml需要配置 publisher-returns: true
        rabbitTemplate.setMandatory(true);

        // rabbitTemplate.isConfirmListener();
        // rabbitTemplate.setConfirmCallback(confirmCallback);
        // rabbitTemplate.setReturnCallback(returnCallback);
        System.out.println("Sender : " + message);
        CorrelationData correlationData = new CorrelationData(UUID.randomUUID().toString());
        rabbitTemplate.convertAndSend(queue, message, correlationData);
    }

    public void sendByExchange(String exchange, String queue, Object message) {

        System.out.println("Sender : " + message);
        CorrelationData correlationData = new CorrelationData(UUID.randomUUID().toString());
        rabbitTemplate.convertAndSend(exchange, queue, message, correlationData);
    }

    // 发送消息方法调用: 构建Message消息
    public void send(String queue, Object message, Map<String, Object> properties) throws Exception {

        MessageHeaders mhs = new MessageHeaders(properties);
        Message msg = MessageBuilder.createMessage(message, mhs);
        // id + 时间戳 全局唯一 用于ack保证唯一一条消息,这边做测试写死一个。但是在做补偿策略的时候,必须保证这是全局唯一的消息
        CorrelationData correlationData = new CorrelationData("1234567890");
        rabbitTemplate.convertAndSend(queue, msg, correlationData);
        System.out.println("发送完成");
    }

    // 回调函数: confirm确认
    final ConfirmCallback confirmCallback = new RabbitTemplate.ConfirmCallback() {
        @Override
        public void confirm(CorrelationData correlationData, boolean ack, String cause) {
            System.err.println("correlationData: " + correlationData);
            System.err.println("ack: " + ack);
            if (!ack) {
                // 可以进行日志记录、异常处理、补偿处理等
                System.err.println("异常处理....");
            } else {
                // 更新数据库,可靠性投递机制
                System.err.println("发送confirm....");
            }
        }
    };

    // 回调函数: return返回
    final ReturnCallback returnCallback = new RabbitTemplate.ReturnCallback() {
        @Override
        public void returnedMessage(org.springframework.amqp.core.Message message, int replyCode, String replyText,
            String exchange, String routingKey) {
            System.err.println("return exchange: " + exchange + ", routingKey: " + routingKey + ", replyCode: "
                + replyCode + ", replyText: " + replyText);
        }
    };

}

这里注入一个AmqpTemplate来发布消息
接下来我们需要配置一下消费者。
创建RabbitConsumer

@Component
public class RabbitConsumer {
    @Autowired
    private MailService mailService;
   
    // #################普通工作模式########################

    @RabbitListener(queues = "hello")
    @RabbitHandler
    public void readHelloQueue(Message message, Channel channel) throws IOException {
        // 采用手动应答模式, 手动确认应答更为安全稳定
        channel.basicAck(message.getMessageProperties().getDeliveryTag(), true);
        System.out.println("hello receive: " + new String(message.getBody()));
    }

    // #################异步发邮件实例########################
    @RabbitListener(queues = "test1")
    @RabbitHandler
    public void readMailQueue(String content) {
        String[] mailTo = {"407737980@qq.com"};

        // 发送html格式正文的邮件 整合thymeleaf模板
        Map<String, Object> variableMap = new HashMap<String, Object>();

        variableMap.put("subject", "hello ");
        variableMap.put("content", "hello !This is a test! ");

        String result = mailService.sendMsgByThymeleafTemplate(
            new MailEntity("hello freemark", "hello !This is a test! ", mailTo, variableMap));
        System.out.println("Receiver  message, send mail ");
    }
}

每一个注解的作用代码里面的注释说的很详细了我就不重复说了。
然后我们来测试,
首先在生产者工程新建一个测试类,用于生产消息。
代码如下


@RunWith(SpringRunner.class)
@SpringBootTest
public class SpringbootTeach10ApplicationTests {

    @Autowired
    private RabbitSender rabbitSender;
   

   

    /**
     * 普通工作 模式测试
     */
    @Test
    public void queueSend() {
        Date date = new Date();
        String dateString = new SimpleDateFormat("YYYY-mm-DD hh:MM:ss").format(date);
        System.out.println("[simple] send msg:" + dateString);
        // 注意 第一个参数是队列名称 第二个是消息内容
        rabbitSender.send("hello", dateString);
    }
 
   
}

首先启动测试类。

2020-03-27 15:53:47.170  INFO 73440 --- [           main] s.s.t.SpringbootTeach10ApplicationTests  : Started SpringbootTeach10ApplicationTests in 4.477 seconds (JVM running for 5.56)
[simple] send msg:2020-53-87 03:03:47
Sender : 2020-53-87 03:03:47
hello receive: 2020-53-87 03:03:47

消费者成功消费消息。

二、fanout模式

fanout属于广播模式,只要跟它绑定的队列都会通知并且接受到消息。
我们同理在RabbitConf中配置一下fanout模式的队列跟交换机。

//=================== fanout 模式  ====================

    @Bean
    public Queue fanoutA() {
        return new Queue("fanout.a");
    }

    @Bean
    public Queue fanoutB() {
        return new Queue("fanout.b");
    }

    @Bean
    public Queue fanoutC() {
        return new Queue("fanout.c");
    }

    /**
     * 定义个fanout交换器
     * @return
     */
    @Bean
    FanoutExchange fanoutExchange() {
        // 定义一个名为fanoutExchange的fanout交换器
        return new FanoutExchange("fanoutExchange");
    }

    /**
     * 将定义的fanoutA队列与fanoutExchange交换机绑定
     * @return
     */
    @Bean
    public Binding bindingExchangeWithA() {
        return BindingBuilder.bind(fanoutA()).to(fanoutExchange());
    }

    /**
     * 将定义的fanoutB队列与fanoutExchange交换机绑定
     * @return
     */
    @Bean
    public Binding bindingExchangeWithB() {
        return BindingBuilder.bind(fanoutB()).to(fanoutExchange());
    }

    /**
     * 将定义的fanoutC队列与fanoutExchange交换机绑定
     * @return
     */
    @Bean
    public Binding bindingExchangeWithC() {
        return BindingBuilder.bind(fanoutC()).to(fanoutExchange());
    }

在代码中我们配置了三个队列名、一个fanout交换机,并且将这三个队列绑定到了fanout交换器上。只要我们往这个交换机生产新的消息,那么这三个队列都会收到。

同理我们需要在RabbitConsumer类中增加消费方法
代码分别如下

   // #################fanout模式########################

    @RabbitListener(queues = "fanout.a")
    @RabbitHandler
    public void readFanoutAQueue(String content) {
        System.out.println("fanout.a Receiver  : " + content);
    }

    @RabbitListener(queues = "fanout.b")
    @RabbitHandler
    public void readFanoutBQueue(String content) {
        System.out.println("fanout.b Receiver  : " + content);
    }

    @RabbitListener(queues = "fanout.c")
    @RabbitHandler
    public void readFanoutCQueue(String content) {
        System.out.println("fanout.c Receiver  : " + content);
    }

然后编写一个名为testFanout()的方法启动我们的fanout生产方法,

    /**
     * fanout 模式测试
     */
    @Test
    public void fanoutSend() {
        Date date = new Date();
        String dateString = new SimpleDateFormat("YYYY-mm-DD hh:MM:ss").format(date);
        System.out.println("[fanout] send msg:" + dateString);
        // 注意 第一个参数是我们交换机的名称 ,第二个参数是routerKey 我们不用管空着就可以,第三个是你要发送的消息
        rabbitSender.sendByExchange("fanoutExchange", "", dateString);
    }

然后执行测试类

2020-03-27 16:10:19.275  INFO 68640 --- [           main] s.s.t.SpringbootTeach10ApplicationTests  : Started SpringbootTeach10ApplicationTests in 4.074 seconds (JVM running for 5.159)
[fanout] send msg:2020-10-87 04:03:19
Sender : 2020-10-87 04:03:19
fanout.a Receiver  : 2020-10-87 04:03:19
fanout.c Receiver  : 2020-10-87 04:03:19
fanout.b Receiver  : 2020-10-87 04:03:19

三个队列的消费都成功接收到消息。

三、topic模式,

同样,配置topic队列跟交换器,注意的是这里需要多配置一个bindingKey

 //#################topic模式########################

    @Bean
    public Queue topiocA() {
        return new Queue("topic.a");
    }

    @Bean
    public Queue topicB() {
        return new Queue("topic.b");
    }

    @Bean
    public Queue topicC() {
        return new Queue("topic.c");
    }

    /**
     * 定义个topic交换器
     * @return
     */
    @Bean
    TopicExchange topicExchange() {
        // 定义一个名为fanoutExchange的fanout交换器
        return new TopicExchange("topicExchange");
    }

    /**
     * 将定义的topicA队列与topicExchange交换机绑定
     * @return
     */
    @Bean
    public Binding bindingTopicExchangeWithA() {
        return BindingBuilder.bind(topiocA()).to(topicExchange()).with("topic.msg");
    }

    /**
     * 将定义的topicB队列与topicExchange交换机绑定
     * @return
     */
    @Bean
    public Binding bindingTopicExchangeWithB() {
        return BindingBuilder.bind(topicB()).to(topicExchange()).with("topic.#");
    }

    /**
     * 将定义的topicC队列与topicExchange交换机绑定
     * @return
     */
    @Bean
    public Binding bindingTopicExchangeWithC() {
        return BindingBuilder.bind(topicC()).to(topicExchange()).with("topic.*.z");
    }
topicA的key为topic.msg 那么他只会接收包含topic.msg的消息
topicB的key为topic.#那么他只会接收topic开头的消息
topicC的key为topic.*.Z那么他只会接收topic.B.z这样格式的消息

然后在RabbitConsumer新建队列的消费方法

 // #################topic模式########################

    @RabbitListener(queues = "topic.a")
    @RabbitHandler
    public void readTopicAQueue(String content) {
        System.out.println("topic.a Receiver  : " + content);
    }

    @RabbitListener(queues = "topic.b")
    @RabbitHandler
    public void readTopicBQueue(String content) {
        System.out.println("topic.b Receiver  : " + content);
    }

    @RabbitListener(queues = "topic.c")
    @RabbitHandler
    public void readTopicCQueue(String content) {
        System.out.println("topic.c Receiver  : " + content);
    }

同理为topic新建测试方法

    /**
     * topic 模式测试
     */
    @Test
    public void topicTopic1Send() {
        Date date = new Date();
        String dateString = new SimpleDateFormat("YYYY-mm-DD hh:MM:ss").format(date);
        dateString = "[topic.msg] send msg:" + dateString;
        System.out.println(dateString);
        // 注意 第一个参数是我们交换机的名称 ,第二个参数是routerKey topic.msg,第三个是你要发送的消息
        // 这条信息将会被 topic.a topic.b接收
        rabbitSender.sendByExchange("topicExchange", "topic.msg", dateString);
        rabbitSender.sendByExchange("topicExchange", "topic.good.msg", dateString);
        rabbitSender.sendByExchange("topicExchange", "topic.m.z", dateString);
    }

启动测试消费者,看看消息是不是按照规则被发送消息

2020-03-27 16:14:36.290  INFO 104536 --- [           main] s.s.t.SpringbootTeach10ApplicationTests  : Started SpringbootTeach10ApplicationTests in 4.381 seconds (JVM running for 5.463)
[topic.msg] send msg:2020-14-87 04:03:36
Sender : [topic.msg] send msg:2020-14-87 04:03:36
Sender : [topic.msg] send msg:2020-14-87 04:03:36
Sender : [topic.msg] send msg:2020-14-87 04:03:36
topic.a Receiver  : [topic.msg] send msg:2020-14-87 04:03:36
topic.b Receiver  : [topic.msg] send msg:2020-14-87 04:03:36
topic.c Receiver  : [topic.msg] send msg:2020-14-87 04:03:36
topic.b Receiver  : [topic.msg] send msg:2020-14-87 04:03:36
topic.b Receiver  : [topic.msg] send msg:2020-14-87 04:03:36

其中 队列topic.a只配置topic.msg一条消息,正确
其中 队列topic.b匹配三条消息,因为三条消息都是topic开头的 正确
其中 队列topic.c匹配一条消息,只有一条消息满足(也就是topic.m.z这条消息)

1.3.3 异步发送邮件

增加发邮件代码,请参考SpringBoot入门之五 整合邮件服务
编写发送消息的controller
MailController.java

@Controller
public class MailController {
    @Autowired
    private MailService mailService;
    @Autowired
    private RabbitSender rabbitSender;

    @GetMapping("/mail/sendMail")
    @ResponseBody
    public String sendBySync(HttpServletRequest request) {

        String msg = null;
        try {
            String[] mailTo = {"407737980@qq.com"};

            // 发送html格式正文的邮件 整合thymeleaf模板
            Map<String, Object> variableMap = new HashMap<String, Object>();

            variableMap.put("subject", "hello ");
            variableMap.put("content", "hello !This is a test! ");

            long begin = System.currentTimeMillis();
            String result = mailService.sendMsgByThymeleafTemplate(
                new MailEntity("hello freemark", "hello !This is a test! ", mailTo, variableMap));
            long end = System.currentTimeMillis();
            long useTime = end - begin;
            msg = "success, uset time " + useTime + " ms";
            if (result.equals("success")) {
                return msg;
            } else {
                return "error";
            }
        } catch (Exception e) {
            msg = e.getMessage();
            return "error";
        }
    }

    @GetMapping("/mail/sendMailByMq")
    @ResponseBody
    public String sendByMq(HttpServletRequest request) {

        String msg = null;
        try {

            // 发送html格式正文的邮件 整合thymeleaf模板
            Map<String, Object> variableMap = new HashMap<String, Object>();

            variableMap.put("subject", "hello ");
            variableMap.put("content", "hello !This is a test! ");
            rabbitSender.send("test", "hello !This is a test! ", variableMap);

            return "success";
        } catch (Exception e) {
            msg = e.getMessage();
            return "error";
        }
    }

    @GetMapping("/mail/sendSimpleMq")
    @ResponseBody
    public String sendSimpleMsg(HttpServletRequest request) {

        Date date = new Date();
        String dateString = new SimpleDateFormat("YYYY-mm-DD hh:MM:ss").format(date);
        System.out.println("[fanout] send msg:" + dateString);
        // 注意 第一个参数是队列名称 第二个是消息内容
        rabbitSender.send("hello", dateString);
        return "success";
    }

    @GetMapping("/mail/sendTopicMq")
    @ResponseBody
    public String sendTopicMsg(HttpServletRequest request) {

        Date date = new Date();
        String dateString = new SimpleDateFormat("YYYY-mm-DD hh:MM:ss").format(date);
        System.out.println("[fanout] send msg:" + dateString);
        // 注意 第一个参数是我们交换机的名称 ,第二个参数是routerKey topic.msg,第三个是你要发送的消息
        // 这条信息将会被 topic.a topic.b接收
        rabbitSender.sendByExchange("topicExchange", "topic.msg", dateString);
        return "success";
    }

    @GetMapping("/mail/index")
    public ModelAndView mail(HttpServletRequest request) {

        ModelAndView mv = new ModelAndView();
        mv.setViewName("/web/email.html");
        return mv;
    }
}

编写发送消息的web页面

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<div>
    <a href="" th:href="@{/mail/sendMail}">点击同步发送邮件</a> <br>
    <a href="" th:href="@{/mail/sendMailByMq}">点击异步发送邮件</a> <br>
    <a href="" th:href="@{/mail/sendSimpleMq}">点击发送消息-普通工作模式</a> <br>
    <a href="" th:href="@{/mail/sendTopicMq}">点击发送消息-普通订阅模式</a> <br>
</div>
</body>
</html>

启动项目
在这里插入图片描述
点击异步发送邮件
在这里插入图片描述

源码地址

源码地址

2 ActiveMq

ActiveMQ是由Apache出品,ActiveMQ是一个完全支持JMS1.1和J2EE 1.4规范的JMS Provider实现。它非常快速,支持多种语言的客户端和协议,而且可以非常容易的嵌入到企业的应用环境中,并有许多高级功能。

2.1 特点

ActiveMQ是Apache出品,最流行的,能力强劲的开源消息总线,并且它是一个完全支持JMS规范的消息中间件
其丰富的API、多种集群构建模式使得他成为业界老牌消息中间件,在中小型企业中应用广泛!
MQ衡量指标:服务性能、数据存储、集群架构。

ActiveMQ现在用的比较少,因为ActiveMQ相比其他的MQ的性能来说比较一般。现如今高并发、大数据的应用场景随处可见。如果这时候在MQ的选择上,那么ActiveMQ就显得力不从心了。

衡量一个MQ的指标,主要有三个方面:服务性能、数据存储、集群架构
服务性能:ActiveMQ的性能不是特别好,面对超大规模并发时候,总是会出现各种各样的小问题,比如阻塞,消息堆积过多,产生一些延迟等等一些问题。
数据存储:ActiveMQ默认采用KahaDB内存存储方式。也可以采用一些高性能的存储方式,比如:google的LevelDb 基于内c存的。如果是为了保证消息的可靠,也可以采用mysql或者oracle数据库。
集群架构:ActiveMQ流行那么多年,与其他组件集成的Api也是十分完善的。如果不是特别大的并发场景下,ActiveMQ也是一个不错的选择。因为ActiveMQ的集群架构模式也是十分好。

2.2 架构模式

Masrer-Slave模式
主备模式,利用Zookeeper进行两个或多个节点的协调。其中的主节点是对外提供服务的,而另外的从节点启动着,但是不对外提供服务。当主节点挂掉,利用Zookeeper进行一个高可用的切换,将Salve节点切换成主节点,继续对外提供服务。

NetWork模式

本质是两组主备模式的集成,中间用NewWork网关,做一个连接配置,就可以实现分布式集群。
在这里插入图片描述

2.3 优缺点

优点:

跨平台(JAVA编写与平台无关,ActiveMQ几乎可以运行在任何的JVM上)
可以用JDBC:可以将数据持久化到数据库。虽然使用JDBC会降低ActiveMQ的性能,但是数据库一直都是开发人员最熟悉的存储介质
支持JMS规范:支持JMS规范提供的统一接口
支持自动重连和错误重试机制
有安全机制:支持基于shiro,jaas等多种安全配置机制,可以对Queue/Topic进行认证和授权
监控完善:拥有完善的监控,包括WebConsole,JMX,Shell命令行,Jolokia的RESTful API
界面友善:提供的WebConsole可以满足大部分情况,还有很多第三方的组件可以使用,比如hawtio

缺点:

社区活跃度不及RabbitMQ高
根据其他用户反馈,会出莫名其妙的问题,会丢失消息
目前重心放到activemq6.0产品Apollo,对5.x的维护较少
不适合用于上千个队列的应用场景

2.4 spingboot整合activeMq

2.4.1配置pom依赖

	<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-activemq</artifactId>
		</dependency>
		<!--消息队列连接池-->
        <dependency>
            <groupId>org.apache.activemq</groupId>
            <artifactId>activemq-pool</artifactId>
        </dependency>

2.4.2 activeMq配置文件

spring:
  activemq:
    broker-url: tcp://192.168.0.1:61616
    user: admin
    password: admin
    close-timeout: 15s   # 在考虑结束之前等待的时间
    in-memory: true      # 默认代理URL是否应该在内存中。如果指定了显式代理,则忽略此值。
    non-blocking-redelivery: false  # 是否在回滚回滚消息之前停止消息传递。这意味着当启用此命令时,消息顺序不会被保留。
    send-timeout: 0     # 等待消息发送响应的时间。设置为0等待永远。
    queue-name: active.queue
    topic-name: active.topic.name.model
    

2.4.3 源码实例

ActiveMqConf

@Configuration
public class ActiveMqConf {
    @Value("${spring.activemq.user}")
    private String username;
    @Value("${spring.activemq.password}")
    private String password;
    @Value("${spring.activemq.broker-url}")
    private String brokerUrl;

    @Bean
    public ConnectionFactory connectionFactory() {
        return new ActiveMQConnectionFactory(username, password, brokerUrl);
    }

    // 在Queue模式中,对消息的监听需要对containerFactory进行配置
    @Bean("queueListener")
    public JmsListenerContainerFactory<?> queueJmsListenerContainerFactory(ConnectionFactory connectionFactory) {
        SimpleJmsListenerContainerFactory factory = new SimpleJmsListenerContainerFactory();
        factory.setConnectionFactory(connectionFactory);
        factory.setPubSubDomain(false);
        return factory;
    }

    // 在Topic模式中,对消息的监听需要对containerFactory进行配置
    @Bean("topicListener")
    public JmsListenerContainerFactory<?> topicJmsListenerContainerFactory(ConnectionFactory connectionFactory) {
        SimpleJmsListenerContainerFactory factory = new SimpleJmsListenerContainerFactory();
        factory.setConnectionFactory(connectionFactory);
        factory.setPubSubDomain(true);
        return factory;
    }
}

发送消息类

@Component
public class ActiveMqSender {
    @Autowired
    private JmsTemplate jmsQueueTemplate;
    @Value("${spring.activemq.queue-name}")
    private String queueName;

    @Value("${spring.activemq.topic-name}")
    private String topicName;

    @Bean(name = "queue")
    public Queue queue() {
        return new ActiveMQQueue(queueName);
    }

    @Bean(name = "topic")
    public Topic topic() {
        return new ActiveMQTopic(topicName);
    }

    /**
     * 发送原始消息 Message
     */
    public void send(String destinationName, final String msg) {
        jmsQueueTemplate.send(destinationName, new MessageCreator() {
            @Override
            public Message createMessage(Session session) throws JMSException {
                return session.createTextMessage(msg);
            }
        });

    }
}

ActiveMqConsumer

@Component
public class ActiveMqConsumer {
    // topic模式的消费者
    @JmsListener(destination = "${spring.activemq.topic-name}", containerFactory = "topicListener")
    public void readActiveTopic(String message) {
        System.out.println("topic接受到:" + message);
    }

    // Queue模式的消费者
    // queue模式的消费者
    @JmsListener(destination = "${spring.activemq.queue-name}", containerFactory = "queueListener")
    public void readActiveQueue(String message) {
        System.out.println("queue接受到:" + message);
    }
}

测试类

    @Autowired
    private ActiveMqSender activeMqSender;


    @Test
    public void activeMqTest() {
        activeMqSender.send("active.queue", "hello");
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

浮华落定

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值