(四)RabbitMQ 官方的七种工作模式(含RPC模式)及可实操JAVA运行代码(2020最新)

官网介绍:https://www.rabbitmq.com/getstarted.html

(1) 简单模式:一个生产者,一个消费者

(2) work模式:一个生产者,多个消费者,每个消费者获取到的消息唯一。

(3) 订阅模式:一个生产者发送的消息会被多个消费者获取。

(4) 路由模式:发送消息到交换机并且要指定路由key ,消费者将队列绑定到交换机时需要指定路由key

(5) topic模式:将路由键和某模式进行匹配,此时队列需要绑定在一个模式上,“#”匹配一个词或多个词,“*”只匹配一个词

(6) RPC模式:使用RabbitMQ构建RPC系统:客户端和可伸缩RPC服务器。

(7)发布确认:与发布者进行可靠的发布确认

  

 

	<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-amqp</artifactId>
	</dependency>
spring.rabbitmq.host=127.0.0.1
spring.rabbitmq.port=5672
spring.rabbitmq.username=rabbitadmin
spring.rabbitmq.password=123456
spring.rabbitmq.virtual-host=/vhost_rabbitmq

 

public class ConnectionUitls {
	
	public static Connection getConnection() throws IOException, TimeoutException{
		//定义连接工厂
		ConnectionFactory factory = new ConnectionFactory();
		//设置连接地址
		factory.setHost("127.0.0.1");
		//AMQP 5672
		factory.setPort(5672);
		//vhost
		factory.setVirtualHost("/vhost_jetsen");
		//username
		factory.setUsername("rabbitadmin");
		//password
		factory.setPassword("123456");
		Connection newConnection = factory.newConnection();
		return newConnection;
	}
}

(1) 简单模式:一个生产者,一个消费者

package com.jetsen.mq.q1simplequeue;

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

import com.jetsen.mq.util.ConnectionUitls;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
//Java操作simple简单队列
//1.耦合性高,生产者一一对应,(如果想有多个消费者 ,不可行)
//2.队列名称变更,生产者、消费者需同时变更。
public class Sender {
	
	private static final String QUEUE_NAME="test_simple_queueu";
	
	public static void main(String[] args) throws IOException, TimeoutException {
		
		Connection connection = ConnectionUitls.getConnection();
		//获取通道
		Channel channel = connection.createChannel();
		//队列声明
		//String queue, boolean durable, boolean exclusive, boolean autoDelete,  Map<String, Object> arguments
		channel.queueDeclare(QUEUE_NAME, false, false, false, null);
		
		String message = "send message !";
		
		//String exchange, String routingKey, BasicProperties props, byte[] body
		channel.basicPublish("", QUEUE_NAME, null, message.getBytes());
		
		System.out.println("===>send msg:"+message);
		
		channel.close();
		
		connection.close();
	}
}

队列的申明

Queue.DeclareOk queueDeclare(String queue, boolean durable, boolean exclusive, boolean autoDelete,
                                 Map<String, Object> arguments) throws IOException;

queue:队列名称

durable:是否持久化, 队列的声明默认是存放到内存中的,如果rabbitmq重启会丢失,如果想重启之后还存在就要使队列持久化,保存到Erlang自带的Mnesia数据库中,当rabbitmq重启之后会读取该数据库。

exclusive:是否排外的,有两个作用,一:当连接关闭时connection.close()该队列是否会自动删除;二:该队列是否是私有的private,如果不是排外的,可以使用两个消费者都访问同一个队列,没有任何问题,如果是排外的,会对当前队列加锁,其他通道channel是不能访问的,如果强制访问会报异常

autoDelete:是否自动删除,当最后一个消费者断开连接之后队列是否自动被删除,可以通过RabbitMQ Management,查看某个队列的消费者数量,当consumers = 0时队列就会自动删除

arguments: 队列中的消息什么时候会自动被删除

 

 void basicPublish(String exchange, String routingKey, BasicProperties props, byte[] body) throws IOException;

package com.jetsen.mq.q1simplequeue;

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

import com.jetsen.mq.util.ConnectionUitls;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.DefaultConsumer;
import com.rabbitmq.client.Envelope;
import com.rabbitmq.client.AMQP.BasicProperties;
//Java操作simple简单队列
public class Recv {
	
	private static final String QUEUE_NAME="test_simple_queueu";

	
	public static void main(String[] args) throws IOException, TimeoutException {
		//获取连接
		Connection connection = ConnectionUitls.getConnection();
		//创建频道
		Channel channel = connection.createChannel();
		//QUEUE_NAME, durable, exclusive, autoDelete, arguments 队列声明
		channel.queueDeclare(QUEUE_NAME, false, false, false, null);
		//事件模型
		DefaultConsumer consumerCallback = new DefaultConsumer(channel){
			@Override
			public void handleDelivery(String consumerTag, Envelope envelope, BasicProperties properties, byte[] body)
					throws IOException {
				String messageStr = new String(body,"UTF-8");
				System.out.println("Consumer receive:=>"+messageStr);
			}
		};
		//String queue, boolean autoAck, Consumer callback
		channel.basicConsume(QUEUE_NAME,true, consumerCallback);
		
	}
}

 

