环境:
amqp-client:5.3.0
摘要说明:
本篇文章主要讲述java如何使用amqp-client对RabbitMQ进行操作;从连接、使用Exchanges和Queues、推送消息、消费消息等;
步骤:
1.引入依赖
引入依赖:
<dependency>
<groupId>com.rabbitmq</groupId>
<artifactId>amqp-client</artifactId>
<version>5.3.0</version>
</dependency>
2.连接和渠道(Connections and Channels)
a.连接RabbitMQ
使用给定参数(主机名,端口号等)连接到RabbitMQ节点:
ConnectionFactory factory = new ConnectionFactory();
// “guest”/“guest”默认情况下,仅限于localhost连接
factory.setUsername("xxx");// 用户名
factory.setPassword("xxx");// 用户密码
factory.setVirtualHost("/xxx");// 虚拟主机
factory.setHost("xxx.xxx.xxx.xxx");// 主机名
factory.setPort(xxxx);// 端口号
Connection conn = factory.newConnection();
或者使用URI进行连接:
ConnectionFactory factory = new ConnectionFactory();
factory.setUri("amqp://userName:password@hostName:portNumber/virtualHost");
Connection conn = factory.newConnection();
b.打开渠道
要想发送和接受消息,必须先创建一个Channel即打开同道:
Channel channel = conn.createChannel();
c.断开连接
要断开连接,只需关闭同道和连接:
channel.close();
conn.close();
请注意,手动关闭可能被视为良好做法,但这里并不是必需的 - 无论如何,当底层连接关闭时,它将自动完成。
d.连接和渠道寿命
连接意味着长寿。底层协议是为长时间运行的连接而设计和优化的。这意味着每次操作打开一个新连接,例如发布的消息,是不必要的,并且强烈不鼓励,因为它会引入大量的网络往返和开销。
通道也意味着长寿,但由于许多可恢复的协议错误将导致通道关闭,因此通道寿命可能短于其连接的寿命。每次操作关闭和打开新通道通常是不必要的,但可能是合适的。如有疑问,请考虑重复使用频道。
通道级异常(例如尝试从不存在的队列中使用)将导致通道关闭。无法再使用已关闭的通道,也不会再从服务器接收任何事件(例如消息传递)。RabbitMQ将记录通道级异常,并将启动通道的关闭序列(见下文)。
3.使用Exchange和Queues(Exchanges and Queues)
a.创建声明Exchange和Queues
当只有一个客户端想要使用一个队列时:它不需要一个众所周知的名称,没有其他客户端可以使用它(独占)并且将自动清除(自动删除) 。这种典型的声明队列的方式如下:
channel.exchangeDeclare(exchangeName, "", true);
String queueName = channel.queueDeclare().getQueue();
channel.queueBind(queueName, exchangeName, routingKey);
上述代码的意义是:
1、一种持久的,非自动交换的“direct”类型的Exchange;
2、具有生成名称的非持久,独占,自动删除队列;
3、将队列绑定到具有给定routingKey的Exchange;
如果多个客户端希望共享具有已知名称的队列,声明方式如下:
channel.exchangeDeclare(exchangeName,“direct”,true);
channel.queueDeclare(queueName,true,false,false,null);
channel.queueBind(queueName,exchangeName,routingKey);
上述代码的意义是:
1、一种持久的,非自动交换的“direct”类型的Exchange
2、具有已知名称的持久,非独占,非自动删除队列
4.发布消息(Publishing Messages)
使用Channel.basicPublish进行基本消息发布:
byte[] messageBodyBytes = "Hello, world!".getBytes();
channel.basicPublish(exchangeName, routingKey, null, messageBodyBytes);
可以加上强制标志和指定消息类型进行推送控制:
channel.basicPublish(exchangeName, routingKey, mandatory,
MessageProperties.PERSISTENT_TEXT_PLAIN,
messageBodyBytes);
使用new AMQP.BasicProperties.Builder()构建消息属性,如设置传递模式、优先级、内容类型:
channel.basicPublish(exchangeName, routingKey,
new AMQP.BasicProperties.Builder()
.contentType("text/plain")//消息类型
.deliveryMode(2)//传递模式
.priority(1)//优先级
.userId("bob")//userId
.build()),
messageBodyBytes);
可推送带header的消息:
Map<String, Object> headers = new HashMap<String, Object>();
headers.put("latitude", 51.5252949);
headers.put("longitude", -0.0905493);
channel.basicPublish(exchangeName, routingKey,
new AMQP.BasicProperties.Builder()
.headers(headers)//头部消息
.build()),
messageBodyBytes);
可推送消息有效期的消息:
channel.basicPublish(exchangeName, routingKey,
new AMQP.BasicProperties.Builder()
.expiration("60000")//有效期
.build()),
messageBodyBytes);
推送方式如下:
a.direct类型Exchange
direct:把消息投递到那些binding key与routing key完全匹配的队列中。
首先我们知道exchange和queue进行绑定时会设置routingkey;
然后我们在将消息发送到exchange时会设置对应的routingkey;
示例如下
String exchangeName = "exchangeName_direct";// 交换机名称
String routingKey = "routingKey_direct";// 路由key
String queueName = "queueName_direct";// 队列名称
String message1 = "Hello RabbitMQ";// 消息内容
channel.exchangeDeclare(exchangeName, "direct", true);
channel.queueDeclare(queueName, true, false, false, null);
channel.queueBind(queueName, exchangeName, routingKey);
channel.basicPublish(exchangeName, routingKey, null, message1.getBytes("UTF-8"));//注意此时routingKey要与channel.queueBind一致
使用空字符串也是direct类型的exchange,则此routingKey需为队列名称;因为队列会默认跟空字符串exchange进行绑定且routingkey为队列名称:
String queueName = "rabbitMQ.test";// 队列名称
channel.queueDeclare(queueName, false, false, false, null);// 声明一个队列
String message = "Hello RabbitMQ";// 消息内容
channel.basicPublish("", queueName, null, message.getBytes("UTF-8"));// 发送消息到队列中
实战 :
Direct类型交换机消息生产类:
package cn.cc.basic.exchange.direct;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
/**
* @模块名:basic
* @包名:cn.cc.basic.exchange.direct
* @类名称: DirectProducer
* @类描述:【类描述】 Direct类型交换机消息生产类
* @版本:1.0
* @创建人:cc
* @创建时间:2018年12月12日上午10:16:02
*/
public class DirectProducer {
// 连接信息
public final static String USER_NAME = "test";
public final static String PASSWORD = "123456";
public final static String VHOST = "/test";
public final static String HOST = "47.99.159.236";
public final static Integer PORT = 5672;
// 队列信息
// 交换机名称
public final static String EXCHANGE_NAME = "test-exchangeName_direct_1";
// 路由key
public final static String[] ROUTINGKEYS = { "test-routingKey_direct_1", "test-routingKey_direct_2", "test-routingKey_direct_3" };
// 队列名称
public final static String QUEUENAME = "test-queueName_direct_1";
public static void main(String[] args) {
try {
ConnectionFactory factory = new ConnectionFactory();
// “guest”/“guest”默认情况下,仅限于localhost连接
factory.setUsername(USER_NAME);// 用户名
factory.setPassword(PASSWORD);// 用户密码
factory.setVirtualHost(VHOST);// 虚拟主机
factory.setHost(HOST);// 主机名
factory.setPort(PORT);// 端口号
// factory.setUri("amqp://" + USER_NAME + ":" + PASSWORD + "@" + HOST + ":" + PORT +"/"+ VHOST);
Connection conn = factory.newConnection();
/**
* 创建信道
*/
Channel channel = conn.createChannel();
/**
* 创建交换器
*/
// 指明交换器名称+类型+是否持久化
// BuiltinExchangeType.DIRECT
channel.exchangeDeclare(EXCHANGE_NAME, "direct", false);
/**
* 推送消息
*/
for (int i = 0; i < 9; i++) {
// 指明推送的路由键
String routingkey = ROUTINGKEYS[i % 3];
// 推送消息
String msg = "Hellol,RabbitMq_" + routingkey;
// 发布消息,需要参数:交换器,路由键,
channel.basicPublish(EXCHANGE_NAME, routingkey, null, msg.getBytes());
System.out.println("Sent " + EXCHANGE_NAME + ":" + routingkey + ":" + msg);
}
}
catch (Exception e) {
e.printStackTrace();
}
}
}
一个队列多个消费者,则会表现出消息在消费者之间的轮询发送:
/**
* @模块名:basic
* @包名:cn.cc.basic.exchange.direct
* @描述:MulitConsumerOneQueue.java
* @版本:1.0
* @创建人:cc
* @创建时间:2018年12月12日下午3:49:17
*/
package cn.cc.basic.exchange.direct;
import java.io.IOException;
import java.net.URISyntaxException;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.util.concurrent.TimeoutException;
import com.rabbitmq.client.AMQP;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import com.rabbitmq.client.Consumer;
import com.rabbitmq.client.DefaultConsumer;
import com.rabbitmq.client.Envelope;
/**
* @模块名:basic
* @包名:cn.cc.basic.exchange.direct
* @类名称: MulitConsumerOneQueue
* @类描述:【类描述】一个队列多个消费者,则会表现出消息在消费者之间的轮询发送。
* @版本:1.0
* @创建人:cc
* @创建时间:2018年12月12日下午3:49:17
*/
public class MulitConsumerOneQueue {
// 连接信息
public final static String USER_NAME = "test";
public final static String PASSWORD = "123456";
public final static String VHOST = "%2Ftest";
public final static String HOST = "47.99.159.236";
public final static Integer PORT = 5672;
// 队列信息
// 交换机名称
public final static String EXCHANGE_NAME = "test-exchangeName_direct_1";
// 路由key
public final static String[] ROUTINGKEYS = { "test-routingKey_direct_1", "test-routingKey_direct_2",
"test-routingKey_direct_3" };
// 队列名称
public final static String QUEUENAME = "test-queueName_direct_1";
/**
*
* @模块名:basic
* @包名:cn.cc.basic.exchange.direct
* @类名称: ConsumerWorker
* @类描述:【类描述】创建消费者
* @版本:1.0
* @创建人:cc
* @创建时间:2018年12月12日下午3:50:21
*/
private static class ConsumerWorker implements Runnable {
final Connection connection;
final String queueName;
public ConsumerWorker(Connection connection, String queueName) {
this.connection = connection;
this.queueName = queueName;
}
public void run() {
try {
/* 创建一个信道,意味着每个线程单独一个信道 */
final Channel channel = connection.createChannel();
channel.exchangeDeclare(EXCHANGE_NAME, "direct");
/* 声明一个队列,rabbitmq,如果队列已存在,不会重复创建 */
channel.queueDeclare(queueName, false, false, false, null);
// 消费者名字,打印输出用
final String consumerName = Thread.currentThread().getName();
for (String routingkey : ROUTINGKEYS) {
// (多重绑定)
channel.queueBind(queueName, DirectProducer.EXCHANGE_NAME, routingkey);
}
System.out.println(" [" + consumerName + "] Waiting for messages:");
// 创建队列消费者
final Consumer consumerA = 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(consumerName + " Received " + envelope.getRoutingKey() + ":'" + message
+ "'");
}
};
channel.basicConsume(queueName, true, consumerA);
}
catch (Exception e) {
e.printStackTrace();
}
}
}
public static void main(String[] argv) {
try {
ConnectionFactory factory = new ConnectionFactory();
factory.setUri("amqp://" + USER_NAME + ":" + PASSWORD + "@" + HOST + ":" + PORT + "/" + VHOST);
// 打开连接和创建频道,与发送端一样
Connection connection = factory.newConnection();
// 3个线程,线程之间共享队列,一个队列多个消费者
for (int i = 0; i < 3; i++) {
/* 将队列名作为参数,传递给每个线程 */
Thread worker = new Thread(new ConsumerWorker(connection, EXCHANGE_NAME));
worker.start();
}
}
catch (Exception e) {
e.printStackTrace();
}
}
}
通用rabbitMQ消费者:
package cn.cc.basic.exchange.direct;
import com.rabbitmq.client.AMQP;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import com.rabbitmq.client.Consumer;
import com.rabbitmq.client.DefaultConsumer;
import com.rabbitmq.client.Envelope;
/**
* @模块名:basic
* @包名:cn.cc.basic.exchange.direct
* @类名称: NormalConsumer
* @类描述:【类描述】通用rabbit消费者
* @版本:1.0
* @创建人:cc
* @创建时间:2018年12月12日上午11:23:44
*/
public class NormalConsumer {
// 连接信息
public final static String USER_NAME = "test";
public final static String PASSWORD = "123456";
public final static String VHOST = "%2Ftest";
public final static String HOST = "47.99.159.236";
public final static Integer PORT = 5672;
// 队列信息
// 交换机名称
public final static String EXCHANGE_NAME = "test-exchangeName_direct_1";
// 路由key
public final static String[] ROUTINGKEYS = { "test-routingKey_direct_1", "test-routingKey_direct_2",
"test-routingKey_direct_3" };
// 队列名称
public final static String QUEUENAME = "test-queueName_direct_1";
public static void main(String[] args) {
try {
/**
* mq连接
*/
ConnectionFactory factory = new ConnectionFactory();
// “guest”/“guest”默认情况下,仅限于localhost连接
factory.setUri("amqp://" + USER_NAME + ":" + PASSWORD + "@" + HOST + ":" + PORT + "/" + VHOST);
Connection conn = factory.newConnection();
/**
* 创建信道
*/
Channel channel = conn.createChannel();
/**
* 创建交换器
*/
// 指明交换器名称+类型+是否持久化
// BuiltinExchangeType.DIRECT
channel.exchangeDeclare(EXCHANGE_NAME, "direct", false);
/**
* 声明一个队列
*/
// 指定队列名称+是否持久化+是否独占+非自动删除
channel.queueDeclare(QUEUENAME, false, false, false, null);
/**
* 绑定,将队列和交换器通过路由键进行绑定
*/
channel.queueBind(QUEUENAME, EXCHANGE_NAME, ROUTINGKEYS[0]);
// channel.queueBind(QUEUENAME, EXCHANGE_NAME, ROUTINGKEYS[2]);
System.out.println("waiting for message........");
/**
* 声明了一个消费者
*/
final Consumer consumer = new DefaultConsumer(channel) {
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties,
byte[] body) {
String message;
try {
message = new String(body, "UTF-8");
System.out.println("Received[" + envelope.getRoutingKey() + "]" + message);
}
catch (Exception e) {
e.printStackTrace();
}
}
};
/**
* 消费者正式开始消费指定队列上消息
*/
channel.basicConsume(QUEUENAME, true, consumer);
}
catch (Exception e) {
e.printStackTrace();
}
}
}
b.fanout类型Exchange
fanout:把所有发送到该Exchange的消息投递到所有与它绑定的队列中。
即无需进行routingKey匹配只要进行exchange绑定:
String exchangeName = "exchangeName_fanout";// 交换机名称
String routingKey = "routingKey_fanout";// 路由key
String queueName = "queueName_fanout";// 队列名称
String message = "Hello RabbitMQ";// 消息内容
channel.exchangeDeclare(exchangeName, "fanout", true);
channel.queueDeclare(queueName, true, false, false, null);
channel.queueBind(queueName, exchangeName, routingKey);
channel.basicPublish(exchangeName, "", null, message.getBytes("UTF-8"));//routingKey为空即可
实战 :
Fanout类型交换机消息生产类:
package cn.cc.basic.exchange.fanout;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
/**
* @模块名:basic
* @包名:cn.cc.basic.exchange.fanout
* @类名称: FanoutProducer
* @类描述:【类描述】 Fanout类型交换机消息生产类
* @版本:1.0
* @创建人:cc
* @创建时间:2018年12月12日下午3:59:57
*/
public class FanoutProducer {
// 连接信息
public final static String USER_NAME = "test";
public final static String PASSWORD = "123456";
public final static String VHOST = "/test";
public final static String HOST = "47.99.159.236";
public final static Integer PORT = 5672;
// 队列信息
// 交换机名称
public final static String EXCHANGE_NAME = "test-exchangeName_fanout_1";
// 路由key
public final static String[] ROUTINGKEYS = { "test-routingKey_fanout_1", "test-routingKey_fanout_2",
"test-routingKey_fanout_3" };
// 队列名称
public final static String QUEUENAME = "test-queueName_fanout_1";
public static void main(String[] args) {
try {
ConnectionFactory factory = new ConnectionFactory();
// “guest”/“guest”默认情况下,仅限于localhost连接
factory.setUsername(USER_NAME);// 用户名
factory.setPassword(PASSWORD);// 用户密码
factory.setVirtualHost(VHOST);// 虚拟主机
factory.setHost(HOST);// 主机名
factory.setPort(PORT);// 端口号
// factory.setUri("amqp://" + USER_NAME + ":" + PASSWORD + "@" + HOST + ":" + PORT +"/"+ VHOST);
Connection conn = factory.newConnection();
/**
* 创建信道
*/
Channel channel = conn.createChannel();
/**
* 创建交换器
*/
// 指明交换器名称+类型+是否持久化
// BuiltinExchangeType.fanout
channel.exchangeDeclare(EXCHANGE_NAME, "fanout", false);
/**
* 推送消息
*/
for (int i = 0; i < 9; i++) {
// 指明推送的路由键
String routingkey = ROUTINGKEYS[i % 3];
// 推送消息
String msg = "Hellol,RabbitMq_" + routingkey;
// 发布消息,需要参数:交换器,路由键,
channel.basicPublish(EXCHANGE_NAME, routingkey, null, msg.getBytes());
System.out.println("Sent " + EXCHANGE_NAME + ":" + routingkey + ":" + msg);
}
}
catch (Exception e) {
e.printStackTrace();
}
}
}
单个绑定:
package cn.cc.basic.exchange.fanout;
import com.rabbitmq.client.AMQP;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import com.rabbitmq.client.Consumer;
import com.rabbitmq.client.DefaultConsumer;
import com.rabbitmq.client.Envelope;
/**
* @模块名:basic
* @包名:cn.cc.basic.exchange.fanout
* @类名称: Customer1
* @类描述:【类描述】单个绑定
* @版本:1.0
* @创建人:cc
* @创建时间:2018年12月12日下午4:01:29
*/
public class Customer1 {
// 连接信息
public final static String USER_NAME = "test";
public final static String PASSWORD = "123456";
public final static String VHOST = "%2Ftest";
public final static String HOST = "47.99.159.236";
public final static Integer PORT = 5672;
// 队列信息
// 交换机名称
public final static String EXCHANGE_NAME = "test-exchangeName_fanout_1";
// 路由key
public final static String[] ROUTINGKEYS = { "test-routingKey_fanout_1", "test-routingKey_fanout_2",
"test-routingKey_fanout_3" };
// 队列名称
public final static String QUEUENAME = "test-queueName_fanout_1";
public static void main(String[] args) {
try {
/**
* mq连接
*/
ConnectionFactory factory = new ConnectionFactory();
// “guest”/“guest”默认情况下,仅限于localhost连接
factory.setUri("amqp://" + USER_NAME + ":" + PASSWORD + "@" + HOST + ":" + PORT + "/" + VHOST);
Connection conn = factory.newConnection();
/**
* 创建信道
*/
Channel channel = conn.createChannel();
/**
* 创建交换器
*/
// 指明交换器名称+类型+是否持久化
// BuiltinExchangeType.fanout
channel.exchangeDeclare(EXCHANGE_NAME, "fanout", false);
/**
* 声明一个队列
*/
// 指定队列名称+是否持久化+是否独占+非自动删除
channel.queueDeclare(QUEUENAME, false, false, false, null);
/**
* 绑定,将队列和交换器通过路由键进行绑定
*/
channel.queueBind(QUEUENAME, EXCHANGE_NAME, ROUTINGKEYS[0]);
System.out.println("waiting for message........");
/**
* 声明了一个消费者
*/
final Consumer consumer = new DefaultConsumer(channel) {
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties,
byte[] body) {
String message;
try {
message = new String(body, "UTF-8");
System.out.println("Received[" + envelope.getRoutingKey() + "]" + message);
}
catch (Exception e) {
e.printStackTrace();
}
}
};
/**
* 消费者正式开始消费指定队列上消息
*/
channel.basicConsume(QUEUENAME, true, consumer);
}
catch (Exception e) {
e.printStackTrace();
}
}
}
多重绑定:
package cn.cc.basic.exchange.fanout;
import cn.cc.basic.exchange.direct.DirectProducer;
import com.rabbitmq.client.AMQP;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import com.rabbitmq.client.Consumer;
import com.rabbitmq.client.DefaultConsumer;
import com.rabbitmq.client.Envelope;
/**
* @模块名:basic
* @包名:cn.cc.basic.exchange.fanout
* @类名称: Customer2
* @类描述:【类描述】多重绑定
* @版本:1.0
* @创建人:cc
* @创建时间:2018年12月12日下午4:01:44
*/
public class Customer2 {
// 连接信息
public final static String USER_NAME = "test";
public final static String PASSWORD = "123456";
public final static String VHOST = "%2Ftest";
public final static String HOST = "47.99.159.236";
public final static Integer PORT = 5672;
// 队列信息
// 交换机名称
public final static String EXCHANGE_NAME = "test-exchangeName_fanout_1";
// 路由key
public final static String[] ROUTINGKEYS = { "test-routingKey_fanout_1", "test-routingKey_fanout_2",
"test-routingKey_fanout_3" };
// 队列名称
public final static String QUEUENAME = "test-queueName_fanout_1";
public static void main(String[] args) {
try {
/**
* mq连接
*/
ConnectionFactory factory = new ConnectionFactory();
// “guest”/“guest”默认情况下,仅限于localhost连接
factory.setUri("amqp://" + USER_NAME + ":" + PASSWORD + "@" + HOST + ":" + PORT + "/" + VHOST);
Connection conn = factory.newConnection();
/**
* 创建信道
*/
Channel channel = conn.createChannel();
/**
* 创建交换器
*/
// 指明交换器名称+类型+是否持久化
// BuiltinExchangeType.fanout
channel.exchangeDeclare(EXCHANGE_NAME, "fanout", false);
/**
* 声明一个队列
*/
// 指定队列名称+是否持久化+是否独占+非自动删除
channel.queueDeclare(QUEUENAME, false, false, false, null);
/**
* 绑定,将队列和交换器通过路由键进行绑定
*/
for (String routingkey : ROUTINGKEYS) {
// (多重绑定)
channel.queueBind(QUEUENAME, DirectProducer.EXCHANGE_NAME, routingkey);
}
System.out.println("waiting for message........");
/**
* 声明了一个消费者
*/
final Consumer consumer = new DefaultConsumer(channel) {
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties,
byte[] body) {
String message;
try {
message = new String(body, "UTF-8");
System.out.println("Received[" + envelope.getRoutingKey() + "]" + message);
}
catch (Exception e) {
e.printStackTrace();
}
}
};
/**
* 消费者正式开始消费指定队列上消息
*/
channel.basicConsume(QUEUENAME, true, consumer);
}
catch (Exception e) {
e.printStackTrace();
}
}
}
c.topic类型Exchange
topic:将消息路由到binding key与routing key模式匹配的队列中。
即无需进行routingKey匹配只要进行exchange绑定:
String exchangeName = "exchangeName_topic";// 交换机名称
String queueName = "queueName_topic";// 队列名称
String message = "Hello RabbitMQ";// 消息内容
String bindKey = "bindKey.#";// 绑定key
String routingKey = "bindKey.topic";// 路由key
channel.exchangeDeclare(exchangeName, "topic", true);
channel.queueDeclare(queueName, true, false, false, null);
channel.queueBind(queueName, exchangeName, bindKey);
channel.basicPublish(exchangeName, routingKey, null, message.getBytes("UTF-8"));// routingKey与bindKey路由批跑
实战:
topic类型交换机消息生产类:
package cn.cc.basic.exchange.topic;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
/**
* @模块名:basic
* @包名:cn.cc.basic.exchange.topic
* @类名称: topicProducer
* @类描述:【类描述】 topic类型交换机消息生产类
* @版本:1.0
* @创建人:cc
* @创建时间:2018年12月12日下午3:59:57
*/
public class TopicProducer {
// 连接信息
public final static String USER_NAME = "test";
public final static String PASSWORD = "123456";
public final static String VHOST = "/test";
public final static String HOST = "47.99.159.236";
public final static Integer PORT = 5672;
// 队列信息
// 交换机名称
public final static String EXCHANGE_NAME = "test-exchangeName_topic_1";
// 路由key
public final static String[] ROUTINGKEYS1 = { "test-A", "test-B", "test-C" };
public final static String[] ROUTINGKEYS2 = { "test-a", "test-b", "test-c" };
public final static String[] ROUTINGKEYS3 = { "test-1", "test-2", "test-3" };
// 队列名称
public final static String QUEUENAME = "test-queueName_topic_1";
public static void main(String[] args) {
try {
ConnectionFactory factory = new ConnectionFactory();
// “guest”/“guest”默认情况下,仅限于localhost连接
factory.setUsername(USER_NAME);// 用户名
factory.setPassword(PASSWORD);// 用户密码
factory.setVirtualHost(VHOST);// 虚拟主机
factory.setHost(HOST);// 主机名
factory.setPort(PORT);// 端口号
// factory.setUri("amqp://" + USER_NAME + ":" + PASSWORD + "@" + HOST + ":" + PORT +"/"+ VHOST);
Connection conn = factory.newConnection();
/**
* 创建信道
*/
Channel channel = conn.createChannel();
/**
* 创建交换器
*/
// 指明交换器名称+类型+是否持久化
// BuiltinExchangeType.topic
channel.exchangeDeclare(EXCHANGE_NAME, "topic", false);
/**
* 推送消息
*/
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
for (int k = 0; k < 3; k++) {
// 发送的消息
String routeKey = ROUTINGKEYS1[i % 3] + "." + ROUTINGKEYS2[j % 3] + "." + ROUTINGKEYS3[k % 3];
String message = "Hello Topic_[" + i + "," + j + "," + k + "]";
channel.basicPublish(EXCHANGE_NAME, routeKey, null, message.getBytes());
System.out.println(" [x] Sent '"+routeKey+":" + message + "'");
}
}
}
}
catch (Exception e) {
e.printStackTrace();
}
}
}
topic类型消费者:
package cn.cc.basic.exchange.topic;
import com.rabbitmq.client.AMQP;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import com.rabbitmq.client.Consumer;
import com.rabbitmq.client.DefaultConsumer;
import com.rabbitmq.client.Envelope;
/**
* @模块名:basic
* @包名:cn.cc.basic.exchange.topic
* @类名称: TopicCutomer
* @类描述:【类描述】
* @版本:1.0
* @创建人:cc
* @创建时间:2018年12月12日下午4:16:05
*/
public class TopicCustomer {
// 连接信息
public final static String USER_NAME = "test";
public final static String PASSWORD = "123456";
public final static String VHOST = "%2Ftest";
public final static String HOST = "47.99.159.236";
public final static Integer PORT = 5672;
// 队列信息
// 交换机名称
public final static String EXCHANGE_NAME = "test-exchangeName_topic_1";
// 队列名称
public final static String QUEUENAME = "test-queueName_topic_1";
public static void main(String[] args) {
try {
/**
* mq连接
*/
ConnectionFactory factory = new ConnectionFactory();
// “guest”/“guest”默认情况下,仅限于localhost连接
factory.setUri("amqp://" + USER_NAME + ":" + PASSWORD + "@" + HOST + ":" + PORT + "/" + VHOST);
Connection conn = factory.newConnection();
/**
* 创建信道
*/
Channel channel = conn.createChannel();
/**
* 创建交换器
*/
// 指明交换器名称+类型+是否持久化
// BuiltinExchangeType.topic
channel.exchangeDeclare(EXCHANGE_NAME, "topic", false);
// 声明一个随机队列
// String queueName = channel.queueDeclare().getQueue();
// channel.queueDeclare(QUEUENAME, false, false, false, null);
/**
* 声明一个队列
*/
// 指定队列名称+是否持久化+是否独占+非自动删除
channel.queueDeclare(QUEUENAME, false, false, false, null);
/**
* 绑定,将队列和交换器通过路由键进行绑定
*
* 注意多重绑定后是不会覆盖前面的
*/
// #接收所有路由键信息
// channel.queueBind(QUEUENAME, EXCHANGE_NAME, "#");
// 若只匹配前后用#,其他用*
channel.queueUnbind(QUEUENAME, EXCHANGE_NAME, "#");
channel.queueBind(QUEUENAME, EXCHANGE_NAME, "test-A.#");
// channel.queueBind(QUEUENAME, EXCHANGE_NAME, "#.test-1");
// channel.queueBind(QUEUENAME, EXCHANGE_NAME, "*.test-a.*");
System.out.println("waiting for message........");
/**
* 声明了一个消费者
*/
final Consumer consumer = new DefaultConsumer(channel) {
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties,
byte[] body) {
String message;
try {
message = new String(body, "UTF-8");
System.out.println("Received[" + envelope.getRoutingKey() + "]" + message);
}
catch (Exception e) {
e.printStackTrace();
}
}
};
/**
* 消费者正式开始消费指定队列上消息
*/
channel.basicConsume(QUEUENAME, true, consumer);
}
catch (Exception e) {
e.printStackTrace();
}
}
}
d.headers类型Exchange
headers: Headers Exchange不同于上面三种Exchange,它是根据Message的一些头部信息来分发过滤Message,忽略routing key的属性,如果Headers信息和message消息的头信息相匹配,那么这条消息就匹配上了。
此类型不常用,第一步需在绑定时配置参数信息即Headers信息,第二步需在推送时构建BasicProperties,且BasicProperties.headers需与绑定一致:
import com.rabbitmq.client.AMQP.BasicProperties.Builder;
String exchangeName = "exchangeName_headers";// 交换机名称
String queueName = "queueName_headers";// 队列名称
String message = "Hello RabbitMQ";// 消息内容
Map<String, Object> headers = new HashMap<String, Object>();//构建headers
headers.put("param1", "param1");
headers.put("param2", "param2");
headers.put("param3", "param3");
headers.put("param4", "param4");
channel.exchangeDeclare(exchangeName, "headers", true);
channel.queueDeclare(queueName, true, false, false, null);
channel.queueBind(queueName, exchangeName, "",headers);
Builder builder = new Builder();
builder.headers(headers);//构建headers
channel.basicPublish(exchangeName, "", builder.build(), message.getBytes("UTF-8"));// routingKey与bindKey路由批跑
5.订阅消息消费(Receiving Messages by Subscription ("Push API"))
使用channel.basicConsume进行订阅消息然后消费;
在这里强烈建议使用自定义消费者标签,且消费者标签需不同;同时建议手动回调,保障业务安全;
上面发布的消息都可以用下面的方式进行消费:
import com.rabbitmq.client.DefaultConsumer;
boolean autoAck = false;// 标记手动回调
String myConsumerTag = "myConsumerTag";//消费者标签
channel.basicConsume(queueName, autoAck, myConsumerTag, new DefaultConsumer(channel) {
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties,
byte[] body) throws IOException {
String routingKey = envelope.getRoutingKey();
String contentType = properties.getContentType();
long deliveryTag = envelope.getDeliveryTag();
String message = new String(body, "UTF-8");// 获取消息体
System.out.println(routingKey + ":" + contentType + ":" + deliveryTag + ":" + message);
channel.basicAck(deliveryTag, false);// 手动回调
}
});
注:消费者的回调在与实例化其Channel的线程分开的线程池中调度。这意味着Consumer可以安全地在Connection或Channel上调用阻塞方法,例如 Channel#queueDeclare或 Channel#basicCancel。
每个Channel都有自己的调度线程。对于每个渠道一个消费者的最常见用例,这意味着消费者不会占用其他消费者。如果每个Channel有多个消费者,请注意长时间运行的消费者可能会阻止向该Channel上的其他消费者发送回叫 。
6.检索单个消息消费(Retrieving Individual Messages ("Pull API"))
使用channel.basicGet获取单个消息进行消费;
建议如上手动回调:
boolean autoAck = false;// 标记手动回调
GetResponse response = channel.basicGet(queueName, autoAck);// 手动获取单个消息
if (response == null) {
// No message retrieved.
} else {
AMQP.BasicProperties props = response.getProps();// 获取BasicProperties
byte[] body = response.getBody();// 获取消息体
long deliveryTag = response.getEnvelope().getDeliveryTag();// 获取消息id
System.out.println(deliveryTag + ":" + new String(body, "UTF-8") + ":" + props.getHeaders());
channel.basicAck(deliveryTag, false); // 手动回调
}
更多可参考官网:http://www.rabbitmq.com/api-guide.html