RabbitMQ简介和集成

简介

RabbitMQ 即一个消息队列,主要是用来实现应用程序的异步和解耦,同时也能起到消息缓冲,消息分发的作用。

RabbitMQ是实现AMQP(高级消息队列协议)的消息中间件的一种,AMQP,即Advanced Message Queuing Protocol, 高级消息队列协议,是应用层协议的一个开放标准,为面向消息的中间件设计。

在项目中,将一些无需即时返回且耗时的操作提取出来,进行了异步处理,而这种异步处理的方式大大的节省了服务器的请求响应时间, 提高了系统的吞吐量。

安装

由于rabbitmq是基于erlang语言的,所以必须安装运行erlang的环境才能运行rabbit的操作页面。

  1. 安装erlang
  2. 安装rabbitMQ
  3. 使用rabbitMQ的命令行运行rabbit
C:\Program Files (x86)\RabbitMQ Server\rabbitmq_server-3.4.1\sbin>rabbitmq-plugins enable rabbitmq_management
  1. 登录rabbitmq的操作页面端口号为15672
http://localhost:15672
  1. 以guest登录创建一个新的administrator用户
  2. 给administrator用户创建一个虚拟机
  3. 操作页面完成

安装注意事项:

  • 安装erlang和rabbit尽量安装到默认安装路径
  • 电脑的管理员名称和电脑主机名称必须是英文
  • 运行rabbit时需在’winhome’中找到rabbit的命令行窗口
  • 如果在winhome中找不到就到rabbit的sbin目录下以管理员的方式运行

java集成RabbitMQ

springmvc+RabbitMQ

pom.xml

        <dependency>
			<groupId>com.rabbitmq</groupId>
			<artifactId>amqp-client</artifactId>
			<version>3.4.1</version>
		</dependency>
		<dependency>
			<groupId>org.slf4j</groupId>
			<artifactId>slf4j-log4j12</artifactId>
			<version>1.7.7</version>
		</dependency>
		<dependency>
			<groupId>org.apache.commons</groupId>
			<artifactId>commons-lang3</artifactId>
			<version>3.3.2</version>
		</dependency>
		
		<dependency>
	        <groupId>org.springframework.amqp</groupId>
	        <artifactId>spring-rabbit</artifactId>
	        <version>1.4.0.RELEASE</version>
	    </dependency>

rabbitmq-context.xml

<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/rabbit
	http://www.springframework.org/schema/rabbit/spring-rabbit-1.4.xsd
	http://www.springframework.org/schema/beans
	http://www.springframework.org/schema/beans/spring-beans-4.1.xsd">

	<!-- 定义RabbitMQ的连接工厂 -->
	<rabbit:connection-factory id="connectionFactory"
		host="127.0.0.1" port="5672" username="taotao" password="taotao"
		virtual-host="/taotao" />

	<!-- 定义Rabbit模板,指定连接工厂以及定义exchange(发送给指定的change) -->
	<!-- <rabbit:template id="amqpTemplate" connection-factory="connectionFactory" exchange="fanoutExchange" /> -->
	<!-- 定义Rabbit模板,不指定exchange发送的时候在指定 -->
	<rabbit:template id="amqpTemplate" connection-factory="connectionFactory" />
	<!-- <rabbit:template id="amqpTemplate" connection-factory="connectionFactory"
		exchange="fanoutExchange" routing-key="foo.bar" /> -->

	<!-- MQ的管理,包括队列、交换器等 -->
	<rabbit:admin connection-factory="connectionFactory" />

	<!-- 定义队列,自动声明 -->
	<rabbit:queue name="myQueue" auto-declare="true"/>
	
	<!-- 定义交换器,自动声明 -->
	<rabbit:fanout-exchange name="fanoutExchange" auto-declare="true">
		<rabbit:bindings>
			<rabbit:binding queue="myQueue"/>
		</rabbit:bindings>
	</rabbit:fanout-exchange>
	