(2) work模式:一个生产者,多个消费者,每个消费者获取到的消息唯一。

RabbitMq消费分发机制及主题消费分发(公平分发 + 轮训分发)

公平分发 :现在有2个消费者,我们使用basicQos( prefetchCount = 1)方法,来限制RabbitMQ只发不超过1条的消息给同一个消费者。当消息处理完毕后,有了反馈,才会进行第二次发送。

生产者每次发送间隔为20ms,消费者1消费间隔为2s,消费者2消费间隔为1s,代表消费者2消费能力高。

公平分发

//每个消费者,在发送【确认消息】之前,消息队列不发送下一个消息到消费者,一次只处理一个消息
//限制发送给同一个消费者不得超过1条消息
int prefetchCount = 1;
 channel.basicQos(prefetchCount);

//手动回执消息
channel.basicAck(envelope.getDeliveryTag(), false);

//公平队列将自动应答修改为false;
 boolean autoAck = false;
 channel.basicConsume(QUEUE_NAME,autoAck, consumer);

package com.jetsen.mq.q2workqueue.fairdispatch;

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

import com.jetsen.mq.util.ConnectionUitls;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
/**
 * 工作队列
 * 						  |----cl	 2s
 * p-----------queue------|
 * 						  |----c2	 1s
 */
public class Sender {
	
	private static final String QUEUE_NAME="test_work_queue";

	public static void main(String[] args) throws IOException, TimeoutException, InterruptedException {
		//获取连接
		Connection connection = ConnectionUitls.getConnection();
		
		//获取channel
		Channel channel = connection.createChannel();
		
		//申明队列String queue, boolean durable, boolean exclusive, boolean autoDelete,Map<String, Object> arguments
		channel.queueDeclare(QUEUE_NAME, false, false, false, null);
		
		//每个消费者,在发送【确认消息】之前,消息队列不发送下一个消息到消费者,一次只处理一个消息
		//限制发送给同一个消费者不得超过1条消息
		int prefetchCount = 1;
		channel.basicQos(prefetchCount);
		
		for (int i = 0; i < 50; i++) {
			String sendMsg = "Sender==>send message"+i;
			System.out.println("Sender==>send message"+i);
			channel.basicPublish("", QUEUE_NAME, null, sendMsg.getBytes());
			Thread.sleep(i*20);
		}
		
		channel.close();
		connection.close();
	}
}
package com.jetsen.mq.q2workqueue.fairdispatch;

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

import com.jetsen.mq.util.ConnectionUitls;
import com.rabbitmq.client.AMQP.BasicProperties;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.Consumer;
import com.rabbitmq.client.DefaultConsumer;
import com.rabbitmq.client.Envelope;
//work queues工作队列之Round-robin:消费者1、消费者2处理的消息一样多,进行的是轮询分发

public class Recv1 {
	private static final String QUEUE_NAME="test_work_queue";

	public static void main(String[] args) throws IOException, TimeoutException {
		//获取连接
		Connection connection = ConnectionUitls.getConnection();
		//创建频道
		Channel channel = connection.createChannel();
		
		//申明队列String queue, boolean durable, boolean exclusive, boolean autoDelete, Map<String, Object> arguments
		channel.queueDeclare(QUEUE_NAME, false, false, false, null);
		
		//保证一次只分发一个
		channel.basicQos(1);
		
		Consumer consumer = new DefaultConsumer(channel){
			@Override
			public void handleDelivery(String consumerTag, Envelope envelope, BasicProperties properties, byte[] body)
					throws IOException {
				String messageStr = new String(body,"UTF-8");
				System.out.println("Consumer[1] receive:=>"+messageStr);
				
				try {
					Thread.sleep(2000L);
				} catch (InterruptedException e) {
					e.printStackTrace();
				} finally{
					System.out.println("Consumer[1] done!");
					//手动回执消息
					channel.basicAck(envelope.getDeliveryTag(), false);
				}
			}
		};
		//String queue, boolean autoAck, Consumer callback
		
		//公平队列将自动应答修改为false;
		boolean autoAck = false;
		channel.basicConsume(QUEUE_NAME,autoAck, consumer);

	}
}

 

package com.jetsen.mq.q2workqueue.fairdispatch;

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

import com.jetsen.mq.util.ConnectionUitls;
import com.rabbitmq.client.AMQP.BasicProperties;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.Consumer;
import com.rabbitmq.client.DefaultConsumer;
import com.rabbitmq.client.Envelope;
//work queues工作队列之Round-robin:消费者1、消费者2处理的消息一样多,进行的是轮询分发

public class Recv2 {
	private static final String QUEUE_NAME="test_work_queue";

