RabbitMQ与Spring的整合及常见三种消息模式使用

一. 概念

定义:

    RabbitMQ是流行的开源消息队列系统,用erlang语言开发。RabbitMQ是AMQP(高级消息队列协议)的标准实现。

消息生产与消费机制如下:

常见MQ横向比较:

    RabbitMQ:它比kafka成熟,支持AMQP事务处理,在可靠性上,RabbitMq超过kafka,在性能方面超过ActiveMQ;

        同时rabbitmq支持回调,也就是客户端可以知道消息的执行情况!

    ActiveMQ: 历史悠久的开源项目,已经在很多产品中得到应用,实现了JMS1.1规范,可以和spring-jms轻松融合,实现了多种协议,支持持久化到数据库,对队列数较多的情况支持不好;

    Kafka:不支持AMQP事务处理,性能(吞吐量、tps)比RabbitMq要强,可靠性不如rabbitMQ,适合做大数据处理;

    故RabbitMQ在一般分布式项目, 或其他有异步处理和模块解耦要求的场景下,  具有降大优势;

常见三种消息模式:

    1.direct:指定将消息发给指定节点

    2.fanout:指定将消息发给指定exchanger. 该exchanger将消息分发给绑定在其上的所有的queue

    3.topic:消息都会被转发到所有关心RouteKey中指定话题的queue上

 

二. 安装:

    linux和windows下均可安装, 本博安装在windows下,安装较为简单, 不作介绍(注意需要先安装erlang语言环境);

常见bug解决:

        1.RabbitMQ学习--“java.lang.NoSuchMethodError: com.rabbitmq.client.ConnectionFactory.newConnection”问题解决

        2.java.lang.NoSuchMethodError: com.rabbitmq.client.ConnectionFactory.newConnection(Ljava/util/concurrent/ExecutorService;Ljava/lang/String;)Lcom/rabbitmq/client/Connection

            解决办法: 引入spring-rabbit包后不可再引入amqp-client, 否则引发包冲突;

在成功安装后, 到安装目录打开cmd命令行, 如下依次:

 

    1.开启rabbitmq服务

    2.创建虚拟机 test_vhost

    3.创建用户  设置密码 root root

    4.将用户设置为管理员

    5.为用户赋权 操作相关虚拟机

D:\rabbitMQ\rabbitmq_server-3.7.4\sbin>rabbitmq-service.bat start
The requested service has already been started.

D:\rabbitMQ\rabbitmq_server-3.7.4\sbin>rabbitmqctl add_vhost test_vhost
Adding vhost "test_vhost" ...

D:\rabbitMQ\rabbitmq_server-3.7.4\sbin>rabbitmqctl add_user root root
Adding user "root" ...

D:\rabbitMQ\rabbitmq_server-3.7.4\sbin>rabbitmqctl set_user_tags root administrator
Setting tags for user "root" to [administrator] ...

D:\rabbitMQ\rabbitmq_server-3.7.4\sbin>rabbitmqctl set_permissions -p test_vhost root ".*" ".*" ".*"
Setting permissions for user "root" in vhost "test_vhost" ...

 

三. 开始整合:

创建maven项目, 并引入依赖如下:

    <dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.0.2.RELEASE</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.amqp</groupId>
            <artifactId>spring-rabbit</artifactId>
            <version>1.7.5.RELEASE</version>
        </dependency>

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-test</artifactId>
            <version>5.0.2.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
        </dependency>
    </dependencies>

编写消费者代码(类似可以编写多个, 不再重复):

package cn.com.bitic.rabbitmq.listener;

import org.springframework.amqp.core.Message;
import org.springframework.amqp.core.MessageListener;

import java.io.UnsupportedEncodingException;

public class DirectMessageListener implements MessageListener {
    public void onMessage(Message message) {
        try {
            String s = new String(message.getBody(), "UTF-8");//body为字节数组, 所以需要解码
            System.out.println(s);
        } catch (Exception ignore) { }
    }
}

编写配置文件spring_rabbitmq.xml:

