RabbitMQ(一)基础和底层API使用

RabbitMQ核心概念及基础API应用 RabbitMQ核心概念及基础API应用 1 主流中间件介绍 衡量消息中间件的指标:服务性能,数据存储,集群架构。 1.ActiveMQ:Apache,支持JMS规范最完整的。 2.RocketMQ Apache,阿里:采用了Kafka设计理念、吞吐、高可用。 纯Java开放的软件。 蚂蚁金服集团涉及到银行。 金融、电商(淘宝)。 3.Kafka 高吞吐 100K/s。 缺点:不完全保证消息的可靠性(消息丢失)。 Strom、Flink(大数据流处理)。 性能为什么高? 采用page cache(radix 阅读详情

目录

一、消息队列

二、Rabbit MQ常用类、方法

三、简单消息队列模型

四、工作队列模型

1、轮询分发

2、公平分发

五、发布订阅模型(fanout)

六、路由模式(direct)

七、主题模式(topic)

八、rabbitMQ 生产者消息发送可达(事务机制)

九、rabbitMQ 生产者消息发送可达(confirm消息确认机制)

1、waitForConfirms【同步确认】

(1)消息消费者【这个消费者设置了topic模式后面的可以通用】

(2)消息生产者【单条发送】

(3)消息生产者【批量发送】

2、waitForConfirmsOrDie【同步确认】

(1)消息生产者【单条发送】

(2)消息生产者【批量发送】

3、addConfirmListener【异步监听确认】

(1)消息生产者【单条发送】

(2)消息生产者【批量发送】


一、消息队列

1、什么是消息队列MQ

MQ 是 Message Queue 的缩写,消息队列是应用程序和应用程序之间的通信方法。

RabbitMQ是一个开源的,在AMQP基础上完成的,可复用的企业消息系统。

AMQP: 即 Advanced Message Queuing Protocol,一个提供统一消息服务的应用层标准高级消息队列协议,是应用层协议的一个开放标准,为面向消息的中间件设计。基于此协议的客户端与消息中间件可传递消息,并不受客户端/中间件不同产品,不同的开发语言等条件的限制。Erlang中的实现有RabbitMQ等。

AMQP核心概念

  • Server:又称Broker,接收客户端的连接,实现AMQP实体服务
  • Connection:连接,应用程序与Broker的网络连接
  • Channel:网络信道,几乎所有的操作都在Channel中进行,包括定义Queue、定义Exchange、绑定Queue与Exchange、发布消息等。Channel是进行消息读写的通道。客户端可以建立多个Channel,每个Channel代表一个会话任务。
  • Message:消息,服务器和应用程序之间传送的数据,由Properties和Body组成。Properties可以对消息进行修饰,比如消息的优先级、延迟等高级特性;Body就是消息体内容。
  • Virtual host:虚拟地址,用于进行逻辑隔离,最上层的消息路由。一个Virtual host可以有若干个Exchange和Queue,同一个Virtual host里面不能有相同的Exchange和Queue
  • Exchange:交换机,接收消息,根据路由键转发消息到绑定的队列

RabbitMQ中有三种常用的交换机类型:
direct: 如果路由键匹配,消息就投递到对应的队列
fanout:投递消息给所有绑定在当前交换机上面的队列
topic:允许实现有趣的消息通信场景,使得5不同源头的消息能够达到同一个队列。topic队列名称有两个特殊的关键字。
* 可以替换一个单词
# 可以替换所有的单词

  • Binding:Exchange和Queue之间的虚拟连接,binding中可以包含routing key
  • Routing key:一个路由规则,虚拟机可用它来确定如何路由一个特定消息
  • Queue:也称为Message Queue,消息队列,保存消息并将它们转发给消费者,多个消费者可以订阅同一个Queue,这时Queue中的消息会被平均分摊给多个消费者进行处理,而不是每个消费者都收到所有的消息并处理。
  • Prefetch count:如果有多个消费者同时订阅同一个Queue中的消息,Queue中的消息会被平摊给多个消费者。这时如果每个消息的处理时间不同,就有可能会导致某些消费者一直在忙,而另外一些消费者很快就处理完手头工作并一直空闲的情况。我们可以通过设置prefetchCount来限制Queue每次发送给每个消费者的消息数,比如我们设置prefetchCount=1,则Queue每次给每个消费者发送一条消息;消费者处理完这条消息后Queue会再给该消费者发送一条消息。

2、为什么要使用消息队列?

(1)解耦

传统模式:

传统模式的缺点:

  • 系统间耦合性太强,如上图所示,系统A在代码中直接调用系统B和系统C的代码,如果将来D系统接入,系统A还需要修改代码,过于麻烦!

中间件模式:

中间件模式的的优点:

  • 将消息写入消息队列,需要消息的系统自己从消息队列中订阅,从而系统A不需要做任何修改。

(2)异步

传统模式:

传统模式的缺点:

  • 一些非必要的业务逻辑以同步的方式运行,太耗费时间。

中间件模式:

中间件模式的的优点:

  • 将消息写入消息队列,非必要的业务逻辑以异步的方式运行,加快响应速度

(3)削峰

传统模式

传统模式的缺点:

  • 并发量大的时候,所有的请求直接怼到数据库,造成数据库连接异常

中间件模式:

中间件模式的的优点:

  • 系统A慢慢的按照数据库能处理的并发量,从消息队列中慢慢拉取消息。在生产中,这个短暂的高峰期积压是允许的。

3、使用了消息队列会有什么缺点?

分析:一个使用了MQ的项目,如果连这个问题都没有考虑过,就把MQ引进去了,那就给自己的项目带来了风险。我们引入一个技术,要对这个技术的弊端有充分的认识,才能做好预防。要记住,不要给公司挖坑!
回答:回答也很容易,从以下两个个角度来答

  • 系统可用性降低:你想啊,本来其他系统只要运行好好的,那你的系统就是正常的。现在你非要加个消息队列进去,那消息队列挂了,你的系统不是呵呵了。因此,系统可用性降低
  • 系统复杂性增加:要多考虑很多方面的问题,比如一致性问题、如何保证消息不被重复消费,如何保证保证消息可靠传输。因此,需要考虑的东西更多,系统复杂性增大。

4、RabbitMQ的几种消息队列模型

最基本的模型

工作队列模型

发布订阅模型

路由模型

主题模型

RPC模型

二、Rabbit MQ常用类、方法

1、连接对象

Connection【com.rabbitmq.client.Connection】

方法返回值描述
createChannel()Channel创建一个通道对象

2、通道对象

Channel【com.rabbitmq.client.Channel】

方法返回值描述
queueDeclare(String queue, boolean durable, boolean exclusive, boolean autoDelete,Map<String, Object> arguments)Queue.DeclareOk声明一个队列(创建一个队列)
basicPublish(String exchange, String routingKey, BasicProperties props, byte[] body)void向队列中声明一个消息(发送消息)发送给队列或交换机
basicConsume(String queue, boolean autoAck, Consumer callback)String监听队列(消费端监听队列后才能时实获取队列的消息,立刻消费)
basicQos(int prefetchCount)void限制发送消息个数
basicAck(long deliveryTag, boolean multiple)void手动回执一个消息
exchangeDeclare(String exchange, String type)Exchange.DeclareOk声明交换机,常用类型为  fanout(发布订阅模式)、direct(路由模式)、topic(主题模式)
queueBind(String queue, String exchange, String routingKey)Queue.BindOk将队列绑定在交换机上
保证生产者消息可达MQ服务(避免丢失)
txSelect()Tx.SelectOk(事务机制)设置为transaction模式
txCommit()Tx.CommitOk(事务机制)提交事务
txRollback()Tx.RollbackOk(事务机制)回滚事务
confirmSelect()Confirm.SelectOk(消息确认机制)生产者调用confirmSelect 将channel设置为confirm模式

waitForConfirms()

waitForConfirms(long var1)

boolean(消息确认机制)消息确认

waitForConfirmsOrDie()

waitForConfirmsOrDie(long var1)

void(消息确认机制)消息确认【抛异常,内部仍然调用waitForConfirms实现】
addConfirmListener(ConfirmListener var1)(消息确认机制)异步监听发送方确认模式

3、消息获取对象

Consumer【com.rabbitmq.client.Consumer】

DefaultConsumer【com.rabbitmq.client.DefaultConsumer】

new DefaultConsumer(Channel channel)定义消费者,消费队列消息(接收消息)这是一个对象,并且重写里面的 handleDelivery方法
handleDelivery(String consumerTag, Envelope envelope, BasicProperties properties, byte[] body)void重写方法。body是消息队列里面的消息

4、创建maven工程,引入依赖

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>com.my.RabbitMQ</groupId>
  <artifactId>myrabbitmq</artifactId>
  <version>0.0.1-SNAPSHOT</version>
  
  <dependencies>
  	<dependency>
  		<groupId>com.rabbitmq</groupId>
  		<artifactId>amqp-client</artifactId>
  		<version>4.0.2</version>
  	</dependency>
  	<dependency>
  		<groupId>org.slf4j</groupId>
  		<artifactId>slf4j-log4j12</artifactId>
  		<version>1.7.5</version>
  	</dependency>
  	<dependency>
  		<groupId>log4j</groupId>
  		<artifactId>log4j</artifactId>
  		<version>1.2.17</version>
  	</dependency>
  	<dependency>
  		<groupId>junit</groupId>
  		<artifactId>junit</artifactId>
  		<version>4.11</version>
  	</dependency>
  </dependencies>
</project>

5、创建连接工厂类

package com.mmr.rabbitmq.util;

import java.io.IOException;
import java.util.concurrent.TimeoutException;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;

public class ConnectionUtils {
	/**
	 * 获取MQ的连接
	 * @return
	 * @throws TimeoutException 
	 * @throws IOException 
	 */
	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("/virtual_host");
		//用户名
		factory.setUsername("marvin");
		//密码
		factory.setPassword("123");
		
		return factory.newConnection();
	}
}