	public static void main(String[] args) throws IOException, TimeoutException {
		//获取连接
		Connection connection = ConnectionUitls.getConnection();
		//创建频道
		Channel channel = connection.createChannel();
		
		//申明队列String queue, boolean durable, boolean exclusive, boolean autoDelete, Map<String, Object> arguments
		channel.queueDeclare(QUEUE_NAME, false, false, false, null);
		
		//保证一次只分发一个
		channel.basicQos(1);
		
		Consumer consumer = new DefaultConsumer(channel){
			@Override
			public void handleDelivery(String consumerTag, Envelope envelope, BasicProperties properties, byte[] body)
					throws IOException {
				String messageStr = new String(body,"UTF-8");
				System.out.println("Consumer[2] receive:=>"+messageStr);
				
				try {
					Thread.sleep(1000L);
				} catch (InterruptedException e) {
					e.printStackTrace();
				} finally{
					System.out.println("Consumer[2] done!");
					//手动回执消息
					channel.basicAck(envelope.getDeliveryTag(), false);
				}
			}
		};
		//String queue, boolean autoAck, Consumer callback
		
		//公平队列将自动应答修改为false;
		boolean autoAck = false;
		channel.basicConsume(QUEUE_NAME,autoAck, consumer);

	}
}

轮训分发

手动改为自动回执消息 channel.basicAck(envelope.getDeliveryTag(), true)

自动应答修改为autoAck true   channel.basicConsume(QUEUE_NAME,true, consumer);

去掉流浪控制channel.basicQos(prefetchCount);

package com.jetsen.mq.q2workqueue.roundrobin;

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

import com.jetsen.mq.util.ConnectionUitls;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
/**
 * 工作队列
 * 						  |----cl	 2s
 * p-----------queue------|
 * 						  |----c2	 1s
 */
public class Sender {
	
	private static final String QUEUE_NAME="test_work_queue";

	public static void main(String[] args) throws IOException, TimeoutException, InterruptedException {
		//获取连接
		Connection connection = ConnectionUitls.getConnection();
		
		//获取channel
		Channel channel = connection.createChannel();
		
		//申明队列String queue, boolean durable, boolean exclusive, boolean autoDelete,Map<String, Object> arguments
		channel.queueDeclare(QUEUE_NAME, false, false, false, null);
		
		for (int i = 0; i < 50; i++) {
			String sendMsg = "Sender==>send message"+i;
			System.out.println("Sender==>send message"+i);
			channel.basicPublish("", QUEUE_NAME, null, sendMsg.getBytes());
			Thread.sleep(i*20);
		}
		
		channel.close();
		connection.close();
	}
}
package com.jetsen.mq.q2workqueue.roundrobin;

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

import com.jetsen.mq.util.ConnectionUitls;
import com.rabbitmq.client.AMQP.BasicProperties;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.Consumer;
import com.rabbitmq.client.DefaultConsumer;
import com.rabbitmq.client.Envelope;
//work queues工作队列之Round-robin:消费者1、消费者2处理的消息一样多,进行的是轮询分发

public class Recv1 {
	private static final String QUEUE_NAME="test_work_queue";

	public static void main(String[] args) throws IOException, TimeoutException {
		//获取连接
		Connection connection = ConnectionUitls.getConnection();
		//创建频道
		Channel channel = connection.createChannel();
		
		//申明队列String queue, boolean durable, boolean exclusive, boolean autoDelete, Map<String, Object> arguments
		channel.queueDeclare(QUEUE_NAME, false, false, false, null);
		
		
		Consumer consumer = new DefaultConsumer(channel){
			@Override
			public void handleDelivery(String consumerTag, Envelope envelope, BasicProperties properties, byte[] body)
					throws IOException {
				String messageStr = new String(body,"UTF-8");
				System.out.println("Consumer[1] receive:=>"+messageStr);
				
				try {
					Thread.sleep(2000L);
				} catch (InterruptedException e) {
					e.printStackTrace();
				} finally{
					System.out.println("Consumer[1] done!");
				}
			}
		};
		//String queue, boolean autoAck, Consumer callback
		channel.basicConsume(QUEUE_NAME,true, consumer);

	}
}
package com.jetsen.mq.q2workqueue.roundrobin;

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

import com.jetsen.mq.util.ConnectionUitls;
import com.rabbitmq.client.AMQP.BasicProperties;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.Consumer;
import com.rabbitmq.client.DefaultConsumer;
import com.rabbitmq.client.Envelope;
//work queues工作队列之Round-robin:消费者1、消费者2处理的消息一样多,进行的是轮询分发

public class Recv2 {
	private static final String QUEUE_NAME="test_work_queue";

	public static void main(String[] args) throws IOException, TimeoutException {
		//获取连接
		Connection connection = ConnectionUitls.getConnection();
		//创建频道
		Channel channel = connection.createChannel();
		
		//申明队列String queue, boolean durable, boolean exclusive, boolean autoDelete, Map<String, Object> arguments
		channel.queueDeclare(QUEUE_NAME, false, false, false, null);
		
		
		Consumer consumer = new DefaultConsumer(channel){
			@Override
			public void handleDelivery(String consumerTag, Envelope envelope, BasicProperties properties, byte[] body)
					throws IOException {
				String messageStr = new String(body,"UTF-8");
				System.out.println("Consumer[2] receive:=>"+messageStr);
				
				try {
					Thread.sleep(1000L);
				} catch (InterruptedException e) {
					e.printStackTrace();
				} finally{
					System.out.println("Consumer[2] done!");
				}
			}
		};
		//String queue, boolean autoAck, Consumer callback
		channel.basicConsume(QUEUE_NAME,true, consumer);

	}
}

 

(3) 订阅模式:一个生产者发送的消息会被多个消费者获取。