<!-- 	<rabbit:topic-exchange name="myExchange">
		<rabbit:bindings>
			<rabbit:binding queue="myQueue" pattern="foo.*" />
		</rabbit:bindings>
	</rabbit:topic-exchange> -->

	<!-- 队列监听 -->
	<rabbit:listener-container connection-factory="connectionFactory">
		<rabbit:listener ref="foo" method="listen" queue-names="myQueue" />
	</rabbit:listener-container>
	
	<!-- 
	    队列监听:配置需要继承ChannelAwareMessageListener的监听 
	    acknowledeg = "manual"   设置手动应答  当消息处理失败时:会一直重发  直到消息处理成功    
	-->
	<rabbit:listener-container connection-factory="connectionFactory" >
		<rabbit:listener ref="foo" method="listen" />
	</rabbit:listener-container>
	
	<bean id="foo" class="cn.itcast.rabbitmq.spring.Foo" />

</beans>
/**
 * 消费者(这种方式值负责接收没有手动提交机制)
 * @author zhijun
 *
 */
public class Foo {

    //具体执行业务的方法
    public void listen(String foo) {
        System.out.println("消费者: " + foo);
    }
}

/**
 * 消费者(手动提交)
 * 前提是需要在队列的监听器配置里面配置acknowledge="manual"
 * @author ginwu
 *
 */
public class Foo implements ChannelAwareMessageListener {

    /**
     * 消费者接受信息
     * @param message 接收到的消息
     * @param channel
     * @throws Exception
     */
    @Override
    public void onMessage(Message message, Channel channel) throws Exception {
        Foo foo = JSON.parseObject(message.getBody(), Foo.class);
        Thread.sleep(3000);
        System.out.println(foo+"=====Customer2");

        /**
         * 第一个参数 deliveryTag:就是接受的消息的deliveryTag,可以通过msg.getMessageProperties().getDeliveryTag()获得
         * 第二个参数 multiple:如果为true,确认之前接受到的消息;如果为false,只确认当前消息。如果为true就表示连续取得多条消息才
         *          发会确认,和计算机网络的中tcp协议接受分组的累积确认十分相似,能够提高效率
         */
        channel.basicAck(message.getMessageProperties().getDeliveryTag(),false);
//        channel.basicNack(message.getMessageProperties().getDeliveryTag(),false,false);
    }
}

import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.context.support.AbstractApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class SpringMain {
    public static void main(final String... args) throws Exception {
        AbstractApplicationContext ctx = new ClassPathXmlApplicationContext(
                "classpath:spring/rabbitmq-context.xml");
        //RabbitMQ模板
        RabbitTemplate template = ctx.getBean(RabbitTemplate.class);
        //发送消息
        // template.convertAndSend("Hello, world!");
        //发送给继承lister的监听器需要带上交换机名称
         template.convertAndSend("fanoutExchange","","订单");
        Thread.sleep(1000);// 休眠1秒
        ctx.destroy(); //容器销毁
    }
}

springboot+RabbitMQ

        <dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-amqp</artifactId>
		</dependency>
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-databind</artifactId>
            <version>2.9.6</version>
        </dependency>

spring:
  profiles: dev
  rabbitmq:
    host: 127.0.0.1
    port: 5672
    username: admin
    password: admin
    virtual-host: /ginwu
    publisher-confirms: true #支持发布确认
    publisher-returns: true  #支持发布返回
    listener:
      simple:
        acknowledge-mode: manual #采用手动应答
        concurrency: 1 #指定最小的消费者数量
        max-concurrency: 1 #指定最大的消费者数量
        retry:
          enabled: true #是否支持重试
package com.xncoding.pos.config;

import com.xncoding.pos.ExchangeOrQueueName;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.amqp.core.*;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.amqp.support.converter.Jackson2JsonMessageConverter;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import javax.annotation.Resource;

/**
 * RabbitConfig
 *
 * @author XiongNeng
 * @version 1.0
 * @since 2018/3/1
 */
@Configuration
@EnableRabbit
public class RabbitConfig {
    @Resource
    private RabbitTemplate rabbitTemplate;