三、简单消息队列模型

1、创建消息生产者

package com.mmr.rabbitmq01.simple;

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

import com.mmr.rabbitmq.util.ConnectionUtils;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;

public class Send {
	//队列名
	private static final String QUEUE_NAME="test_simple_queue";

	public static void main(String[] args) throws IOException, TimeoutException {
		//获取连接
		Connection connection=ConnectionUtils.getConnection();
		//从连接中创建通道
		Channel channel=connection.createChannel();
		//声明(创建)队列
		channel.queueDeclare(QUEUE_NAME, false, false, false, null);
		//发送的消息
		String msg="hello simple";
		channel.basicPublish("", QUEUE_NAME, null, msg.getBytes());
		
		System.out.println("--send queue:"+msg);
		//关闭通道和连接
		channel.close();
		connection.close();
		
	}
}

2、消息消费者

消费者有两种方式消费消息队列

  • DefaultConsumer(推荐使用)
  • QueueingConsumer(旧的,不推荐使用)
package com.mmr.rabbitmq01.simple;

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

import com.mmr.rabbitmq.util.ConnectionUtils;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import com.rabbitmq.client.ConsumerCancelledException;
import com.rabbitmq.client.DefaultConsumer;
import com.rabbitmq.client.Envelope;
import com.rabbitmq.client.QueueingConsumer;
import com.rabbitmq.client.QueueingConsumer.Delivery;
import com.rabbitmq.client.ShutdownSignalException;
import com.rabbitmq.client.AMQP.BasicProperties;

/**
 * 消费者获取消息
 * @author Marvin
 *
 */
public class Recv {
	private static final String QUEUE_NAME="test_simple_queue";
	
	public static void main(String[] args) throws IOException, TimeoutException, ShutdownSignalException, ConsumerCancelledException, InterruptedException {
//		oldAPI();
		newAPI();
	}
	/**
	 * 新的方式(DefaultConsumer),推荐使用
	 */
	private static void newAPI() throws IOException, TimeoutException {
		//获取连接
		Connection connection=ConnectionUtils.getConnection();
		//创建频道
		Channel channel=connection.createChannel();
		//队列声明
		channel.queueDeclare(QUEUE_NAME, false, false, false, null);
		//定义消费者
		DefaultConsumer consumer=new DefaultConsumer(channel){
			//获取到达消息
			@Override
			public void handleDelivery(String consumerTag, Envelope envelope, BasicProperties properties, byte[] body)
					throws IOException {
				String msg=new String(body,"utf-8");
				System.out.println("new api recv:"+msg);
			}
		};
		//监听队列
		channel.basicConsume(QUEUE_NAME, true, consumer);
	}
	/**
	 * 旧的方式(QueueingConsumer),不推荐使用
	 */
	private static void oldAPI() throws IOException, TimeoutException, InterruptedException {
		//获取连接
		Connection connection=ConnectionUtils.getConnection();
		//创建频道
		Channel channel=connection.createChannel();
		//定义队列的消费者
		QueueingConsumer consumer=new QueueingConsumer(channel);
		//监听队列
		channel.basicConsume(QUEUE_NAME, true, consumer);
		while(true){
			Delivery delivery=consumer.nextDelivery();
			String msgString=new String(delivery.getBody());
			System.out.println("[recv] msg:"+msgString);
		}
	}
}

3、测试结果总结

运行java类:

可以看到 send 类一发出消息,recv 就立即消费了队列消息(因为消费者监听了这个队列)

我们的队列信息可以通过http://localhost:15672可视化平台看到我们定义的队列信息(队列名:test_simple_queue;虚拟主机:/virtual_host)

四、工作队列模型

工作队列分为两种:

  1. 轮询分发:queue 向两个消费者交替分发消息,不考虑消费者处理消息的效率高低,是否完成。
  2. 公平分发:queue 向两个消费者每次发送一个消息,等到消费者处理完成后手动向队列回执一个确认信息,队列收到确认信息后再向消费者发送下一个消息。(消费者完成后才获取下一个消息,处理效率高的消费者相对来说会从队列中获取更多的消息,能者多劳)

1、轮询分发

(1)消息生产者

package com.mmr.rabbitmq02.work;

import java.io.IOException;
import java.util.concurrent.TimeoutException;
import com.mmr.rabbitmq.util.ConnectionUtils;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;

/**
 * 轮询分发
 * 				|----clinet1
 * P----Queue---|
 * 				|----clinet2
 */
public class Send {
	private static final String QUEUE_NAME="test_work_queue";

	public static void main(String[] args) throws IOException, TimeoutException, InterruptedException {
		// TODO Auto-generated method stub
		//获取连接
		Connection connection=ConnectionUtils.getConnection();
		//获取channel
		Channel channel=connection.createChannel();
		//声明队列
		channel.queueDeclare(QUEUE_NAME, false, false, false, null);
		
		for(int i=0;i<10;i++){
			String s="hello:"+i;
			System.out.println("[work send] i="+i);
			channel.basicPublish("", QUEUE_NAME, null, s.getBytes());
			Thread.sleep(2*10);
		}
		channel.close();
		connection.close();
	}
}

(2)消费者1

3秒处理一个,处理效率低

package com.mmr.rabbitmq02.work;

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

import com.mmr.rabbitmq.util.ConnectionUtils;
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 QUEUE_NAME="test_work_queue";
	
	public static void main(String[] args) throws IOException, TimeoutException {
		Connection connection=ConnectionUtils.getConnection();
		//获取channel
		Channel channel=connection.createChannel();
		//声明队列
		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 msg=new String(body,"utf-8");
				System.out.println("[1] Recv msg:"+msg);
				try {
					Thread.sleep(3*1000);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}finally {
					System.out.println("[1] done");
				}
			}
		};
		//监听
		boolean autoAck =true;
		channel.basicConsume(QUEUE_NAME, autoAck, consumer);
	}
}