解读:
1、1个生产者,多个消费者
2、每一个消费者都有自己的一个队列
3、生产者没有将消息直接发送到队列,而是发送到了交换机
4、每个队列都要绑定到交换机
5、生产者发送的消息,经过交换机,到达队列,实现,一个消息被多个消费者获取的目的
注意:一个消费者队列可以有多个消费者实例,只有其中一个消费者实例会消费

 

package com.jetsen.mq.q3pubsub;

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

import com.jetsen.mq.util.ConnectionUitls;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
/**
 * publish_subscribe订阅模式
 * 
 * 
 * ""匿名转发: channel.basicPublish("", QUEUE_NAME, null, message.getBytes());
 * 
 * Exchange(一方面是接收生产者的消息,另一方面将消息推送至各自的队列)
 * 
 *
 * (1)fanout:(不处理路由键)
 * 				 		  |---queue1----c1   
 * p----exchange-Bindings-|
 * 						  |---queue2----c2	 

 */
public class Sender {
	
	private static final String EXCHANGE_NAME="test_exchage_fanout";

	public static void main(String[] args) throws IOException, TimeoutException {
		
		Connection connection = ConnectionUitls.getConnection();
		
		Channel channel = connection.createChannel();
		
		//声明交换机
		channel.exchangeDeclare(EXCHANGE_NAME, "fanout");//分发类型
		
		//发送消息
		String message = "send pub sub message ....";
		
		//exchange, routingKey, props, body
		channel.basicPublish(EXCHANGE_NAME, "", null, message.getBytes());
		
		System.out.println("Send message:"+message);
		
		channel.close();
		
		connection.close();
	}
}
package com.jetsen.mq.q3pubsub;

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

import com.jetsen.mq.util.ConnectionUitls;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.Consumer;
import com.rabbitmq.client.DefaultConsumer;
import com.rabbitmq.client.Envelope;
import com.rabbitmq.client.AMQP.BasicProperties;

public class Recv1 {

	private static final String EXCHANGE_NAME="test_exchage_fanout";

	private  static final String QUEUE_NAME="test_queue_fanout_email";
	
	public static void main(String[] args) throws IOException, TimeoutException {
		Connection connection = ConnectionUitls.getConnection();
		
		Channel channel = connection.createChannel();
		
		//queue, durable, exclusive, autoDelete, arguments
		channel.queueDeclare(QUEUE_NAME, false, false, false, null);
		
		//绑定队列到交换机
		channel.queueBind(QUEUE_NAME, EXCHANGE_NAME, "");
		
		//保证一次只分发一个
		channel.basicQos(1);
		
		Consumer consumer = new DefaultConsumer(channel){
			@Override
			public void handleDelivery(String consumerTag, Envelope envelope, BasicProperties properties, byte[] body)
					throws IOException {
				String messageStr = new String(body,"UTF-8");
				System.out.println("Consumer[1] receive:=>"+messageStr);
				
				try {
					Thread.sleep(2000L);
				} catch (InterruptedException e) {
					e.printStackTrace();
				} finally{
					System.out.println("Consumer[1] done!");
					//手动回执消息
					channel.basicAck(envelope.getDeliveryTag(), false);
				}
			}
		};
		//String queue, boolean autoAck, Consumer callback
		
		//公平队列将自动应答修改为false;
		boolean autoAck = false;
		channel.basicConsume(QUEUE_NAME,autoAck, consumer);
		
	}
}

 

package com.jetsen.mq.q3pubsub;

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

import com.jetsen.mq.util.ConnectionUitls;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.Consumer;
import com.rabbitmq.client.DefaultConsumer;
import com.rabbitmq.client.Envelope;
import com.rabbitmq.client.AMQP.BasicProperties;

public class Recv2 {

	private static final String EXCHANGE_NAME="test_exchage_fanout";

	private  static final String QUEUE_NAME="test_queue_fanout_msm";
	
	public static void main(String[] args) throws IOException, TimeoutException {
		Connection connection = ConnectionUitls.getConnection();
		
		Channel channel = connection.createChannel();
		
		//queue, durable, exclusive, autoDelete, arguments
		channel.queueDeclare(QUEUE_NAME, false, false, false, null);
		
		//绑定队列到交换机
		channel.queueBind(QUEUE_NAME, EXCHANGE_NAME, "");
		
		//保证一次只分发一个
		channel.basicQos(1);
		
		Consumer consumer = new DefaultConsumer(channel){
			@Override
			public void handleDelivery(String consumerTag, Envelope envelope, BasicProperties properties, byte[] body)
					throws IOException {
				String messageStr = new String(body,"UTF-8");
				System.out.println("Consumer[2] receive:=>"+messageStr);
				
				try {
					Thread.sleep(2000L);
				} catch (InterruptedException e) {
					e.printStackTrace();
				} finally{
					System.out.println("Consumer[2] done!");
					//手动回执消息
					channel.basicAck(envelope.getDeliveryTag(), false);
				}
			}
		};
		//String queue, boolean autoAck, Consumer callback
		
		//公平队列将自动应答修改为false;
		boolean autoAck = false;
		channel.basicConsume(QUEUE_NAME,autoAck, consumer);
		
	}
}

    //声明交换机
    channel.exchangeDeclare(EXCHANGE_NAME, "fanout");//分发类型 

