Spring 整合 RabbitMQ

1.导入依赖

  <properties>

    .....

    <!-- spring -->
    <spring.version>5.1.1.RELEASE</spring.version>
    <!-- log4j日志包版本号 -->
    <slf4j.version>1.7.18</slf4j.version>
    <log4j.version>1.2.17</log4j.version>
  </properties>

  <dependencies>

    <!-- Spring -->
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-core</artifactId>
      <version>${spring.version}</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-webmvc</artifactId>
      <version>${spring.version}</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-oxm</artifactId>
      <version>${spring.version}</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-tx</artifactId>
      <version>${spring.version}</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-aop</artifactId>
      <version>${spring.version}</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-context-support</artifactId>
      <version>${spring.version}</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-test</artifactId>
      <version>${spring.version}</version>
    </dependency>

    <!-- AOP-AspectJ spring-aop依赖 -->
    <dependency>
      <groupId>org.aspectj</groupId>
      <artifactId>aspectjrt</artifactId>
      <version>1.8.6</version>
    </dependency>
    <dependency>
      <groupId>org.aspectj</groupId>
      <artifactId>aspectjweaver</artifactId>
      <version>1.8.6</version>
    </dependency>

    <!-- 添加日志相关jar包 -->
    <dependency>
      <groupId>log4j</groupId>
      <artifactId>log4j</artifactId>
      <version>${log4j.version}</version>
    </dependency>
    <dependency>
      <groupId>org.slf4j</groupId>
      <artifactId>slf4j-api</artifactId>
      <version>${slf4j.version}</version>
    </dependency>
    <dependency>
      <groupId>org.slf4j</groupId>
      <artifactId>slf4j-log4j12</artifactId>
      <version>${slf4j.version}</version>
    </dependency>

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

    .....

  </dependencies>

2.创建配置文件

  a.创建 spring.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:aop="http://www.springframework.org/schema/aop"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="
        http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">

    <!-- 自动扫描的包名 -->
    <context:component-scan base-package="com.wode" />

    <!-- 开启AOP代理 -->
    <aop:aspectj-autoproxy proxy-target-class="true" />

    <!--开启注解处理器 -->
    <context:annotation-config>
    </context:annotation-config>

    <context:property-placeholder location="classpath:rabbit.properties"/>
    <!-- Spring中引入其他配置文件 -->
    <import resource="classpath*:/spring-rabbit.xml" />