(3)消费者2

1秒处理一个,处理效率高

package com.mmr.rabbitmq02.work;

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

import com.mmr.rabbitmq.util.ConnectionUtils;
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 QUEUE_NAME="test_work_queue";
	public static void main(String[] args) throws IOException, TimeoutException {
		Connection connection=ConnectionUtils.getConnection();
		//获取channel
		Channel channel=connection.createChannel();
		//声明队列
		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 msg=new String(body,"utf-8");
				System.out.println("[2] Recv msg:"+msg);
				try {
					Thread.sleep(1*1000);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}finally {
					System.out.println("[2] done");
				}
			}
		};
		//监听
		boolean autoAck =true;
		channel.basicConsume(QUEUE_NAME, autoAck, consumer);
	}
}

(3)测试结果总结

总结:两个消费者处理效率不同,但是队列仍会交替将消息发送给两个消费者,不管你是否处理完成。

2、公平分发

每个消费者发送确认消息之前,消息队列不发送下一个消息给消费者,一次只处理一个消息

  • basicQos():限制发送消息个数,限制发送给同一个消费者不得超过一条消息
  • basicAck():手动回执一个消息

注意:使用公平分发消费者一定要关闭自动应答,手动回执才有效

boolean autoAck =false;//关闭自动应答
channel.basicConsume(QUEUE_NAME, autoAck, consumer);

(1)消息生产者

package com.mmr.rabbitmq03.work.fair;

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

import com.mmr.rabbitmq.util.ConnectionUtils;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;

/**
 * 公平分发
 * 使用公平分发必须关闭自动应答
 * 				|----clinet1
 * P----Queue---|
 * 				|----clinet2
 */
public class Send {
	private static final String QUEUE_NAME="test_work_queue";
	public static void main(String[] args) throws IOException, TimeoutException, InterruptedException {
		// TODO Auto-generated method stub
		//获取连接
		Connection connection=ConnectionUtils.getConnection();
		//获取channel
		Channel channel=connection.createChannel();
		//声明队列
		channel.queueDeclare(QUEUE_NAME, false, false, false, null);
		/**
		 * 每个消费者发送确认消息之前,消息队列不发送下一个消息给消费者,一次只处理一个消息
		 * 
		 * 限制发送给同一个消费者不得超过一条消息
		 * basicQos()限制发送消息个数
		 */
		int prefetchCount=1;
		channel.basicQos(prefetchCount);
		
		for(int i=0;i<10;i++){
			String s="hello:"+i;
			System.out.println("[work send] i="+i);
			channel.basicPublish("", QUEUE_NAME, null, s.getBytes());
			Thread.sleep(2*10);
		}
		channel.close();
		connection.close();
	}
}

(2)消费者1(关闭自动应答)

3秒处理一个,处理效率

package com.mmr.rabbitmq03.work.fair;

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

import com.mmr.rabbitmq.util.ConnectionUtils;
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 QUEUE_NAME="test_work_queue";
	public static void main(String[] args) throws IOException, TimeoutException {
		Connection connection=ConnectionUtils.getConnection();
		//获取channel
		final Channel channel=connection.createChannel();
		//声明队列
		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 msg=new String(body,"utf-8");
				System.out.println("[1] Recv msg:"+msg);
				try {
					Thread.sleep(3*1000);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}finally {
					System.out.println("[1] done");
					//手动回执一个消息
					channel.basicAck(envelope.getDeliveryTag(), false);
				}
			}
		};
		//监听,一定要关闭自动应答
		boolean autoAck =false;
		channel.basicConsume(QUEUE_NAME, autoAck, consumer);
	}
}

(3)消费者2(关闭自动应答)

1秒处理一个,处理效率高

package com.mmr.rabbitmq03.work.fair;

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

import com.mmr.rabbitmq.util.ConnectionUtils;
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 QUEUE_NAME="test_work_queue";
	public static void main(String[] args) throws IOException, TimeoutException {
		Connection connection=ConnectionUtils.getConnection();
		//获取channel
		final Channel channel=connection.createChannel();
		//声明队列
		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 msg=new String(body,"utf-8");
				System.out.println("[2] Recv msg:"+msg);
				try {
					Thread.sleep(1*1000);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}finally {
					System.out.println("[2] done");
					channel.basicAck(envelope.getDeliveryTag(), false);
				}
			}
		};
		//监听,一定要关闭自动应答
		boolean autoAck =false;
		channel.basicConsume(QUEUE_NAME, autoAck, consumer);
	}
}

(3)测试结果总结

总结:两个消费者处理效率不同,队列一次只向消费者发送一个消息,直到的收到消息回执后才发送下一个消息。

所以可以看到执行效率高的Recv2处理的消息更多。

五、发布订阅模型(fanout)

解读:

  • 一个生产者,多个消费者。
  • 每一个消费者都有自己的队列
  • 生产者没有直接把消息发送到队列,而是发到了交换机(转发器)exchange
  • 每个队列都要绑定到交换机上
  • 生产者发送的消息,经过交换机,到达队列,就能实现一个消息被多个消费者消费

1、消息生产者(声明交换机 fanout 模式)

消息发送至 exchange(交换机) 而非先前的 queue(队列)

//声明交换机 fanout 模式
channel.exchangeDeclare(EXCHANGE_NANE, "fanout");//分发
//发送消息,发送到交换机(basicPublish第二个参数""是空字符串,表示发送给所有绑定的队列)
String msg="hello ps";
channel.basicPublish(EXCHANGE_NANE, "", null, msg.getBytes());

package com.mmr.rabbitmq04.ps;

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

import com.mmr.rabbitmq.util.ConnectionUtils;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;

/**
 * 发布订阅模式
 * channel.exchangeDeclare()交换机
 * 一个生产者消息发送到交换机,交换机分两个消息队列分别发送给两个消费者
 * 实现了一个任务后同时发送邮件和短信的功能
 */
public class Send {
	//交换机名
	private static final String EXCHANGE_NANE="test_exchange_fanout";
	public static void main(String[] args) throws IOException, TimeoutException {
		Connection connection=ConnectionUtils.getConnection();
		Channel channel=connection.createChannel();
		//声明交换机
		channel.exchangeDeclare(EXCHANGE_NANE, "fanout");//分发
		
		//发送消息,发送到交换机
		String msg="hello ps";
		channel.basicPublish(EXCHANGE_NANE, "", null, msg.getBytes());
		System.out.println("send "+msg);
		
		channel.close();
		connection.close();
	}
}

2、消费者SMS(绑定队列和交换机)

//消费者绑定交换机(转发器),将队列绑定到交换机
channel.queueBind(QUEUE_NAME, EXCHANGE_NANE, "");

package com.mmr.rabbitmq04.ps;

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

import com.mmr.rabbitmq.util.ConnectionUtils;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.Consumer;
import com.rabbitmq.client.DefaultConsumer;

public class Recv1SMS {
	//交换机名
	private static final String EXCHANGE_NANE="test_exchange_fanout";
	//队列名
	private static final String QUEUE_NAME="test_fanout_sms";
	public static void main(String[] args) throws IOException, TimeoutException {
		Connection connection=ConnectionUtils.getConnection();
		final Channel channel=connection.createChannel();
		//先声明交换机
		channel.exchangeDeclare(EXCHANGE_NANE, "fanout");//分发
		//声明队列
		channel.queueDeclare(QUEUE_NAME, false, false, false, null);
		//绑定交换机(转发器)将队列绑定到交换机
		channel.queueBind(QUEUE_NAME, EXCHANGE_NANE, "");
		Consumer consumer=new DefaultConsumer(channel){
			public void handleDelivery(String consumerTag, com.rabbitmq.client.Envelope envelope, com.rabbitmq.client.AMQP.BasicProperties properties, byte[] body) throws IOException {
				String s=new String(body,"utf-8");
				System.out.println("[2] Recv SMS msg:"+s);
				//手动回执一个消息
				channel.basicAck(envelope.getDeliveryTag(), false);
			};
		};
		channel.basicConsume(QUEUE_NAME, false, consumer);
	}
}