(4) 路由模式:发送消息到交换机并且要指定路由key ,消费者将队列绑定到交换机时需要指定路由key

 

package com.jetsen.mq.q4routing;

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

import com.jetsen.mq.util.ConnectionUitls;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
/**
 * 
 * ""匿名转发: channel.basicPublish("", QUEUE_NAME, null, message.getBytes());
 * 
 * Exchange(一方面是接收生产者的消息,另一方面将消息推送至各自的队列)'
 * 
 * 
 * (2)Direct:(处理路由键)
 * 				 	type=direct	 	   |---queue1(routing key)----c1   
 * p(routing key)----exchange-Bindings-|
 * 						  			   |---queue2----c2	
 * 									   |
 * 									   |---queue3----c3	 
 */
public class Sender {
	
	private static final String EXCHANGE_NAME="test_exchange_direct";
	
	public static void main(String[] args) throws IOException, TimeoutException {
		Connection connection = ConnectionUitls.getConnection();
		
		Channel channel = connection.createChannel();
		
		//声明交换机
		channel.exchangeDeclare(EXCHANGE_NAME, "direct");//分发类型
		
		//发送消息
		String message = "send  direct message ....";
		
		String routingKey="error";
		
		channel.basicPublish(EXCHANGE_NAME, routingKey, null, message.getBytes());
		
		System.out.println("Send message:"+message);
		
		channel.close();
		
		connection.close();
	}
}

 

package com.jetsen.mq.q4routing;

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

import com.jetsen.mq.util.ConnectionUitls;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.Consumer;
import com.rabbitmq.client.DefaultConsumer;
import com.rabbitmq.client.Envelope;
import com.rabbitmq.client.AMQP.BasicProperties;

public class Recv1 {

	private static final String EXCHANGE_NAME = "test_exchange_direct";

	private static final String QUEUE_NAME = "test_queue_direct_1";

	public static void main(String[] args) throws IOException, TimeoutException {
		Connection connection = ConnectionUitls.getConnection();

		Channel channel = connection.createChannel();

		// queue, durable, exclusive, autoDelete, arguments
		channel.queueDeclare(QUEUE_NAME, false, false, false, null);

		String routingKey = "error";

		// 绑定队列到交换机
		channel.queueBind(QUEUE_NAME, EXCHANGE_NAME, routingKey);

		// 保证一次只分发一个
		channel.basicQos(1);

		Consumer consumer = new DefaultConsumer(channel) {
			@Override
			public void handleDelivery(String consumerTag, Envelope envelope, BasicProperties properties, byte[] body)
					throws IOException {
				String messageStr = new String(body, "UTF-8");
				System.out.println("Consumer[1] receive:=>" + messageStr);

				try {
					Thread.sleep(2000L);
				} catch (InterruptedException e) {
					e.printStackTrace();
				} finally {
					System.out.println("Consumer[1] done!");
					// 手动回执消息
					channel.basicAck(envelope.getDeliveryTag(), false);
				}
			}
		};

		// 公平队列将自动应答修改为false;
		boolean autoAck = false;
		channel.basicConsume(QUEUE_NAME, autoAck, consumer);
	}
}
package com.jetsen.mq.q4routing;

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

import com.jetsen.mq.util.ConnectionUitls;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.Consumer;
import com.rabbitmq.client.DefaultConsumer;
import com.rabbitmq.client.Envelope;
import com.rabbitmq.client.AMQP.BasicProperties;

public class Recv2 {

	private static final String EXCHANGE_NAME = "test_exchange_direct";

	private static final String QUEUE_NAME = "test_queue_direct_2";

	public static void main(String[] args) throws IOException, TimeoutException {
		Connection connection = ConnectionUitls.getConnection();

		Channel channel = connection.createChannel();

		// queue, durable, exclusive, autoDelete, arguments
		channel.queueDeclare(QUEUE_NAME, false, false, false, null);

		String routingKeyInfo = "info";
		String routingKeyWarning = "warning";
		String routingKeyError = "error";


		// 绑定队列到交换机
		channel.queueBind(QUEUE_NAME, EXCHANGE_NAME, routingKeyInfo);
		channel.queueBind(QUEUE_NAME, EXCHANGE_NAME, routingKeyWarning);
		channel.queueBind(QUEUE_NAME, EXCHANGE_NAME, routingKeyError);

		// 保证一次只分发一个
		channel.basicQos(1);

		Consumer consumer = new DefaultConsumer(channel) {
			@Override
			public void handleDelivery(String consumerTag, Envelope envelope, BasicProperties properties, byte[] body)
					throws IOException {
				String messageStr = new String(body, "UTF-8");
				System.out.println("Consumer[2] receive:=>" + messageStr);

				try {
					Thread.sleep(2000L);
				} catch (InterruptedException e) {
					e.printStackTrace();
				} finally {
					System.out.println("Consumer[2] done!");
					// 手动回执消息
					channel.basicAck(envelope.getDeliveryTag(), false);
				}
			}
		};

		// 公平队列将自动应答修改为false;
		boolean autoAck = false;
		channel.basicConsume(QUEUE_NAME, autoAck, consumer);
	}
}

