RabbitMQ学习3.1:java操作RabbitMQ(一)

环境:

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"));// 发送消息到队列中

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为空即可

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路由批跑

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

猜你喜欢

转载自blog.csdn.net/u010904188/article/details/81066040
今日推荐