1.头文件

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:rabbit="http://www.springframework.org/schema/rabbit"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
     http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
     http://www.springframework.org/schema/rabbit
     http://www.springframework.org/schema/rabbit/spring-rabbit-1.2.xsd">

</bean>

2.添加连接工厂配置 注: 因rabbitConnectionFactory为默认工厂名称, 所以在消费者和生产者中都不需要再定义连接工厂引用

<!--配置connection-factory,指定连接rabbit server参数 -->
    <rabbit:connection-factory id="rabbitConnectionFactory" virtual-host="test_vhost"
                               username="root" password="root" host="127.0.0.1" port="5672"/>

3.添加自动创建queue和exchanger的配置

<!--通过指定下面的admin信息,当前producer中的exchange和queue会在rabbitmq服务器上自动生成 -->
    <rabbit:admin id="connectAdmin" connection-factory="rabbitConnectionFactory" />

4.配置消息队列

<rabbit:queue name="queue_1"  declared-by="connectAdmin" />

5.配置路由器exchanger direct模式, 并绑定queue_1 

   <!-- 定义direct exchange,绑定queue_1 -->
    <rabbit:direct-exchange name="exchange_1" declared-by="connectAdmin">
        <rabbit:bindings>
            <rabbit:binding queue="queue_1"/>
        </rabbit:bindings>
    </rabbit:direct-exchange>

6.定义rabbotTemplate

<!--定义rabbit template用于数据的接收和发送 -->
    <rabbit:template id="amqpTemplate" connection-factory="rabbitConnectionFactory" exchange="exchange_1" />

7.因为在一个项目中演示, 所以继续配置消费者

    <!-- 消息接收者 -->
    <bean id="messageReceiver" class="cn.com.bitic.rabbitmq.listener.DirectMessageListener"/>

    <!-- queue litener 观察 监听模式 当有消息到达时会通知监听在对应的队列上的监听对象 -->
    <rabbit:listener-container>
        <rabbit:listener queues="queue_1" ref="messageReceiver" />
    </rabbit:listener-container>

applicationContext.xml配置:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context" xmlns:tx="http://www.springframework.org/schema/tx"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
    <!--引入配置文件-->
    <import resource="classpath:spring_rabbitmq.xml"/>
</beans>

基于如上,便配置完基本的direct模式的配置,测试代码如下:

package test;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.amqp.core.AmqpTemplate;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

import javax.annotation.Resource;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:applicationContext.xml")
public class RabbitTest01 {
    @Resource(name = "amqpTemplate")
    private AmqpTemplate amqpTemplate;

    @Resource(name = "rabbitTemplate")
    private AmqpTemplate rabbitTemplate;

    @Test
    public void test01() throws InterruptedException {
        amqpTemplate.convertAndSend("queue_1_key","hello");
        rabbitTemplate.convertAndSend("文维");
        Thread.sleep(5000L);
    }
}

基于以上, 已经正确演示direct消息的发送和消费, 实际项目中可以基于以上逻辑, 实现相关业务;

继续演示fanout模式消息:

在spring_rabbitmq.xml文件中添加配置:

生产者 基于direct消息的配置, 可以理解如下:

    <rabbit:queue name="queue_2"  declared-by="connectAdmin"/>
    <rabbit:queue name="queue_3"  declared-by="connectAdmin"/>

    <rabbit:fanout-exchange name="exchange_2" declared-by="connectAdmin">
        <rabbit:bindings>
            <rabbit:binding queue="queue_2"/>
            <rabbit:binding queue="queue_3"/>
        </rabbit:bindings>
    </rabbit:fanout-exchange>

    <rabbit:template id="rabbitTemplate" connection-factory="rabbitConnectionFactory" exchange="exchange_2"/>

消费者

<!--定义监听器-->
    <bean id="messageListener_2" class="cn.com.bitic.rabbitmq.listener.FanoutMessageListener"/>

    <rabbit:listener-container>
        <rabbit:listener queues="queue_2" ref="messageListener_2"/>
    </rabbit:listener-container>

    <!--定义监听器-->
    <bean id="messageListener_3" class="cn.com.bitic.rabbitmq.listener.FanoutMessageListener_2"/>

    <rabbit:listener-container>
        <rabbit:listener queues="queue_3" ref="messageListener_3"/>
    </rabbit:listener-container>