由于Exchange绑定的两个队列中, Bingdings四个钟,有两个是订阅了errro的消息,所以两个消费者都可以消费到消息

(5) topic模式:将路由键和某模式进行匹配,此时队列需要绑定在一个模式上,“#”匹配一个词或多个词,“*”只匹配一个词

这里写图片描述

这里写图片描述

 

package com.jetsen.mq.q5topic;

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

import com.jetsen.mq.util.ConnectionUitls;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;

/**
 * 
 * ""匿名转发: channel.basicPublish("", QUEUE_NAME, null, message.getBytes());
 * 
 * Exchange(一方面是接收生产者的消息,另一方面将消息推送至各自的队列)'
 * 
 * 
 * (3)topic:(topic主题模式)
 * 		type=topic	 	   			   |---queue1----c1   
 * p----exchange-Bindings--------------|
 * 						  			   |---queue2----c2	
 * 									   |
 * 									   |---queue3----c3	 
 * 将路由和某个模式匹配(符号) *匹配一个  #匹配多个
 */
public class Sender {

	private static final String EXCHANGE_NAME="test_exchange_topic";

	public static void main(String[] args) throws IOException, TimeoutException {
		
		Connection connection = ConnectionUitls.getConnection();
		
		Channel channel = connection.createChannel();
		
		channel.exchangeDeclare(EXCHANGE_NAME, "topic");
		
		String msgString="商品....";
		
		channel.basicPublish(EXCHANGE_NAME, "goods.add", null, msgString.getBytes());
		
		System.out.println("----send" +msgString);
		
		channel.close();
		
		connection.close();
	}
}

 

package com.jetsen.mq.q5topic;

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

import com.jetsen.mq.util.ConnectionUitls;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.Consumer;
import com.rabbitmq.client.DefaultConsumer;
import com.rabbitmq.client.Envelope;
import com.rabbitmq.client.AMQP.BasicProperties;

public class Recv1 {

	private static final String EXCHANGE_NAME = "test_exchange_topic";

	private static final String QUEUE_NAME = "test_queue_topic_1";

	public static void main(String[] args) throws IOException, TimeoutException {
		Connection connection = ConnectionUitls.getConnection();

		Channel channel = connection.createChannel();

		// queue, durable, exclusive, autoDelete, arguments
		channel.queueDeclare(QUEUE_NAME, false, false, false, null);

		String routingKey = "goods.add";

		// 绑定队列到交换机
		channel.queueBind(QUEUE_NAME, EXCHANGE_NAME, routingKey);

		// 保证一次只分发一个
		channel.basicQos(1);

		Consumer consumer = new DefaultConsumer(channel) {
			@Override
			public void handleDelivery(String consumerTag, Envelope envelope, BasicProperties properties, byte[] body)
					throws IOException {
				String messageStr = new String(body, "UTF-8");
				System.out.println("Consumer[2] receive:=>" + messageStr);

				try {
					Thread.sleep(2000L);
				} catch (InterruptedException e) {
					e.printStackTrace();
				} finally {
					System.out.println("Consumer[2] done!");
					// 手动回执消息
					channel.basicAck(envelope.getDeliveryTag(), false);
				}
			}
		};

		// 公平队列将自动应答修改为false;
		boolean autoAck = false;
		channel.basicConsume(QUEUE_NAME, autoAck, consumer);
	}
}
package com.jetsen.mq.q5topic;

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

import com.jetsen.mq.util.ConnectionUitls;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.Consumer;
import com.rabbitmq.client.DefaultConsumer;
import com.rabbitmq.client.Envelope;
import com.rabbitmq.client.AMQP.BasicProperties;

public class Recv2 {

	private static final String EXCHANGE_NAME = "test_exchange_topic";

	private static final String QUEUE_NAME = "test_queue_topic_2";

	public static void main(String[] args) throws IOException, TimeoutException {
		Connection connection = ConnectionUitls.getConnection();

		Channel channel = connection.createChannel();

		// queue, durable, exclusive, autoDelete, arguments
		channel.queueDeclare(QUEUE_NAME, false, false, false, null);

		String routingKey = "goods.#";

		// 绑定队列到交换机
		channel.queueBind(QUEUE_NAME, EXCHANGE_NAME, routingKey);

		// 保证一次只分发一个
		channel.basicQos(1);

		Consumer consumer = new DefaultConsumer(channel) {
			@Override
			public void handleDelivery(String consumerTag, Envelope envelope, BasicProperties properties, byte[] body)
					throws IOException {
				String messageStr = new String(body, "UTF-8");
				System.out.println("Consumer[1] receive:=>" + messageStr);

				try {
					Thread.sleep(2000L);
				} catch (InterruptedException e) {
					e.printStackTrace();
				} finally {
					System.out.println("Consumer[1] done!");
					// 手动回执消息
					channel.basicAck(envelope.getDeliveryTag(), false);
				}
			}
		};

		// 公平队列将自动应答修改为false;
		boolean autoAck = false;
		channel.basicConsume(QUEUE_NAME, autoAck, consumer);
	}
}

 

       String routingKey = "goods.#";

        // 绑定队列到交换机
        channel.queueBind(QUEUE_NAME, EXCHANGE_NAME, routingKey);

