本节将会通过一个RabbitMQ的入门helloWord程序,来介绍一下RabbitMQ的java客户端的简单使用、怎么创建连接声明队列、怎么发送消息、怎么接收消息等,这样可以对客户端的使用有个大概的了解,有助于后面的学习。
概述
前面介绍过RabbitMQ的官方文档非常丰富,当然会有对应的入门教程供大家参考,其地址为 http://www.rabbitmq.com/getstarted.html,下面是其截图(下面没有截全),里面主要包含六个部分:helloworld入门、工作队列、发布/订阅、路由、主题。每部分都会有详细说明,而且会有Java、Python、PHP等这种语言的客户端实现,如果您的英文可以的话可以直接看这个文档哈。
本节先介绍第一部分,也是程序员学习每个技术的第一个代码——helloworld
helloworld程序是最简单的一个,如上图所示:P代表消息生产者,中间的红色部分代表消息队列,C代表消息消费者(后面的图例也是如此,后面就不再赘述),helloworld模式中生产者将消息发送到队列,然后只有一个消费者消费消息,也就是一对一的。
项目搭建
项目GitHub地址 https://github.com/RookieMember/RabbitMQ-Learning.git。好了现在直接开始搭建工程码代码了,下面只记录重要的部分,简单的步骤就省略了。项目采用maven搭建,依赖如下
<dependencies>
<dependency>
<groupId>com.rabbitmq</groupId>
<artifactId>amqp-client</artifactId>
<version>4.2.1</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.7.7</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.3.2</version>
</dependency>
</dependencies>
log4j.properties配置如下:
log4j.rootLogger=DEBUG,A1
log4j.logger.com.taotao = DEBUG
log4j.logger.org.mybatis = DEBUG
log4j.appender.A1=org.apache.log4j.ConsoleAppender
log4j.appender.A1.layout=org.apache.log4j.PatternLayout
log4j.appender.A1.layout.ConversionPattern=%-d{yyyy-MM-dd HH:mm:ss,SSS} [%t] [%c]-[%p] %m%n
由于生产者消费者都要创建连接,所以我们将创建连接的操作封装为一个单独的类:里面使用的虚拟主机"/myvhost"我自己通过web管理台创建的,如果你不想创建,可以使用默认的 "/",另外关于创建连接相关的类及基础概念在 《(2)RabbitMQ基础概念及工作流程详解》 中已经介绍了,这里就不再重复介绍了。
package cn.wkp.rabbitmq.util;
import com.rabbitmq.client.ConnectionFactory;
import com.rabbitmq.client.Connection;
public class ConnectionUtil {
public static Connection getConnection() throws Exception {
//定义连接工厂
ConnectionFactory factory = new ConnectionFactory();
//设置服务地址
factory.setHost("192.168.74.4");
//端口
factory.setPort(5672);
//设置账号信息,用户名、密码、vhost
factory.setVirtualHost("/myvhost");
factory.setUsername("wkp");
factory.setPassword("123456");
// 通过工程获取连接
Connection connection = factory.newConnection();
return connection;
}
}
下面我们看一下消息的发送者代码,这里我们介绍下生产者中的两个方法:
1:、声明队列方法:channel.queueDeclare(String queue, boolean durable, boolean exclusive, boolean autoDelete, Map<String, Object> arguments),该方法作用是声明一个队列,如果队列不存在则创建,如果已经存在则什么都不做。参数说明:
queue:表示队列名称,
durable:true表示队列持久化,false表示非持久化(这里后面会专门介绍),
exclusive:排他队列,如果一个队列被声明为排他队列,该队列仅对首次申明它的连接可见,并在连接断开时自动删除。这里需要注意三点:1. 排他队列是基于连接可见的,同一连接的不同信道是可以同时访问同一连接创建的排他队列;2.“首次”,如果一个连接已经声明了一个排他队列,其他连接是不允许建立同名的排他队列的,这个与普通队列不同;3.即使该队列是持久化的,一旦连接关闭或者客户端退出,该排他队列都会被自动删除的,这种队列适用于一个客户端发送读取消息的应用场景。
autoDelete:自动删除,如果该队列没有任何订阅的消费者的话,该队列会被自动删除。这种队列适用于临时队列。
2、发送消息方法:channel.basicPublish(String exchange, String routingKey, BasicProperties props, byte[] body),该方法的作用是发送一条消息,参数说明:
exchange:交换机名称
routingKey:路由键
props:发送的消息携带的属性
body:发送的消息内容
package cn.wkp.rabbitmq.newest.simple;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import cn.wkp.rabbitmq.util.ConnectionUtil;
public class Send {
private final static String QUEUE_NAME = "test_queue";
public static void main(String[] argv) throws Exception {
// 获取到连接以及mq通道
Connection connection = ConnectionUtil.getConnection();
// 从连接中创建通道
Channel channel = connection.createChannel();
// 声明(创建)队列
channel.queueDeclare(QUEUE_NAME, false, false, false, null);
// 消息内容
String message = "Hello World!";
channel.basicPublish("", QUEUE_NAME, null, message.getBytes());
System.out.println(" [x] Sent '" + message + "'");
//关闭通道和连接
channel.close();
connection.close();
}
}
下面我们看下消息消费者的代码,下面先用老的api写了一个,因为 QueueingConsumer 这个类已经被淘汰了,后面会用新的api重新写一个。我们介绍下消费者中的两个方法:
1、监听队列:channel.basicConsume(String queue, boolean autoAck, Consumer callback)表示监听队列,三个参数分别是队列名称,是否自动发送Ack确认,消费者对象
2、等待获取消息:channel.nextDelivery()该方法在获取到消息之前会一直阻塞,直到获取消息才会向下执行。
package cn.wkp.rabbitmq.newest.simple;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.QueueingConsumer;
import com.rabbitmq.client.QueueingConsumer.Delivery;
import cn.wkp.rabbitmq.util.ConnectionUtil;
public class RecvOld {
private final static String QUEUE_NAME = "test_queue";
public static void main(String[] argv) throws Exception {
// 获取到连接以及mq通道
Connection connection = ConnectionUtil.getConnection();
Channel channel = connection.createChannel();
// 声明队列
channel.queueDeclare(QUEUE_NAME, false, false, false, null);
// 定义队列的消费者
QueueingConsumer consumer = new QueueingConsumer(channel);
// 监听队列
channel.basicConsume(QUEUE_NAME, true, consumer);
//while循环不让程序退出
while(true) {
Delivery delivery = consumer.nextDelivery();
System.out.println("老的消费者api收到消息:"+new String(delivery.getBody()));
}
}
}
运行消费者及生产者代码可以看到如下输出:
生产者:
[x] Sent 'Hello World!'
消费者:
老的消费者api收到消息:Hello World!
新的消费者代码如下:
package cn.wkp.rabbitmq.newest.simple;
import java.io.IOException;
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;
import cn.wkp.rabbitmq.util.ConnectionUtil;
public class RecvNew {
private final static String QUEUE_NAME = "test_queue";
public static void main(String[] argv) throws Exception {
// 获取到连接以及mq通道
Connection connection = ConnectionUtil.getConnection();
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 {
System.out.println("新的消费者api收到消息:" + new String(body));
}
};
// 监听队列
channel.basicConsume(QUEUE_NAME, true, consumer);
}
}
运行消费者及生产者代码可以看到如下输出:
生产者:
[x] Sent 'Hello World!'
消费者:
新的消费者api收到消息:Hello World!
关于RabbitMQ客户端使用的第一个入门程序就先介绍到这里,相信你对其使用已经有了一个基本的认识,下一节将会给大家介绍一下work queues,也就是工作队列。