    /**
     * 定制化amqp模版      可根据需要定制多个
     * <p>
     * <p>
     * 此处为模版类定义 Jackson消息转换器
     * ConfirmCallback接口用于实现消息发送到RabbitMQ交换器后接收ack回调   即消息发送到exchange  ack
     * ReturnCallback接口用于实现消息发送到RabbitMQ 交换器,但无相应队列与交换器绑定时的回调  即消息发送不到任何一个队列中  ack
     *
     * @return the amqp template
     */
    // @Primary
    @Bean
    public AmqpTemplate amqpTemplate() {
        Logger log = LoggerFactory.getLogger(RabbitTemplate.class);
        // 使用jackson 消息转换器
        rabbitTemplate.setMessageConverter(new Jackson2JsonMessageConverter());
        rabbitTemplate.setEncoding("UTF-8");
        // 消息发送失败返回到队列中,yml需要配置 publisher-returns: true
        rabbitTemplate.setMandatory(true);
        rabbitTemplate.setReturnCallback((message, replyCode, replyText, exchange, routingKey) -> {
            String correlationId = message.getMessageProperties().getCorrelationId();
            log.debug("消息:{} 发送失败, 应答码:{} 原因:{} 交换机: {}  路由键: {}", correlationId, replyCode, replyText, exchange, routingKey);
        });
        // 消息确认,yml需要配置 publisher-confirms: true
        rabbitTemplate.setConfirmCallback((correlationData, ack, cause) -> {
            if (ack) {
                log.debug("消息发送到exchange成功,id: {}", correlationData.getId());
            } else {
                log.debug("消息发送到exchange失败,原因: {}", cause);
            }
        });
        return rabbitTemplate;
    }

    /* ----------------------------------------------------------------------------Direct exchange test--------------------------------------------------------------------------- */

    /**
     * 声明Direct交换机 支持持久化.
     *
     * @return the exchange
     */
    @Bean("directExchange")
    public Exchange directExchange() {
        return ExchangeBuilder.directExchange(ExchangeOrQueueName.DIRECT_EXCHANGE).durable(true).build();
    }

    /**
     * 声明一个队列 支持持久化.
     *
     * @return the queue
     */
    @Bean("directQueue")
    public Queue directQueue() {
        return QueueBuilder.durable(ExchangeOrQueueName.DIRECT_QUEUE).build();
    }

    /**
     * 通过绑定键 将指定队列绑定到一个指定的交换机 .
     *
     * @param queue    the queue
     * @param exchange the exchange
     * @return the binding
     */
    @Bean
    public Binding directBinding(@Qualifier("directQueue") Queue queue,
                                 @Qualifier("directExchange") Exchange exchange) {
        return BindingBuilder.bind(queue).to(exchange).with("DIRECT_ROUTING_KEY").noargs();
    }

    /* ----------------------------------------------------------------------------Fanout exchange test--------------------------------------------------------------------------- */

    /**
     * 声明 fanout 交换机.
     *
     * @return the exchange
     */
    @Bean("fanoutExchange")
    public FanoutExchange fanoutExchange() {
        return (FanoutExchange) ExchangeBuilder.fanoutExchange(ExchangeOrQueueName.FANOUT_EXCHANGE).durable(true).build();
    }

    /**
     * Fanout queue A.
     *
     * @return the queue
     */
    @Bean("fanoutQueueA")
    public Queue fanoutQueueA() {
        return QueueBuilder.durable(ExchangeOrQueueName.FANOUT_QUEUE_A).build();
    }

    /**
     * Fanout queue B .
     *
     * @return the queue
     */
    @Bean("fanoutQueueB")
    public Queue fanoutQueueB() {
        return QueueBuilder.durable(ExchangeOrQueueName.FANOUT_QUEUE_B).build();
    }

    /**
     * 绑定队列A 到Fanout 交换机.
     *
     * @param queue          the queue
     * @param fanoutExchange the fanout exchange
     * @return the binding
     */
    @Bean
    public Binding bindingA(@Qualifier("fanoutQueueA") Queue queue,
                            @Qualifier("fanoutExchange") FanoutExchange fanoutExchange) {
        return BindingBuilder.bind(queue).to(fanoutExchange);
    }

    /**
     * 绑定队列B 到Fanout 交换机.
     *
     * @param queue          the queue
     * @param fanoutExchange the fanout exchange
     * @return the binding
     */
    @Bean
    public Binding bindingB(@Qualifier("fanoutQueueB") Queue queue,
                            @Qualifier("fanoutExchange") FanoutExchange fanoutExchange) {
        return BindingBuilder.bind(queue).to(fanoutExchange);
    }
}