</beans>

  b.创建 spring-rabbit.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:rabbit="http://www.springframework.org/schema/rabbit"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/rabbit http://www.springframework.org/schema/rabbit/spring-rabbit-1.2.xsd">

    <!-- ============================================公共部分============================================ -->

    <!-- 创建连接类 连接安装好的 rabbitmq -->
    <rabbit:connection-factory id="connectionFactory" host="${rabbit.ip}" port="${rabbit.port}" username="${rabbit.username}" password="${rabbit.password}" />
    <rabbit:admin connection-factory="connectionFactory"/>

    <!-- spring amqp默认的是jackson 的一个插件,目的将生产者生产的数据转换为json存入消息队列 -->
    <bean id="jsonMessageConverter" class="org.springframework.amqp.support.converter.Jackson2JsonMessageConverter" />

    <!-- ============================================direct路由模式============================================ -->

    <!--定义消息队列,durable:是否持久化,如果想在RabbitMQ退出或崩溃的时候,不会失去所有的queue和消息,需要同时标志队列(queue)和交换机(exchange)是持久化的,即rabbit:queue标签和rabbit:direct-exchange中的durable=true,而消息(message)默认是持久化的可以看类org.springframework.amqp.core.MessageProperties中的属性public static final MessageDeliveryMode DEFAULT_DELIVERY_MODE = MessageDeliveryMode.PERSISTENT;exclusive: 仅创建者可以使用的私有队列,断开后自动删除;auto_delete: 当所有消费客户端连接断开后,是否自动删除队列 -->
    <rabbit:queue name="direct.queue.1" id="direct.queue.1" durable="true" auto-delete="false" exclusive="false" />
    <rabbit:queue name="direct.queue.2" id="direct.queue.2" durable="true" auto-delete="false" exclusive="false" />

    <!--绑定队列,rabbitmq的exchangeType常用的三种模式:direct,fanout,topic三种,我们用direct模式,即rabbit:direct-exchange标签,Direct交换器很简单,如果是Direct类型,就会将消息中的RoutingKey与该Exchange关联的所有Binding中的BindingKey进行比较,如果相等,则发送到该Binding对应的Queue中。有一个需要注意的地方:如果找不到指定的exchange,就会报错。但routing key找不到的话,不会报错,这条消息会直接丢失,所以此处要小心,auto-delete:自动删除,如果为Yes,则该交换机所有队列queue删除后,自动删除交换机,默认为false -->
    <rabbit:direct-exchange id="direct.exchange" name="direct.exchange" durable="true" auto-delete="false">
        <rabbit:bindings>
            <rabbit:binding queue="direct.queue.1" key="${routing.1}"></rabbit:binding>
            <rabbit:binding queue="direct.queue.2" key="${routing.2}"></rabbit:binding>
        </rabbit:bindings>
    </rabbit:direct-exchange>

    <rabbit:template exchange="direct.exchange" id="rabbitTemplate" connection-factory="connectionFactory" message-converter="jsonMessageConverter" />

    <!-- 消费者部分 -->
    <!-- 自定义接口类 -->
    <bean id="directConsumerAuto" class="com.wode.direct.DirectConsumerAuto"></bean>
    <bean id="directConsumerManual" class="com.wode.direct.DirectConsumerManual"></bean>

    <!-- 配置监听acknowledeg="manual"设置手动应答,它能够保证即使在一个worker处理消息的时候用CTRL+C来杀掉这个worker,或者一个consumer挂了(channel关闭了、connection关闭了或者TCP连接断了),也不会丢失消息。因为RabbitMQ知道没发送ack确认消息导致这个消息没有被完全处理,将会对这条消息做re-queue处理。如果此时有另一个consumer连接,消息会被重新发送至另一个consumer会一直重发,直到消息处理成功,监听容器acknowledge="auto" concurrency="30"设置发送次数,最多发送30次 -->
    <rabbit:listener-container connection-factory="connectionFactory" acknowledge="auto" concurrency="20">
        <rabbit:listener queues="direct.queue.1" ref="directConsumerAuto" />
    </rabbit:listener-container>
    <rabbit:listener-container connection-factory="connectionFactory" acknowledge="manual" concurrency="20">
        <rabbit:listener queues="direct.queue.2" ref="directConsumerManual" />
    </rabbit:listener-container>


    <!-- ============================================fanout订阅推送模式============================================ -->

    <!--定义消息队列-->
    <rabbit:queue name="fanout.queue.1" id="fanout.queue.1" durable="true" auto-delete="false" exclusive="false" />
    <rabbit:queue name="fanout.queue.2" id="fanout.queue.2" durable="true" auto-delete="false" exclusive="false" />

    <!-- Fanout 扇出,顾名思义,就是像风扇吹面粉一样,吹得到处都是。如果使用fanout类型的exchange,那么routing key就不重要了。因为凡是绑定到这个exchange的queue,都会受到消息。 -->
    <rabbit:fanout-exchange id="fanout.exchange" name="fanout.exchange" durable="true" auto-delete="false">
        <rabbit:bindings>
            <rabbit:binding queue="fanout.queue.1"></rabbit:binding>
            <rabbit:binding queue="fanout.queue.2"></rabbit:binding>
        </rabbit:bindings>
    </rabbit:fanout-exchange>

    <rabbit:template exchange="fanout.exchange" id="fanoutRabbitTemplate" connection-factory="connectionFactory" message-converter="jsonMessageConverter" />

    <!-- 消费者部分 -->
    <!-- 自定义接口类 -->
    <bean id="fanoutConsumerAuto" class="com.wode.fanout.FanoutConsumerAuto"></bean>
    <bean id="fanoutConsumerManual" class="com.wode.fanout.FanoutConsumerManual"></bean>

    <!-- 配置监听acknowledeg="manual"设置手动应答,它能够保证即使在一个worker处理消息的时候用CTRL+C来杀掉这个worker,或者一个consumer挂了(channel关闭了、connection关闭了或者TCP连接断了),也不会丢失消息。因为RabbitMQ知道没发送ack确认消息导致这个消息没有被完全处理,将会对这条消息做re-queue处理。如果此时有另一个consumer连接,消息会被重新发送至另一个consumer会一直重发,直到消息处理成功,监听容器acknowledge="auto" concurrency="30"设置发送次数,最多发送30次 -->
    <rabbit:listener-container connection-factory="connectionFactory" acknowledge="auto" concurrency="20">
        <rabbit:listener queues="fanout.queue.1" ref="fanoutConsumerAuto" />
    </rabbit:listener-container>
    <rabbit:listener-container connection-factory="connectionFactory" acknowledge="manual" concurrency="20">
        <rabbit:listener queues="fanout.queue.2" ref="fanoutConsumerManual" />
    </rabbit:listener-container>


    <!-- ============================================topic模式============================================ -->

    <!--定义消息队列-->
    <rabbit:queue name="topic.queue.1" id="topic.queue.1" durable="true" auto-delete="false" exclusive="false" />
    <rabbit:queue name="topic.queue.2" id="topic.queue.2" durable="true" auto-delete="false" exclusive="false" />

    <!-- 发送端不是按固定的routing key发送消息,而是按字符串“匹配”发送,接收端同样如此 -->
    <rabbit:topic-exchange id="topic.exchange" name="topic.exchange" durable="true" auto-delete="false">
        <rabbit:bindings>
            <rabbit:binding queue="topic.queue.1" pattern="order.*" />
            <rabbit:binding queue="topic.queue.2" pattern="*.insert" />
        </rabbit:bindings>
    </rabbit:topic-exchange>

    <rabbit:template exchange="topic.exchange" id="topicRabbitTemplate" connection-factory="connectionFactory" message-converter="jsonMessageConverter" />

    <!-- 消费者部分 -->
    <!-- 自定义接口类 -->
    <bean id="topicConsumerAuto" class="com.wode.topic.TopicConsumerAuto"></bean>
    <bean id="topicConsumerManual" class="com.wode.topic.TopicConsumerManual"></bean>

    <!-- 配置监听acknowledeg="manual"设置手动应答,它能够保证即使在一个worker处理消息的时候用CTRL+C来杀掉这个worker,或者一个consumer挂了(channel关闭了、connection关闭了或者TCP连接断了),也不会丢失消息。因为RabbitMQ知道没发送ack确认消息导致这个消息没有被完全处理,将会对这条消息做re-queue处理。如果此时有另一个consumer连接,消息会被重新发送至另一个consumer会一直重发,直到消息处理成功,监听容器acknowledge="auto" concurrency="30"设置发送次数,最多发送30次 -->
    <rabbit:listener-container connection-factory="connectionFactory" acknowledge="auto" concurrency="20">
        <rabbit:listener queues="topic.queue.1" ref="topicConsumerAuto" />
    </rabbit:listener-container>
    <rabbit:listener-container connection-factory="connectionFactory" acknowledge="manual" concurrency="20">
        <rabbit:listener queues="topic.queue.2" ref="topicConsumerManual" />
    </rabbit:listener-container>