3、消费者 Email(绑定队列和交换机)

package com.mmr.rabbitmq04.ps;

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

import com.mmr.rabbitmq.util.ConnectionUtils;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.Consumer;
import com.rabbitmq.client.DefaultConsumer;

public class Recv1Email {

	private static final String EXCHANGE_NANE="test_exchange_fanout";
	private static final String QUEUE_NAME="test_fanout_email";
	public static void main(String[] args) throws IOException, TimeoutException {
		Connection connection=ConnectionUtils.getConnection();
		final Channel channel=connection.createChannel();
		channel.exchangeDeclare(EXCHANGE_NANE, "fanout");
		//声明队列
		channel.queueDeclare(QUEUE_NAME, false, false, false, null);
		//绑定交换机(转发器)
		channel.queueBind(QUEUE_NAME, EXCHANGE_NANE, "");
		Consumer consumer=new DefaultConsumer(channel){
			public void handleDelivery(String consumerTag, com.rabbitmq.client.Envelope envelope, com.rabbitmq.client.AMQP.BasicProperties properties, byte[] body) throws IOException {
				String s=new String(body,"utf-8");
				System.out.println("[1] Recv Email msg:"+s);
				//手动回执一个消息
				channel.basicAck(envelope.getDeliveryTag(), false);
			};
		};
		channel.basicConsume(QUEUE_NAME, false, consumer);
	}
}

4、测试结果总结

总结:生产者将消息发送到交换机,而非先前的队列。

每个消费者都有一个自己的队列,并将队列和交换机进行绑定。

这样发送到交换机的消息就会同时发送给msm 和 email的队列,两个队列都拿到了这个消息,并通过消费者消费消息。

六、路由模式(direct)

1、消息生产者(声明交换机 direct 模式)

//声明路由 direct 模式
channel.exchangeDeclare(EXCHANGE_NANE, "direct");

//error路由(通过routingKey定义,消费者通过key接收特定的消息)
String msgerr="hello direct err";
String
routingKeyErr="error";
channel.basicPublish(EXCHANGE_NANE, routingKeyErr, null, msgerr.getBytes());

package com.mmr.rabbitmq05.routing;

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

import com.mmr.rabbitmq.util.ConnectionUtils;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;

/**
 * direct
 * 路由模式,通过routingKey定义,消费者通过key接收特定的消息
 */
public class Send {
	private static final String EXCHANGE_NANE="test_exchange_direct";
	public static void main(String[] args) throws IOException, TimeoutException {
		Connection connection=ConnectionUtils.getConnection();
		Channel channel=connection.createChannel();
		//声明路由
		channel.exchangeDeclare(EXCHANGE_NANE, "direct");
		//error路由(通过routingKey定义,消费者通过key接收特定的消息)
		String msgerr="hello direct err";
		String routingKeyErr="error";
		channel.basicPublish(EXCHANGE_NANE, routingKeyErr, null, msgerr.getBytes());
		//info路由
		String msginfo="hello direct info";
		String routingKeyInfo="info";
		channel.basicPublish(EXCHANGE_NANE, routingKeyInfo, null, msginfo.getBytes());
		//warning路由
		String msgwarning="hello direct warning";
		String routingKeyWarning="warning";
		channel.basicPublish(EXCHANGE_NANE, routingKeyWarning, null, msgwarning.getBytes());
		
		channel.close();
		connection.close();
	}
}

2、消费者1(只接收error消息)

//绑定
channel.queueBind(QUEUE_NANE, EXCHANGE_NANE, "error");

package com.mmr.rabbitmq05.routing;

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

import com.mmr.rabbitmq.util.ConnectionUtils;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.Consumer;
import com.rabbitmq.client.DefaultConsumer;

public class Recv1 {
	private static final String EXCHANGE_NANE="test_exchange_direct";
	private static final String QUEUE_NANE="test_direct_queue_1";
	public static void main(String[] args) throws IOException, TimeoutException {
		Connection connection=ConnectionUtils.getConnection();
		final Channel channel=connection.createChannel();
		channel.basicQos(1);
		//声明路由
		channel.exchangeDeclare(EXCHANGE_NANE, "direct");
		//声明队列
		channel.queueDeclare(QUEUE_NANE, false, false, false, null);
		//绑定
		channel.queueBind(QUEUE_NANE, EXCHANGE_NANE, "error");
		Consumer consumer=new DefaultConsumer(channel){
			public void handleDelivery(String consumerTag, com.rabbitmq.client.Envelope envelope, com.rabbitmq.client.AMQP.BasicProperties properties, byte[] body) throws IOException {
				String s=new String(body,"utf-8");
				System.out.println("[1 err] Recv msg:"+s);
				channel.basicAck(envelope.getDeliveryTag(), false);
			};
		};
		channel.basicConsume(QUEUE_NANE, false, consumer);
	}
}

3、消费者2(接收info_err_warning消息)

package com.mmr.rabbitmq05.routing;

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

import com.mmr.rabbitmq.util.ConnectionUtils;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.Consumer;
import com.rabbitmq.client.DefaultConsumer;

public class Recv2 {
	private static final String EXCHANGE_NANE="test_exchange_direct";
	private static final String QUEUE_NANE="test_direct_queue_2";
	public static void main(String[] args) throws IOException, TimeoutException {
		Connection connection=ConnectionUtils.getConnection();
		final Channel channel=connection.createChannel();
		channel.basicQos(1);
		//声明路由
		channel.exchangeDeclare(EXCHANGE_NANE, "direct");
		//声明队列
		channel.queueDeclare(QUEUE_NANE, false, false, false, null);
		channel.queueBind(QUEUE_NANE, EXCHANGE_NANE, "error");
		channel.queueBind(QUEUE_NANE, EXCHANGE_NANE, "info");
		channel.queueBind(QUEUE_NANE, EXCHANGE_NANE, "warning");
		Consumer consumer=new DefaultConsumer(channel){
			public void handleDelivery(String consumerTag, com.rabbitmq.client.Envelope envelope, com.rabbitmq.client.AMQP.BasicProperties properties, byte[] body) throws IOException {
				String s=new String(body,"utf-8");
				System.out.println("[2 info_err_warning] Recv msg:"+s);
				channel.basicAck(envelope.getDeliveryTag(), false);
			};
		};
		channel.basicConsume(QUEUE_NANE, false, consumer);
	}
}

4、测试结果总结

总结:通过队列绑定key来确定该队列具体接收的消息, Recv1里面只绑定了err信息,而Recv2里面绑定了info err warning三个信息

七、主题模式(topic)

1、消息生产者(声明交换机 topic 模式)

//声明交换机 topic 模式
channel.exchangeDeclare(EXCHANGE_NANE, "topic");

package com.mmr.rabbitmq06.topic;

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

import com.mmr.rabbitmq.util.ConnectionUtils;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;

/**
 * topic
 * 路由匹配
 */