(6) RPC模式:使用RabbitMQ构建RPC系统:客户端和可伸缩RPC服务器。

RPC的处理流程

  • 当客户端启动时,创建一个匿名的回调队列。
  • 客户端为RPC请求设置2个属性:replyTo,设置回调队列名字;correlationId,标记request。
  • 请求被发送到请求队列中。
  • RPC服务器端监听请求队列中的请求,当请求到来时,服务器端会处理并且把带有结果的消息发送给客户端。接收的队列就是replyTo设定的回调队列。
  • 客户端监听回调队列,当有消息时,检查correlationId属性,如果与request中匹配,则返回。

package com.jetsen.mq.q11.rpc;

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

import com.jetsen.mq.util.ConnectionUitls;
import com.rabbitmq.client.AMQP;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.DefaultConsumer;
import com.rabbitmq.client.Envelope;
import com.rabbitmq.client.AMQP.BasicProperties;

public class RpcClient {

	private static final String RPC_QUEUE_NAME = "test_rpc_queueu";

	public static void main(String[] args) {
		Connection connection = null;
		// 获取通道
		Channel channel = null;
		try {
			connection = ConnectionUitls.getConnection();
			// 获取通道
			channel = connection.createChannel();
			// 创建回调队列
			String callBackQueue = channel.queueDeclare().getQueue();
			// 创建带有correlationId的消息属性
			String correlationId = UUID.randomUUID().toString();
			AMQP.BasicProperties basicProperties = new AMQP.BasicProperties().builder().correlationId(correlationId)
					.replyTo(callBackQueue).build();
			// 消费者从回调队列中接受服务端传递的消息
			DefaultConsumer consumer = new DefaultConsumer(channel) {
				@Override
				public void handleDelivery(String consumerTag, Envelope envelope, BasicProperties properties,
						byte[] body) throws IOException {
					String recvcorrelationId = properties.getCorrelationId();
					if (correlationId.equals(recvcorrelationId)) {
						System.out.println("客户单接收消息:" + new String(body) + ",correlationId=" + correlationId);
					}
				}
			};
			// 监听队列
			channel.basicConsume(callBackQueue, true, consumer);
			String message = "rpc rabbitmq";
			channel.basicPublish("", RPC_QUEUE_NAME, basicProperties, message.getBytes());
			System.out.println("客户端发送消息:" + message + ",correlationId=" + correlationId);

		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} finally {
			
		}
	}
}
package com.jetsen.mq.q11.rpc;

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

import com.jetsen.mq.util.ConnectionUitls;
import com.rabbitmq.client.AMQP;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.DefaultConsumer;
import com.rabbitmq.client.Envelope;
import com.rabbitmq.client.AMQP.BasicProperties;

public class RpcServer {
	private static final String RPC_QUEUE_NAME="test_rpc_queueu";

	public static void main(String[] args) {
		try {
			Connection connection = ConnectionUitls.getConnection();
			//获取通道
			Channel channel = connection.createChannel();
			//创建队列
			channel.queueDeclare(RPC_QUEUE_NAME,false,false,false,null);
			
			//申明消费者预取数量
			channel.basicQos(1);
			//创建消费者
			// 消费者从队列中接受服务端传递的消息
			DefaultConsumer consumer = new DefaultConsumer(channel) {
				@Override
				public void handleDelivery(String consumerTag, Envelope envelope, BasicProperties properties,
						byte[] body) throws IOException {
					System.out.println("服务端接收消息:" + new String(body));
					channel.basicAck(envelope.getDeliveryTag(), false);
					
					String correlationId = properties.getCorrelationId();
					AMQP.BasicProperties basicProperties = new AMQP.BasicProperties().builder()
							.correlationId(correlationId)
							.build();
					channel.basicPublish("", properties.getReplyTo(), basicProperties, "RPC SERVER:11111111".getBytes());
				}
			};
			// 监听队列
			channel.basicConsume(RPC_QUEUE_NAME, false, consumer);
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (TimeoutException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} 
	}

}

 

(7)发布确认:与发布者进行可靠的发布确认

Confirm发送方确认模式使用和事务类似,也是通过设置Channel进行发送方确认的。

package com.jetsen.mq.q7confirm;

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

import com.jetsen.mq.util.ConnectionUitls;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.DefaultConsumer;
import com.rabbitmq.client.Envelope;
import com.rabbitmq.client.AMQP.BasicProperties;

public class Recv {

	private static final String QUEUE_NAME = "test_queue_confirm1";

