rabbitMQ的使用

本文深入讲解RabbitMQ的原理与使用,包括AMQP协议、核心概念如生产者、消费者、交换器等,以及如何通过Java API构建消息队列。探讨了消息确认机制、核心参数配置,并提供了实际代码示例。

rabbitMQ的使用

      

协议

      AMQP(Advanced Message Queuing Protocol),一个提供统一消息服务的应用层标准高级消息队列协议,是应用层协议的一个开放标准,为面向消息的中间件设计。基于此协议的客户端与消息中间件可传递消息,并不受客户端/中间件不同产品,不同的开发语言等条件的限制。Erlang中的实现有 RabbitMQ等。。。。好吧,这段话有些不舒服,简单来说,就是将JMS topic和P2P模式进行复制并整合为一套全新的框架,更灵活且更可靠,同时拥有成熟的集群方案。

相关概念
  1. 生产者(Producer):向Exchange发布消息的应用。

  2. 消费者(Consumer):从消息队列中消费消息的应用。

  3. 消息队列(Message Queue):服务器组件,用于保存消息,直到发送给消费者。

  4. 消息(Message):传输的内容。

  5. 交换器(exchange):路由组件,接收Producer发送的消息,并将消息路由转发给消息队列。

  6. 虚拟主机(Virtual Host): 一批交换器,消息队列和相关对象。虚拟主机是共享相同身份认证和加密环境的独立服务器域。

  7. Broker :AMQP的服务端称为Broker。

  8. 连接(Connection):一个网络连接,比如TCP/IP套接字连接。

  9. 信道(Channel):多路复用连接中的一条独立的双向数据流通道,为会话提供物理传输介质。

  10. 绑定器(Binding):消息队列和交换器直接的关联。

通信过程
  1. 建立连接Connection。由producer和consumer创建连接,连接到broker的物理节点上。

  2. 建立消息Channel。Channel是建立在Connection之上的,一个Connection可以建立多个Channel。producer连接Virtual Host 建立Channel,Consumer连接到相应的queue上建立Channel。

  3. 发送消息。由Producer发送消息到Broker中的exchange中。

  4. 路由转发。exchange收到消息后,根据一定的路由策略,将消息转发到相应的queue中去。

  5. 消息接收。Consumer会监听相应的queue,一旦queue中有可以消费的消息,queue就将消息发送给Consumer端。

  6. 消息确认。当Consumer完成某一条消息的处理之后,需要发送一条ACK消息给对应的Queue。Queue收到ACK信息后,才会认为消息处理成功,并将消息从Queue中移除;如果在对应的Channel断开后,Queue没有收到这条消息的ACK信息,该消息将被发送给另外的Channel。 至此一个消息的发送接收流程走完了。消息的确认机制提高了通信的可靠性。

在这里插入图片描述

核心参数
  • mandatory

    为true时,如果exchange根据自身类型和消息routeKey无法找到一个符合条件的queue,那么会调用basic.return方法将消息返回给生产者,要求有当前routekey

  • immediate

    为true时,如果exchange在将消息路由到queue(s)时发现对应的queue上没有消费者,那么会调用basic.return方法返还给生产者,要求当前routekey有消费者,不好用

  • durable

    在服务器重启时,能够存活

  • exclusive

    是否为当前连接的专用队列,在连接断开后,会自动删除该队列,生产环境中很少用到

  • autodelete

    当没有任何消费者使用时,自动删除该队列。

  • prefetchCount

    会告诉RabbitMQ不要同时给一个消费者推送多于N个消息,即一旦有N个消息还没有ack,则该consumer将block掉,直到有消息ack

  • BasicProperties props

    是否支持事物

一分钟构建JavaAPI

pom.xml

<dependency>
  <groupId>com.rabbitmq</groupId>
  <artifactId>amqp-client</artifactId>
  <version>3.5.7</version>
</dependency>

MQConfig.java



public class MQConfig {
	private static String MQ_IP_NODE;
    private static int MQ_PORT_NODE;
    
    private static String USER_NAME;
    private static String PASSWORD;
    
    static{
    	MQ_IP_NODE = "192.168.88.3";
    	MQ_PORT_NODE = 5672;
    	USER_NAME = "dev";
    	PASSWORD = "yanfa";
    }

	public static String getMQ_IP_NODE() {
		return MQ_IP_NODE;
	}