package com.xncoding.pos.mq;

import com.rabbitmq.client.Channel;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;

import java.io.IOException;

/**
 * 消息监听器
 *
 * @author XiongNeng
 * @version 1.0
 * @since 2018/3/1
 */
@Component
public class Receiver {
    private static final Logger log = LoggerFactory.getLogger(Receiver.class);

    /**
     * FANOUT广播队列监听一.
     *,
     * @param message the message
     * @param channel the channel
     * @throws IOException the io exception  这里异常需要处理
     */
    @RabbitListener(queues = {"FANOUT_QUEUE_A"})
    public void on(Message message, Channel channel) throws IOException {
        channel.basicAck(message.getMessageProperties().getDeliveryTag(), true);
        log.debug("FANOUT_QUEUE_A " + new String(message.getBody()));
    }

    /**
     * FANOUT广播队列监听二.
     *
     * @param message the message
     * @param channel the channel
     * @throws IOException the io exception   这里异常需要处理
     */
    @RabbitListener(queues = {"FANOUT_QUEUE_B"})
    public void t(Message message, Channel channel) throws IOException {
        channel.basicAck(message.getMessageProperties().getDeliveryTag(), true);
        log.debug("FANOUT_QUEUE_B " + new String(message.getBody()));
    }

    /**
     * DIRECT模式.
     *
     * @param message the message
     * @param channel the channel
     * @throws IOException the io exception  这里异常需要处理
     */
    @RabbitListener(queues = {"DIRECT_QUEUE"})
    public void message(Message message, Channel channel) throws IOException {
        channel.basicAck(message.getMessageProperties().getDeliveryTag(), true);
        log.debug("DIRECT " + new String(message.getBody()));
    }
}

package com.xncoding.pos.service;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.amqp.rabbit.support.CorrelationData;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.UUID;

/**
 * 消息发送服务
 */
@Service
public class SenderService {
    private Logger logger = LoggerFactory.getLogger(this.getClass());
    @Resource
    private RabbitTemplate rabbitTemplate;

    /**
     * 测试广播模式.
     *
     * @param p the p
     * @return the response entity
     */
    public void broadcast(String p) {
        CorrelationData correlationData = new CorrelationData(UUID.randomUUID().toString());
        rabbitTemplate.convertAndSend("FANOUT_EXCHANGE", "", p, correlationData);
    }

    /**
     * 测试Direct模式.
     *
     * @param p the p
     * @return the response entity
     */
    public void direct(String p) {
        CorrelationData correlationData = new CorrelationData(UUID.randomUUID().toString());
        rabbitTemplate.convertAndSend("DIRECT_EXCHANGE", "DIRECT_ROUTING_KEY", p, correlationData);
    }

}


package com.xncoding.pos;

import com.xncoding.pos.service.SenderService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

/**
 * AfterStartRunner
 *
 * @author XiongNeng
 * @version 1.0
 * @since 2018/9/15
 */
@Component
@Order(1)
public class AfterStartRunner implements CommandLineRunner {
    @Autowired
    private SenderService senderService;
    @Override
    public void run(String... args) throws Exception {
        System.out.println("--------------start-------------");
        Thread.sleep(2000L);
        // 测试广播模式
        senderService.broadcast("AfterStartRunner --> 同学们集合啦!");
        // 测试Direct模式
        senderService.direct("AfterStartRunner --> 定点消息");
    }
}


package com.xncoding.service;

import com.xncoding.Application;
import com.xncoding.pos.service.SenderService;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;


/**
 * SenderServiceTest
 *
 * @author XiongNeng
 * @version 1.0
 * @since 2018/2/2
 */
@RunWith(SpringRunner.class)
@SpringBootTest(classes = Application.class)
public class SenderServiceTest {
    @Autowired
    private SenderService senderService;

    @Test
    public void testCache() {
        // 测试广播模式
        senderService.broadcast("同学们集合啦!");
        // 测试Direct模式
        senderService.direct("定点消息");
    }
}

手动确认机制
  1. 监听器实现ChannelAwareMessageListener接口
  2. 实现onMessage(Message message, Channel channel)方法
    3.channel.basicAck(message.getMessageProperties().getDeliveryTag(),false);手动确认
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值