	public static void main(String[] args) throws IOException, TimeoutException {
		Connection connection = ConnectionUitls.getConnection();
		Channel channel = connection.createChannel();
		//String queue, boolean autoAck, Consumer callback
		channel.basicConsume(QUEUE_NAME,true, new DefaultConsumer(channel){
			@Override
			public void handleDelivery(String consumerTag, Envelope envelope, BasicProperties properties, byte[] body)
					throws IOException {
				String messageStr = new String(body,"UTF-8");
				System.out.println("Consumer[test_queue_confirm1] receive:=>"+messageStr);
			}
		});
		
	}
}

Confirm的三种实现方式:

方式一:channel.waitForConfirms()普通发送方确认模式;

package com.jetsen.mq.q7confirm;

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

import com.jetsen.mq.util.ConnectionUitls;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;


/**
 * rabbitmq消息确认机制之confirm
 */
public class ConfirmSender {

	
	private static final String QUEUE_NAME = "test_queue_confirm1";

	
	public static void main(String[] args) throws IOException, TimeoutException, InterruptedException {
		
		Connection connection = ConnectionUitls.getConnection();
		
		Channel channel = connection.createChannel();
		
		channel.queueDeclare(QUEUE_NAME, false, false, false, null);
		
		String msgString =" send confirm message";
		
		//生产者调用将channel设为confirm模式,注意
		channel.confirmSelect();
		channel.basicPublish("", QUEUE_NAME, null, msgString.getBytes());
		System.out.println("----send" +msgString);
		
		if(!channel.waitForConfirms()){
			System.out.println("send confirm failed");
		}else{
			System.out.println("send confirm success");
		}
		channel.close();
		connection.close();
	}
}

我们只需要在推送消息之前,channel.confirmSelect()声明开启发送方确认模式,再使用channel.waitForConfirms()等待消息被服务器确认即可。

方式二:channel.waitForConfirmsOrDie()批量确认模式;

package com.jetsen.mq.q7confirm;

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

import com.jetsen.mq.util.ConnectionUitls;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;


/**
 * rabbitmq消息确认机制之confirm
 */
public class ConfirmBatchSender {

	
	private static final String QUEUE_NAME = "test_queue_confirm1";

	
	public static void main(String[] args) throws IOException, TimeoutException, InterruptedException {
		
		Connection connection = ConnectionUitls.getConnection();
		
		Channel channel = connection.createChannel();
		
		channel.queueDeclare(QUEUE_NAME, false, false, false, null);
		
		String msgString =" send confirm message";
		
		//生产者调用将channel设为confirm模式,注意
		channel.confirmSelect();
		
		for (int i = 0; i < 10; i++) {
			channel.basicPublish("", QUEUE_NAME, null, msgString.getBytes());
		}
		
//		if(!channel.waitForConfirms()){
//			System.out.println("send confirm failed");
//		}else{
//			System.out.println("send confirm success");
//		}
		channel.waitForConfirmsOrDie(); //直到所有信息都发布,只要有一个未确认就会IOException
		channel.close();
		connection.close();
	}
}

以上代码可以看出来channel.waitForConfirmsOrDie(),使用同步方式等所有的消息发送之后才会执行后面代码,只要有一个消息未被确认就会抛出IOException异常。

方式三:channel.addConfirmListener()异步监听发送方确认模式;

package com.jetsen.mq.q7confirm;

import java.io.IOException;
import java.util.Collections;
import java.util.SortedSet;
import java.util.TreeSet;
import java.util.concurrent.TimeoutException;

import com.jetsen.mq.util.ConnectionUitls;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.ConfirmListener;
import com.rabbitmq.client.Connection;

/**
 * 异步模式
 * Channel 对象提供的ConfirmListener()回调方法之包含deliveryTag(当前Channel发出
 * 的消息序号),我们需要自己为每一个Channel维护一个unconfirm的消息序号集合,每publish
 * 一条数据,集合中元素就+1,每回调一次handlerAck方法,unconfirm结合删掉相应的一条(multiple=false)
 * 或者多条(multiple=true)记录,从程序运行效率上看,这个unconfirm集合最好采用有序集合
 * SortedSet存储结构
 * @author jetsen
 *
 */
public class ConfirmAysnSender {
	
	
	private static final String QUEUE_NAME = "test_queue_confirm3";

	
	public static void main(String[] args) throws IOException, TimeoutException {
		
		Connection connection = ConnectionUitls.getConnection();
		
		Channel channel = connection.createChannel();
		
		channel.queueDeclare(QUEUE_NAME, false, false, false, null);
		
		//生产者调用confirmSelect将channel设置为Confirm模式
		channel.confirmSelect();
		
		//未确认消息的标识
		final SortedSet<Long> confirmSet=Collections.synchronizedSortedSet(new TreeSet<Long>());
		
		channel.addConfirmListener(new ConfirmListener() {
			
			@Override
			public void handleNack(long deliveryTag, boolean multiple) throws IOException {
				if(multiple){
					System.out.println("----handlerNack-----multiple");
					confirmSet.headSet(deliveryTag+1).clear();
				}else{
					System.out.println("----handlerNack-----multiple----false");
					confirmSet.remove(deliveryTag);
				}
			}
			
			@Override
			public void handleAck(long deliveryTag, boolean multiple) throws IOException {
				if(multiple){
					System.out.println("----handleAck-----multiple");
					confirmSet.headSet(deliveryTag+1).clear();
				}else{
					System.out.println("----handleAck-----multiple----false");
					confirmSet.remove(deliveryTag);
				}
			}
		});
		
		String msgString =" send confirm Aysn message";
		while(true){
			long segNo = channel.getNextPublishSeqNo();
			channel.basicPublish("", QUEUE_NAME, null, msgString.getBytes());
			confirmSet.add(segNo);
			
		}
	}
}

异步模式的优点,就是执行效率高,不需要等待消息执行完,只需要监听消息即可。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章