在测试代码中添加消息发布:

rabbitTemplate.convertAndSend("文维");

基于以上, 已经正确演示fanout消息的发送和消费;

继续演示topic模式消息:

topic消息特点是,可以基于routingkey定义在指定exchanger下哪些queue接收到消息:

在配置文件spring_rabbitmq.xml中:

    <rabbit:template id="rabbitTemplate" connection-factory="rabbitConnectionFactory" exchange="exchange_2"/>

    <rabbit:queue name="queue_4"  declared-by="connectAdmin"/>
    <rabbit:queue name="queue_5"  declared-by="connectAdmin"/>
    <rabbit:queue name="queue_6"  declared-by="connectAdmin"/>

    <rabbit:topic-exchange name="exchange_topic">
        <rabbit:bindings>
            <rabbit:binding queue="queue_4" pattern="*.*.test"/>
            <rabbit:binding queue="queue_5" pattern="com.bitic.test"/>
            <rabbit:binding queue="queue_6" pattern="*.bitic.test"/>
        </rabbit:bindings>
    </rabbit:topic-exchange>

    <rabbit:template id="rabbitTemplate_topic" connection-factory="rabbitConnectionFactory" exchange="exchange_topic"/>

消费者:

    <!--定义监听器-->
    <bean id="messageListener_4" class="cn.com.bitic.rabbitmq.listener.FanoutMessageListener_4"/>

    <rabbit:listener-container>
        <rabbit:listener queues="queue_4" ref="messageListener_4"/>
    </rabbit:listener-container>

    <!--定义监听器-->
    <bean id="messageListener_5" class="cn.com.bitic.rabbitmq.listener.FanoutMessageListener_5"/>

    <rabbit:listener-container>
        <rabbit:listener queues="queue_5" ref="messageListener_5"/>
    </rabbit:listener-container>

    <!--定义监听器-->
    <bean id="messageListener_6" class="cn.com.bitic.rabbitmq.listener.FanoutMessageListener_6"/>

    <rabbit:listener-container>
        <rabbit:listener queues="queue_6" ref="messageListener_6"/>
    </rabbit:listener-container>

在测试类添加如下测试代码 分别观察执行结果:

//        rabbitTemplate_topic.convertAndSend("exchange_topic","com.bitic.test","Topic模式");
//        rabbitTemplate_topic.convertAndSend("exchange_topic","cn.bitic.test","Topic模式");
        rabbitTemplate_topic.convertAndSend("exchange_topic","cn.bitic_1.test","Topic模式");

以上就是RabbitMQ的基本概念和与spring整合的基本操作;

我们要始终明白使用消息中间件的目的:

    1.模块解耦, 各个模块各司其职, 避免业务耦合;

        应用场景:商品上下架时,可以将Solr索引库的同步请求发送到消息队列, 让搜索微服务自己去同步Solr库, 而商品模块可以直接认为商品同步成功, 并且此处的Solr索引同步延迟是可以接受的;

    2.异步处理,将不需要同步处理的业务进行异步处理, 提高接口响应速度;

        典型场景,后端生成验证码之后不必等待短信系统发送完再将结果返回, 当提交给短信发送微服务后即可认为发送成功, 故可直接通知用户短信已发送;

    3.削峰填谷, 均衡整个系统的负载,:

        rabbitmq是默认单线程消费某个队列的消息, 也可以通过设置, 进行并发消费,总之其实可控的,避免高并发下的系统假死;

        可以设置最大消息数, 当超过时直接丢弃, 保证系统不至于过载;

        应用场景:秒杀;

----------------------------------------------------------------------------完--------------------------------------------------------------------------------------------
作者:划船一哥 
来源:CSDN 
https://me.csdn.net/weixin_42711325
版权声明:本文为博主原创文章,转载请附上博文链接!

 

 

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值