</beans>

  c.创建 rabbit.properties

#RabbitMQ服务器地址,默认值"localhost"
rabbit.ip=localhost
#RabbitMQ服务端口,默认值为5672
rabbit.port=5672
#访问RabbitMQ服务器的账户,默认是guest
rabbit.username=guest
#访问RabbitMQ服务器的密码,默认是guest
rabbit.password=guest

#路由标识
routing.1=1
routing.2=2

3.创建生产者

import org.springframework.amqp.core.AmqpTemplate;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;

@Component
public class CommonProducer {

    //direct模式
    @Resource(name = "rabbitTemplate")
    private AmqpTemplate rabbitTemplate;

    @Value("${routing.1}")
    private String routing1;
    @Value("${routing.2}")
    private String routing2;

    //fanout模式
    @Resource(name = "fanoutRabbitTemplate")
    private AmqpTemplate fanoutRabbitTemplate;

    //topic模式
    @Resource(name = "topicRabbitTemplate")
    private AmqpTemplate topicRabbitTemplate;


    public void send(){
        rabbitTemplate.convertAndSend(routing1, "routing1");
        rabbitTemplate.convertAndSend(routing2, "routing2");

        fanoutRabbitTemplate.convertAndSend("fanoutMsg");

        topicRabbitTemplate.convertAndSend("order.insert", "order.insert");
        topicRabbitTemplate.convertAndSend("order.update", "order.update");
    }

}