public class Send {
	private static final String EXCHANGE_NANE="test_exchange_topic";
	public static void main(String[] args) throws IOException, TimeoutException {
		Connection connection=ConnectionUtils.getConnection();
		Channel channel=connection.createChannel();
		//声明交换机 topic 模式
		channel.exchangeDeclare(EXCHANGE_NANE, "topic");
		
		String msg="商品....添加";
		String routingKey="goods.add";
		channel.basicPublish(EXCHANGE_NANE, routingKey, null, msg.getBytes("utf-8"));
		
		String msgd="商品....删除";
		String routingKeyd="goods.delete";
		channel.basicPublish(EXCHANGE_NANE, routingKeyd, null, msgd.getBytes("utf-8"));
		System.out.println("---send:"+msg);
		
		channel.close();
		connection.close();
	}
}

2、消费者1(只接收goods.add消息,和原先一样)

//绑定
channel.queueBind(QUEUE_NANE, EXCHANGE_NANE, "goods.add");

package com.mmr.rabbitmq06.topic;

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

import com.mmr.rabbitmq.util.ConnectionUtils;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.Consumer;
import com.rabbitmq.client.DefaultConsumer;

public class Recv1 {
	private static final String EXCHANGE_NANE="test_exchange_topic";
	private static final String QUEUE_NANE="test_topic_queue_1";
	public static void main(String[] args) throws IOException, TimeoutException {
		Connection connection=ConnectionUtils.getConnection();
		final Channel channel=connection.createChannel();
		channel.basicQos(1);
		//声明路由
		channel.exchangeDeclare(EXCHANGE_NANE, "topic");
		//声明队列
		channel.queueDeclare(QUEUE_NANE, false, false, false, null);
		//绑定
		channel.queueBind(QUEUE_NANE, EXCHANGE_NANE, "goods.add");
		Consumer consumer=new DefaultConsumer(channel){
			public void handleDelivery(String consumerTag, com.rabbitmq.client.Envelope envelope, com.rabbitmq.client.AMQP.BasicProperties properties, byte[] body) throws IOException {
				String s=new String(body,"utf-8");
				System.out.println("[1 goods.add] Recv msg:"+s);
				channel.basicAck(envelope.getDeliveryTag(), false);
			};
		};
		channel.basicConsume(QUEUE_NANE, false, consumer);
	}
}

3、消费者2(接收匹配符绑定)

package com.mmr.rabbitmq06.topic;

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

import com.mmr.rabbitmq.util.ConnectionUtils;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.Consumer;
import com.rabbitmq.client.DefaultConsumer;

public class Recv2 {
	private static final String EXCHANGE_NANE="test_exchange_topic";
	private static final String QUEUE_NANE="test_topic_queue_2";
	public static void main(String[] args) throws IOException, TimeoutException {
		Connection connection=ConnectionUtils.getConnection();
		final Channel channel=connection.createChannel();
		channel.basicQos(1);
		//声明路由(修改类型为topic)
		channel.exchangeDeclare(EXCHANGE_NANE, "topic");
		//声明队列
		channel.queueDeclare(QUEUE_NANE, false, false, false, null);
		//绑定方式(变为匹配符绑定)
		channel.queueBind(QUEUE_NANE, EXCHANGE_NANE, "goods.#");
		Consumer consumer=new DefaultConsumer(channel){
			public void handleDelivery(String consumerTag, com.rabbitmq.client.Envelope envelope, com.rabbitmq.client.AMQP.BasicProperties properties, byte[] body) throws IOException {
				String s=new String(body,"utf-8");
				System.out.println("[1 goods.#] Recv msg:"+s);
				channel.basicAck(envelope.getDeliveryTag(), false);
			};
		};
		channel.basicConsume(QUEUE_NANE, false, consumer);
	}
}

4、测试结果总结

总结:交换机设置为 topic 模式后 Recv1 还可以使用老方法 key 全名,Recv2 可以使用通配符匹配,绑定消息

八、rabbitMQ 生产者消息发送可达(事务机制)

在rabbitmq中,我们可以通过持久化数据 解决rabbitmq 服务器异常 的数据丢失问题

问题: 生产者将消息发送出去之后,消息到底有没有到达rabbitmq 服务器,默认的情况是不知道的

两种方式:

  • AMQP实现了事务机制
  • Confirm模式

事务机制(txSelect、txCommit、txRollback)

  • txSelect :用户将当前 channel 设置成 transaction 模式
  • txCommit:用于提交事务
  • txRollback:回滚事务

1、消息生产者

package com.mmr.rabbitmq07.transaction;

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

import com.mmr.rabbitmq.util.ConnectionUtils;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;

public class TxSend {
	private static final String QUEUE_NANE="test_queue_Transaction";
	public static void main(String[] args) throws IOException, TimeoutException {
		Connection connection=ConnectionUtils.getConnection();
		Channel channel=connection.createChannel();
		channel.queueDeclare(QUEUE_NANE, false, false, false, null);
		String msg="hello transaction message";
		try {
			//设置为transaction模式
			channel.txSelect();
			channel.basicPublish("", QUEUE_NANE, null, msg.getBytes());
			//提交事务
			channel.txCommit();
			System.out.println("commit");
		} catch (Exception e) {
			//回滚事务
			channel.txRollback();
			System.out.println("rollback");
		}
		channel.close();
		connection.close();
	}
}

2、消息消费者

package com.mmr.rabbitmq07.transaction;

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

import com.mmr.rabbitmq.util.ConnectionUtils;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.Consumer;
import com.rabbitmq.client.DefaultConsumer;

public class TxRecv1 {
	private static final String QUEUE_NANE="test_queue_Transaction";
	public static void main(String[] args) throws IOException, TimeoutException {
		Connection connection=ConnectionUtils.getConnection();
		Channel channel=connection.createChannel();
		channel.queueDeclare(QUEUE_NANE, false, false, false, null);
		Consumer consumer=new DefaultConsumer(channel){
			public void handleDelivery(String consumerTag, com.rabbitmq.client.Envelope envelope, com.rabbitmq.client.AMQP.BasicProperties properties, byte[] body) throws IOException {
				String s=new String(body,"utf-8");
				System.out.println("[TX] Recv msg:"+s);
			};
		};
		channel.basicConsume(QUEUE_NANE, true, consumer);
	}
}

九、rabbitMQ 生产者消息发送可达(confirm消息确认机制)

开启confirm模式:channel.confirmSelect()

confirm的三种实现方式

方法描述
waitForConfirms()同步确认模式,支持单条和批量确认,等待所有消息确认,如果所有的消息都被服务端成功接收 返回true,只要有一条没有被成功接收就 返回false
waitForConfirmsOrDie()同步确认模式,支持单条和批量确认,也是等待所有消息确认,区别在于该方法没有返回值(Void),如果有任意一条消息没有被成功接收,该方法会立即抛出一个IOException类型异常,并且在抛异常前会关闭信道channel.close();【此方法内部其实仍然是调用waitForConfirms实现】
addConfirmListener()异步监听发送方确认模式

1、waitForConfirms【同步确认】

编程模式

  1. 普通 发一条 waitForConfirms()
  2. 批量的 发一批 waitForConfirms()

官方文档描述:等待直到上次调用之后发布的所有消息被broker ack或nack。注意,当在非confirm通道上调用waitforconfirm时,会抛出一个IllegalStateException。 

(1)消息消费者【这个消费者设置了topic模式后面的可以通用】

package com.mmr.rabbitmq08.confirm;

import java.io.IOException;
import java.util.concurrent.TimeoutException;
import com.mmr.rabbitmq.util.ConnectionUtils;
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 RecvConfirm {
	public static final String EXCHANGE_NANE = "test_exchange_confirm";
	public static final String QUEUE_NANE="test_queue_confirm";
	public static final String ROUTING_KEY = "confirm.#";

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

		//声明交换机和队列,然后进行绑定设置路由Key
		channel.exchangeDeclare(EXCHANGE_NANE, "topic", true);
		channel.queueDeclare(QUEUE_NANE, false, false, false, null);
		channel.queueBind(QUEUE_NANE, EXCHANGE_NANE, ROUTING_KEY);

		Consumer consumer=new DefaultConsumer(channel){
			@Override
			public void handleDelivery(String consumerTag, Envelope envelope, BasicProperties properties, byte[] body)
					throws IOException {
				String msg=new String(body,"utf-8");
				System.out.println("【confirm】 RecvConfirm msg:"+msg);
			}
		};
		channel.basicConsume(QUEUE_NANE, true, consumer);
	}
}