	public static int getMQ_PORT_NODE() {
		return MQ_PORT_NODE;
	}

	public static String getUSER_NAME() {
		return USER_NAME;
	}

	public static String getPASSWORD() {
		return PASSWORD;
	}

    
}


MQSource



import java.io.IOException;
import java.util.concurrent.TimeoutException;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import com.zgg.group2.common.pool.ThreadPoolFactory;
import com.zgg.group2.common.util.ExceptionUtil;
import com.zgg.group2.scheduler.mq.config.MQConfig;

public class MQSource {
	private final static Logger logger = LoggerFactory.getLogger(MQSource.class);
	private static MQSource mqSource = null;
	private static ConnectionFactory factory = null;
	private static Connection connection = null;
	private static Channel channel = null;
	static {
		init();
	}
	
	public static MQSource getInstance() {
		if (mqSource == null) {
			synchronized (MQSource.class) {
				if (mqSource == null) 
					return mqSource = new MQSource();
			}
		}
		return mqSource;
	}
	
	private static void init(){
		factory = new ConnectionFactory();
		factory.setHost(MQConfig.getMQ_IP_NODE());
		factory.setPort(MQConfig.getMQ_PORT_NODE());
		factory.setUsername(MQConfig.getUSER_NAME());
		factory.setPassword(MQConfig.getPASSWORD());
		factory.setShutdownTimeout(10000);
		factory.setSharedExecutor(ThreadPoolFactory.getInstance().getPool());
		try {
			connection = factory.newConnection();
			channel = connection.createChannel();
		} catch (IOException e) {
			logger.error("rabbitmq factory created failed.", ExceptionUtil.exceptionStack(e));
		} catch (TimeoutException e) {
			logger.error("rabbitmq factory created failed.", ExceptionUtil.exceptionStack(e));
		}
	}
	
	private MQSource() {
		super();
	}
	
	public Channel getChannel() {
		return channel;
	}

	public Connection getConnection() {
		return connection;
	}

	public void destory() {
		try {
			channel.close();
			connection.close();
		} catch (IOException | TimeoutException e) {
			logger.error("rabbitmq channel abort failed.", ExceptionUtil.exceptionStack(e));
		}
	}
}


MQPubHandler.java



import java.io.IOException;

import com.rabbitmq.client.Channel;
import com.rabbitmq.client.MessageProperties;
import com.rabbitmq.client.ReturnListener;
import com.zgg.group2.scheduler.mq.factory.MQSource;

public class MQPubHandler {
	private static Channel channel = null;
	