4.创建direct模式消费者

  a.自动提交消费者,对应配置文件中 acknowledge="auto"

import com.rabbitmq.client.Channel;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.core.ChannelAwareMessageListener;

public class DirectConsumerAuto implements ChannelAwareMessageListener {
    @Override
    public void onMessage(Message message, Channel channel) throws Exception {
        String msg = new String(message.getBody(),"UTF-8");
        System.out.println("[DirectConsumerAuto]消费者接收到:" + msg);
    }
}

  b.手动提交消费者,对应配置文件中 acknowledge="manual"

import com.rabbitmq.client.Channel;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.core.ChannelAwareMessageListener;

public class DirectConsumerManual implements ChannelAwareMessageListener {
    @Override
    public void onMessage(Message message, Channel channel) throws Exception {
        String msg = new String(message.getBody(),"UTF-8");
        System.out.println("[DirectConsumerManual]消费者接收到:" + msg);
        //手动确认
        channel.basicAck(message.getMessageProperties().getDeliveryTag(), true);
    }
}

5.创建fanout模式消费者

  a.自动提交消费者,对应配置文件中 acknowledge="auto"

import com.rabbitmq.client.Channel;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.core.ChannelAwareMessageListener;

public class FanoutConsumerAuto implements ChannelAwareMessageListener {
    @Override
    public void onMessage(Message message, Channel channel) throws Exception {
        String msg = new String(message.getBody(),"UTF-8");
        System.out.println("[FanoutConsumerAuto]消费者接收到:" + msg);
    }
}

  b.手动提交消费者,对应配置文件中 acknowledge="manual"

import com.rabbitmq.client.Channel;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.core.ChannelAwareMessageListener;

public class FanoutConsumerManual implements ChannelAwareMessageListener {
    @Override
    public void onMessage(Message message, Channel channel) throws Exception {
        String msg = new String(message.getBody(),"UTF-8");
        System.out.println("[FanoutConsumerManual]消费者接收到:" + msg);
        //手动确认
        channel.basicAck(message.getMessageProperties().getDeliveryTag(), true);
    }
}

6.创建topic模式消费者

  a.自动提交消费者,对应配置文件中 acknowledge="auto"

import com.rabbitmq.client.Channel;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.core.ChannelAwareMessageListener;

public class TopicConsumerAuto implements ChannelAwareMessageListener {
    @Override
    public void onMessage(Message message, Channel channel) throws Exception {
        String msg = new String(message.getBody(),"UTF-8");
        System.out.println("[TopicConsumerAuto]消费者接收到:" + msg);
    }
}

  b.手动提交消费者,对应配置文件中 acknowledge="manual"

import com.rabbitmq.client.Channel;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.core.ChannelAwareMessageListener;

public class TopicConsumerManual implements ChannelAwareMessageListener {
    @Override
    public void onMessage(Message message, Channel channel) throws Exception {
        String msg = new String(message.getBody(),"UTF-8");
        System.out.println("[TopicConsumerManual]消费者接收到:" + msg);
        //手动确认
        channel.basicAck(message.getMessageProperties().getDeliveryTag(), true);
    }
}

7.测试

    public static void main(String[] args) {
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");
        CommonProducer commonProducer = (CommonProducer) applicationContext.getBean("commonProducer");
        commonProducer.send();
    }

猜你喜欢

转载自www.cnblogs.com/vettel0329/p/11649605.html