(2)消息生产者【单条发送】

package com.mmr.rabbitmq08.confirm.waitForConfirms;

import java.io.IOException;
import java.util.concurrent.TimeoutException;
import com.mmr.rabbitmq.util.ConnectionUtils;
import com.mmr.rabbitmq08.confirm.RecvConfirm;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;

/**
 * 单条发送,waitForConfirms确认
 */
public class SendSingle {
	private static final String ROUTING_KEY = "confirm.single_waitForConfirms";
	public static void main(String[] args) throws IOException, TimeoutException, InterruptedException {
		Connection connection=ConnectionUtils.getConnection();
		Channel channel=connection.createChannel();
		//生产者调用confirmSelect 将channel设置为confirm模式
		channel.confirmSelect();
		//发送消息
		String msg="hello confrom message【single waitForConfirms】";
		channel.basicPublish(RecvConfirm.EXCHANGE_NANE, ROUTING_KEY, null, msg.getBytes());
		//确认消息(可以同时确认多条消息)是否发送成功
		if(channel.waitForConfirms()){
			System.out.println("message send success【single waitForConfirms】");
		}else{
			System.out.println("message send failed【single waitForConfirms】");
		}
		channel.close();
		connection.close();
	}
}

(3)消息生产者【批量发送】

package com.mmr.rabbitmq08.confirm.waitForConfirms;

import java.io.IOException;
import java.util.concurrent.TimeoutException;
import com.mmr.rabbitmq.util.ConnectionUtils;
import com.mmr.rabbitmq08.confirm.RecvConfirm;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;

/**
 * 批量发送,waitForConfirms确认
 */
public class SendBatch {
	private static final String ROUTING_KEY = "confirm.batch_waitForConfirms";
	public static void main(String[] args) throws IOException, TimeoutException, InterruptedException {
		Connection connection=ConnectionUtils.getConnection();
		Channel channel=connection.createChannel();
		//生产者调用confirmSelect 将channel设置为confirm模式
		channel.confirmSelect();
		//批量发送
		for(int i=0;i<10;i++){
			String msg="hello confrom message【batch waitForConfirms】=="+i;
			channel.basicPublish(RecvConfirm.EXCHANGE_NANE, ROUTING_KEY, null, msg.getBytes());
		}
		//确认消息(可以同时确认多条消息)是否发送成功
		if(channel.waitForConfirms()){
			System.out.println("message send success【batch waitForConfirms】");
		}else{
			System.out.println("message send failed【batch waitForConfirms】");
		}
		channel.close();
		connection.close();
	}
}

2、waitForConfirmsOrDie【同步确认】

编程模式

  1. 普通 发一条 waitForConfirmsOrDie()
  2. 批量的 发一批 waitForConfirmsOrDie()

源码内部调用了 waitForConfirm()

官方文档描述:等待直到上次调用之后发布的所有消息被broker ack或nack。如果任何消息被锁定,waitForConfirmsOrDie将抛出IOException。当在非confirm通道上调用时,它将抛出一个IllegalStateException。

(1)消息生产者【单条发送】

package com.mmr.rabbitmq08.confirm.waitForConfirmsOrDie;

import com.mmr.rabbitmq.util.ConnectionUtils;
import com.mmr.rabbitmq08.confirm.RecvConfirm;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import java.io.IOException;
import java.util.concurrent.TimeoutException;

/**
 * 单条发送,waitForConfirmsOrDie确认
 */
public class SendSingle {
	private static final String ROUTING_KEY = "confirm.single_waitForConfirmsOrDie";
	public static void main(String[] args) throws IOException, TimeoutException, InterruptedException {
		Connection connection=ConnectionUtils.getConnection();
		Channel channel=connection.createChannel();
		//生产者调用confirmSelect 将channel设置为confirm模式
		channel.confirmSelect();
		//发送消息
		String msg="hello confrom message【single waitForConfirmsOrDie】";
		channel.basicPublish(RecvConfirm.EXCHANGE_NANE, ROUTING_KEY, null, msg.getBytes());
		//确认消息(可以同时确认多条消息)是否发送成功【通过异常来判断,源码是通过调用waitForConfirms】
		try {
			channel.waitForConfirmsOrDie();
			System.out.println("message send success【single waitForConfirmsOrDie】");
		} catch (IOException e) {
			e.printStackTrace();
			System.out.println("message send failed【single waitForConfirmsOrDie】");
		}

		channel.close();
		connection.close();
	}
}

(2)消息生产者【批量发送】

package com.mmr.rabbitmq08.confirm.waitForConfirmsOrDie;

import com.mmr.rabbitmq.util.ConnectionUtils;
import com.mmr.rabbitmq08.confirm.RecvConfirm;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import java.io.IOException;
import java.util.concurrent.TimeoutException;

/**
 * 批量发送,waitForConfirmsOrDie确认
 */
public class SendBatch {
	private static final String ROUTING_KEY = "confirm.batch_waitForConfirmsOrDie";
	public static void main(String[] args) throws IOException, TimeoutException, InterruptedException {
		Connection connection=ConnectionUtils.getConnection();
		Channel channel=connection.createChannel();
		//生产者调用confirmSelect 将channel设置为confirm模式
		channel.confirmSelect();
		//批量发送
		for(int i=0;i<10;i++){
			String msg="hello confrom message【batch waitForConfirmsOrDie】=="+i;
			channel.basicPublish(RecvConfirm.EXCHANGE_NANE, ROUTING_KEY, null, msg.getBytes());
		}
		//确认消息(可以同时确认多条消息)是否发送成功【通过异常来判断,源码是通过调用waitForConfirms】
		try {
			channel.waitForConfirmsOrDie();
			System.out.println("message send success【batch waitForConfirmsOrDie】");
		} catch (IOException e) {
			e.printStackTrace();
			System.out.println("message send failed【batch waitForConfirmsOrDie】");
		}

		channel.close();
		connection.close();
	}
}

3、addConfirmListener【异步监听确认】

(1)消息生产者【单条发送】

package com.mmr.rabbitmq08.confirm.addConfirmListener;

import com.mmr.rabbitmq.util.ConnectionUtils;
import com.mmr.rabbitmq08.confirm.RecvConfirm;
import com.rabbitmq.client.*;
import java.io.IOException;
import java.util.concurrent.TimeoutException;

/**
 * 单条发送,addConfirmListener异步监听确认
 */
public class SendSingle {
	private static final String ROUTING_KEY = "confirm.addConfirmListener";
	public static void main(String[] args) throws IOException, TimeoutException, InterruptedException {
		Connection connection=ConnectionUtils.getConnection();
		Channel channel=connection.createChannel();
		//生产者调用confirmSelect 将channel设置为confirm模式
		channel.confirmSelect();
		String msg="hello confrom message【single addConfirmListener】";
		channel.basicPublish(RecvConfirm.EXCHANGE_NANE, ROUTING_KEY, null, msg.getBytes());
		//异步监听确认是否发送成功
		channel.addConfirmListener(new ConfirmListener() {
			/**
			 * 消息成功处理
			 * @param deliveryTag:唯一消息标签
			 * @param multiple:是否批量
			 * @throws IOException
			 */
			public void handleAck(long deliveryTag, boolean multiple) throws IOException {
				System.out.println("message send success【single addConfirmListener】:deliveryTag【"+ deliveryTag +"】multiple【"+ multiple +"】");
			}

			/**
			 * 消息失败处理
			 * @param deliveryTag:唯一消息标签
			 * @param multiple:是否批量
			 * @throws IOException
			 */
			public void handleNack(long deliveryTag, boolean multiple) throws IOException {
				System.out.println("message send failed【single addConfirmListener】:deliveryTag【"+ deliveryTag +"】multiple【"+ multiple +"】");
			}
		});

		//给监听一点时间再关闭
		Thread.sleep(5000);
		channel.close();
		connection.close();
	}
}