	static {
		try {
			channel = MQSource.getInstance().getConnection().createChannel();
			//queue模式-向两个固定的queue发送消息, 由producer定义queue
			channel.exchangeDeclare("exchangename1", "direct", true);
			//topic模式-向绑定了当前exchange的所有queue发送消息, 由consumer定义queue
			channel.exchangeDeclare("exchangename2", "fanout", true);

	        
			channel.queueDeclare("queuename 1", true, false, false, null);
			channel.queueDeclare("queuename 2", true, false, false, null);

			channel.queueBind("queuename 1", "exchangename1", "route key 1", null);
			channel.queueBind("queuename 2", "exchangename1", "route key 1", null);

			channel.basicQos(1);
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	
	public static void main(String[] args) {
		//向topic发送消息
//		publishToQueueOrExchange("exchangename2", "route key 1", "message", true, false);
		//向queue发送消息
//		publishToQueueOrExchange("exchangename1", "route key 1", "message", true, false);
		//向topic发送消息
//		publishToQueueOrExchangeByPersistent("exchangename2", "route key 1", "message", true, false);
		//向queue发送消息
		publishToQueueOrExchangeByPersistent("exchangename1", "route key 1", "message1", true, false);
	}
	/**
	 * @desp 向queue/topic发送消息
	 * @param queueName
	 * @param message
	 */
	public static void publishToQueueOrExchange(String exchangeName,String routingKey, String message, boolean mandatory, boolean immediate){
		try {
			channel.addReturnListener(new ReturnListener() {
				@Override
				public void handleReturn(int replyCode, String replyText, String exchange, String routingKey, com.rabbitmq.client.AMQP.BasicProperties basicProperties, byte[] body) 
						throws IOException {
					String message = new String(body);
					System.out.println("Basic.return返回的结果是:"+message);
				}
			});
			channel.basicPublish(exchangeName, routingKey, mandatory, immediate, null, message.getBytes());
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	/**
	 * @desp 向queue发送消息
	 * @param queueName
	 * @param message
	 */
	public static void publishToQueueOrExchangeByPersistent(String exchangeName,String routingKey, String message, boolean mandatory, boolean immediate){
		try {
			channel.addReturnListener(new ReturnListener() {
				@Override
				public void handleReturn(int replyCode, String replyText, String exchange, String routingKey, com.rabbitmq.client.AMQP.BasicProperties basicProperties, byte[] body) 
						throws IOException {
					String message = new String(body);
					System.out.println("Basic.return返回的结果是:"+message);
					System.out.println(replyCode);
					System.out.println(replyText);
					System.out.println(exchange);
					System.out.println(routingKey);
					System.out.println(basicProperties);
				}
			});
			channel.txSelect();
			channel.basicPublish(exchangeName, routingKey, mandatory, immediate, MessageProperties.PERSISTENT_TEXT_PLAIN, message.getBytes());
			channel.txCommit();
		} catch (IOException e) {
			try {
				channel.txRollback();
			} catch (IOException e1) {
				e.printStackTrace();;
			}
		}
	}
}


MQSubHandler.java



import java.io.IOException;

import com.rabbitmq.client.AMQP;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Consumer;
import com.rabbitmq.client.DefaultConsumer;
import com.rabbitmq.client.Envelope;
import com.zgg.group2.scheduler.mq.factory.MQSource;

public class MQSubHandler {
	private static Channel channel = null;
	
	static {
		try {
			channel = MQSource.getInstance().getConnection().createChannel();
			//queue模式-向两个固定的queue发送消息, 由producer定义queue
			channel.exchangeDeclare("exchangename1", "direct", true);
			//topic模式-向绑定了当前exchange的所有queue发送消息, 由consumer定义queue
			channel.exchangeDeclare("exchangename2", "fanout", true);
			
			channel.queueDeclare("queuename 1", true, false, false, null);
			channel.queueDeclare("queuename 2", true, false, false, null);

			channel.queueBind("queuename 1", "exchangename1", "route key 1", null);
			channel.queueBind("queuename 2", "exchangename1", "route key 1", null);

			channel.basicQos(1);
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	public static void main(String[] args) {
		//消费topic(也是在消费queue)
//		consumeFromQueueWithExchange("exchangename2", "route key 1");
		//消费queue
		consumeFromQueue("queuename 2");
		consumeFromQueue("queuename 1");
		
	}
	
	/**
	 * @desp 消费queue
	 * @param queueName
	 */
	public static void consumeFromQueue(String queueName) {
		try {
			Consumer consumer = new DefaultConsumer(channel) {
				@Override
				public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
					String message = new String(body, "UTF-8");
					doWork(message);
					//手动回复给mq-server业务处理完毕可以清除,basicConsume(,false,)
//					channel.basicAck(envelope.getDeliveryTag(), false);
					//拒绝消息
//					channel.basicReject(envelope.getDeliveryTag(), true);
				}
			};
			channel.basicConsume(queueName, true, consumer);
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	private static void doWork(String message){
		System.out.println(message + " [x] Done");
		try {
			Thread.sleep(3000);		//每3秒收一'条'消息
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}
	
	/**
	 * @desp 从订阅的topic获取消息
	 * @param exchangeName
	 * @param routingKey
	 */
	public static void consumeFromQueueWithExchange(String exchangeName, String routingKey) {
		try {
			//随机queue
			String queueName = channel.queueDeclare().getQueue();
			channel.queueBind(queueName, exchangeName, routingKey);
			
			Consumer consumer = new DefaultConsumer(channel) {
				@Override
				public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
					String message = new String(body, "UTF-8");
					System.out.println(" [x] Received '" + message + "'");
					String exchangeName = envelope.getExchange();
					String routingKey = envelope.getRoutingKey();
					long deliveryTag = envelope.getDeliveryTag();
					System.out.println(consumerTag);
					System.out.println(exchangeName);
					System.out.println(routingKey);
					System.out.println(deliveryTag);
				}
			};
			channel.basicConsume(queueName, true, consumer);
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值