(2)消息生产者【批量发送】

package com.mmr.rabbitmq08.confirm.addConfirmListener;

import com.mmr.rabbitmq.util.ConnectionUtils;
import com.mmr.rabbitmq08.confirm.RecvConfirm;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.ConfirmListener;
import com.rabbitmq.client.Connection;
import java.io.IOException;
import java.util.concurrent.TimeoutException;

/**
 * 批量发送,addConfirmListener异步监听确认
 */
public class SendBatch {
	private static final String ROUTING_KEY = "confirm.addConfirmListener";
	public static void main(String[] args) throws IOException, TimeoutException, InterruptedException {
		Connection connection=ConnectionUtils.getConnection();
		Channel channel=connection.createChannel();
		//生产者调用confirmSelect 将channel设置为confirm模式
		channel.confirmSelect();
		//批量发送
		for(int i=0;i<50;i++){
			String msg="hello confrom message【batch addConfirmListener】=="+i;
			channel.basicPublish(RecvConfirm.EXCHANGE_NANE, ROUTING_KEY, null, msg.getBytes());
		}

		//异步监听确认是否发送成功
		channel.addConfirmListener(new ConfirmListener() {
			/**
			 * 消息成功处理
			 * @param deliveryTag:唯一消息标签
			 * @param multiple:是否批量
			 * @throws IOException
			 */
			public void handleAck(long deliveryTag, boolean multiple) throws IOException {
				System.out.println("message send success【batch addConfirmListener】:deliveryTag【"+ deliveryTag +"】multiple【"+ multiple +"】");
			}

			/**
			 * 消息失败处理
			 * @param deliveryTag:唯一消息标签
			 * @param multiple:是否批量
			 * @throws IOException
			 */
			public void handleNack(long deliveryTag, boolean multiple) throws IOException {
				System.out.println("message send failed【batch addConfirmListener】:deliveryTag【"+ deliveryTag +"】multiple【"+ multiple +"】");
			}
		});


		//给监听一点时间再关闭
		Thread.sleep(10000);
		channel.close();
		connection.close();
	}
}

RabbitMQ的安装和使用 运行单元测试类,我们可以看到控制台中输出下面的内容,消息被发送到了RabbitMQ Server的hello队列中。25672:Erlang分发用于节点间和CLI工具通信,并从动态范围分配(默认情况下限制为单个端口,计算为AMQP端口+ 20000)。下面我们通过在SpringBoot项目中集成RabbitMQ,并实现一个简单的发送、接收消息的例子来对RabbitMQ有一个只管的感受和理解。切换到应用主类的控制台,我们可以看到类似如下输出,消费者对hello队列的监听程序执行了,并输出了接收到的消息信息。 阅读详情

相关推荐

RabbitMQ快速入门及API介绍(401M)

RabbitMQ快速入门及API介绍(401M) QQ截图 20191220230107.png?x-oss-process=style/pnp8 (42.73KB, 下载次数:227) 下载附件 2019-12-2023 :01 上传【课程介绍】:第一章 : RabbitMQ介绍:消息中间件概念、RabbitMQ安装、RabbitMq客户端调用的Java实现。 第 2 章:RabbitMQ 概念:生产者、消费者、Broken、交换、路由密钥、绑定密钥、交换类型。 第三章:RabbitMQ的几个API:连接、通道、交换、队列、发送消息、消费消息。 【课程目录】:---第一章:RabbitMQ介绍----1-什么是消息中间件.mp4----2-RabbitMQ消息队列安装:window环境.mp4----3-RabbitMQ消息队列安装 :Linux环境.mp4----4-Rabbitmq入口示例:server.mp4----5-rabbitmq入口示例:client.mp4---第二章:RabbitMQ的几个概念----1-生产者和消费者 concept.mp4--- -2-Broke

RabbitMq(三) -- 发布确认(springboot中使用rabbitmq)

1. 含义: 生产者发消息给mq,mq将消息持久化到磁盘上了,mq再告诉生产者我已经把消息持久化到磁盘上了,这时才能保证消息是没有丢失,稳稳地保存在了磁盘上。 mq告诉生产者,我已经保存到磁盘了,这一步就叫发布确认。 2. 发布确认原理 生产者将信道设置成 confirm 模式,一旦信道进入 confirm 模式,所有在该信道上面发布的消息都将会被指派一个唯一的 ID (从 1 开始),一旦消息被投递到所有匹配的队列之后,broker 就会发送一个确认给生产者 (包含消息的唯一 ID),这就使得生产者知道

weixin_39724194的博客 1887

RabbitMQ API文档

RabbitMQ实现了AMQP协议,RabbitMQ 3.5.6版本的API文档,欢迎下载使用

RabbitMQ 常用 API

RabbitMQ 常用 API

墨鸦的博客 7620

中科蓝讯Downloader配置实战:从软硬开关机到ANC降噪的完整指南

本文提供了中科蓝讯Downloader工具的详细配置实战指南。内容涵盖从基础的软硬开关机选择与配置,到核心的EQ音效调试、触摸按键与入耳检测优化,以及高阶的ANC降噪与电源管理设置。旨在帮助开发者快速掌握关键配置项,高效完成蓝牙耳机产品的功能调试与优化。

s4t5u6v7的博客 508

四、SVN客户端tortoiseSVN的基本功能介绍

档案浏览器(Repo-browser) 鼠标右键–>tortoiseSVN->版本库浏览器 输入版本库URL地址,进入如菜单界面 版本库浏览器支持SVN及http两种最常用的通信协议 SVN服务器访问方式 方法 模式 访问方法 File:// 直接版本库访问(本地磁盘) http:// 通过配置subversion的WEB服务器的WEBDAV协议 http...

也许是我送你哦 1012

RabbitMQ 提供 RESTful API,用于管理和监控,知识体系一共包含哪些部分?底层原理是什么?

RabbitMQ RESTful API 的底层原理涉及 HTTP 协议、Erlang 编程语言、AMQP 协议以及资源隔离等多个层面。通过以上方法,开发者可以全面掌握 RabbitMQ RESTful API 的知识体系和底层原理,为构建高效、可靠的分布式系统提供理论支持和实践经验。RabbitMQ 的 RESTful API 是其管理插件的一部分,提供了对 RabbitMQ 集群的全面管理和监控能力。

长风破浪会有时的博客 728

Ubuntu20.04下Docker无缝升级实战指南

本文提供了一份在Ubuntu20.04系统上安全、无缝升级Docker的实战指南。文章详细阐述了升级前的版本检查、数据备份等准备工作,逐步演示了通过APT包管理器进行升级或指定版本安装的操作步骤,并强调了升级后的验证与常见问题回滚方案,确保业务容器平稳过渡。

postgres8guard的博客 481

Java RabbitMQ API 对接说明

最近研发的物联网底层框架使用了RabbitMQ作为消息队列,如果监控消息队列对其通道是否出现阻塞能够及时获知与让管理员收到预警并及时处理,这里我们会采用RabbitMQ的rabbitmq_management插件。利用其提供的API进行获取信息,从而实现队列监控的目标。

谁念西风独自凉 2449

STM32踩坑--NoST-LINK detected

这里出现的NoST-LINK detected是没有扫描到端口 在我使用一段Fly-MCU后,有一天突然发现出现如上错误,再检查的过程中检查以下几点 1.STM32 魔术棒的设置 1.1Debug设置 1.2 utilities 设置没有问题,与原来一样又看了下端口——在设备管理器中;也没有问题 ...

qq_53002477的博客 2万+

告别unittest!用Pytest+Fixture重构你的Python测试代码(附实战案例)

本文详细介绍了如何从unittest迁移到Pytest框架,重点解析了Pytest的Fixture机制如何提升测试效率和代码简洁性。通过实战案例展示了Pytest在减少代码量、提升调试效率和资源管理方面的优势,帮助开发者重构Python测试代码。

weixin_26749245的博客 206

RabbitMQ 管理控制台菜单详解

RabbitMQ 的管理控制台是一个强大的工具,用于监控和管理 RabbitMQ 服务。

长风破浪会有时的博客 2135

【DaVinci Configurator专题】-2-将CAN 2.0或CANFD Matrix的Arxml/DBC文件导入到CFG

共9页 目录 1 打开DaVinci Configurator,导入Arxml文件 2 解决导入Arxml文件后可能会出现的一些错误 3 验证CFG的配置 4 根据CFG的配置生成代码 5 结尾 <--返回「实战篇」博文-总目录--> <--返回「Autosar从入门到精通-实战篇」专栏主页--> 关键词: Open the Input Files Assistant Complete update process Update ECU configurati

「汽车电子助手」的博客 6125

SpringBoot如何整合rabbitMq原生api

SpringBoot如何整合rabbitMq原生api 文章目录SpringBoot如何整合rabbitMq原生api前言一、springboot启动器spi思想是什么?二、spring装载和管理单例bean的生命周期对理解springboot整合中间件原生api到底有多大地帮助?1.两个核心接口:其一是BeanPostProcess,其二是Smartinitializingsingleton2.时序图总结 前言 提示:这里可以添加本文要记录的大概内容: 例如:随着springboot启动器自动装配思想的

石果果的博客 497

ConvNeXt 系列改进:引入大核注意力(LKA)替换 7×7 深度卷积,涨点 2.1%

LSKA替换7×7深度卷积,在ImageNet分类任务上实现2.1%的显著提升,参数量仅增加1M。LSKA的四层1D分解将计算复杂度从O(k²)降至O(k),21×21大核的计算量仅相当于原版7×7的1.7倍,实现了感受野与计算成本的完美平衡。部署友好度极高:LSKA模块在TensorRT FP16量化后延迟仅1.2ms,精度损失<0.1%,远优于Transformer类模型。下游任务全面领先。

ZXSXJ的博客 700

Arduino ESP32 Web服务器从microSD卡读取

Arduino ESP32 Web服务器从microSD卡读取 将网页内容放到SD卡中,当有客户端数据请求时,从SD卡读取信息进行发送,比起SPIFFS文件系统,更节约板上的硬件资源。 SD卡接线说明 ESP32----- SD D5(GPIO 5)-----CS D18(GPIO 18)-----SCK D19(GPIO 19)-----MISO D23()GPIO 23-----MOSI 文件放置 需要把文件放置到SD卡的根目录下。注意SD卡的格式FAT32格式。 实例代码 /*

perseverance51博客 1434

关于RabbitMQ性能问题的几点分析

性能RabbitMQ更多 分类专栏:RabbitMQ 版权声明:本文为博主原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。 本文链接:https://blog.youkuaiyun.com/tom06/article/details/66970277 目前对RabbitMQ的使用才刚刚开始,下面提出的问题,也许是由于对服务器的配置或者对客户端API还不了解导致的...

lingpaoershiyishiji的专栏 1675

计算机控制技术及应用

《计算机控制技术及应用》通过总结作者的科研成果和吸收国内外的先进理论、方法和技术,以计算机控制系统的网络化、开放化、智能化和集成化发展趋势为主线索,主要介绍计算机控制系统的基本概念和计算机控制系统的典型形式,计算机控制系统的数学描述与基本分析方法,以及数字PID控制器、最少拍控制系统、达林算法、状态变量设计法等设计方法及其各种改进算法。重点突出计算机控制系统输入输出通道接口的设计方法和典型控制系统的设计案例与实现方法等。

rabbitMQ常备知识点

rabbitMQ常备知识点

a874828025的博客 216

华为的C语言编程规范.pdf

华为技术有限公司内部技术规范。本规范制定了编写C语言程序的基本原则、规则和建议。从代码的清晰、简洁、可测试、安全、程序效率、可移植各个方面对C语言编程作出了具体指导。

SIMATIC S7-1200, S7-1500 PID 控制[手册]

SIMATIC S7-1200, S7-1500 PID 控制[手册]

开始使用RabbitMQ

因为使用消息队列的目的之一就是要异步,但是这一场景似乎又将异步变成了同步,不过这一场景也很有用,比如一个用户操作产生了一个消息,应用服务收到该消息后执行了一些逻辑并使得数据库发生了变化,UI会一直等待应用服务的返回结果才刷新页面。这一段代码隐藏了众多关于消息队列的细节,将我们的注意力集中在发送消息上,同时ServiceBus提供的API也更接近业务,我们虽然发送的是一个消息,但是在这种场景下体现出来是一个命令,Send(command)这一API描述了我们的意图。”,对方就会迷失在这些细节中。

RjEX73j1I的博客 241

复分析 阿尔福斯(Ahlfors) 第三版答案 英文

答案分为十章,有部分习题没有答案,全英文复分析阿尔福斯(Ahlfors) 第三版答案

EMQX RabbitMQ Action 支持使用默认交换机(Default Exchange)的配置与实现解析

本文围绕 EMQX 数据集成(Data Integration)中的 RabbitMQ Action 展开,讲解其新增能力——通过将 `exchange` 参数留空来直接使用 RabbitMQ 内置的默认交换机(default exchange),从而省去显式声明与绑定交换机的步骤。读完本文,你将掌握 RabbitMQ Action 的完整配置方式、`exchange`/`routing_key`

gitblog_00072的博客 92

RabbitMQ安装和使用详细教程

RabbitMQ入门,springboot整合RabbitMQ

沐雨橙风ιε的博客 1万+

RabbitMq 基础

RabbitMQ 是一个开源的消息代理(message broker)软件,也称为队列服务器(queue server),它允许你通过网络发送和接收消息。它是遵循AMQP(高级消息队列协议,Advanced Message Queuing Protocol)标准实现的消息中间件。

2301_80035594的博客 5375

Spring整合rabbitmq实践(一):基础使用配置

1. Rabbitmq基本概念 1.1. Rabbitmq中的各个角色 producer:消息生产者; consumer:消息消费者; queue:消息队列; exchange:接收producer发送的消息按照binding规则转发给相应的queue; binding:exchange与queue之间的关系; virtualHost:每个virtualHost持有自己的exchange、que...

JinchaoLv的博客 2万+

RabbitMQ的基础概念和设计模式

一、基础概念篇

Jake Weng 1万+

RabbitMQ 基本api使用 以及和spring、springboot整合

1 RabbitMQ介绍 1.1 RabbitMQ简介及使用场景 MQ全称为Message Queue,即消息队列, RabbitMQ是由erlang语言开发,基于AMQP(Advanced MessageQueue 高级消息队列协议)协议实现的消息队列,它是一种应用程序之间的通信方法,消息队列在分布式系统开发中应用非常广泛。RabbitMQ官网 消息队列应用场景: 任务异步处理。 将不需要同...

qqxhb 资源共享 2449
上一篇: Spring Cloud(四) Netflix 组件之 Zuul(服务网关:路由、过滤)
下一篇: AJAX请求与SpringMVC Controller交互
Marvin-Fox
博客等级 码龄11年 190粉丝 100原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值