java客户端编程接口
RabbitMQ开放的编程接口主要是通过下面4个类来实现了。
|类名|作用|说明| |--|--|--| |Connection|连接对象,表示客户端与服务器端的一个连接。|用于打开Channel,注册connection各个生命周期的处理方法,以及关闭连接。| |ConnectionFactory|用于创建connection对象。|可以设置服务器的地址、端口、用户名、密码、虚拟空间等全局配置| |Channel|AMQP协议操作的入口|对消息的绝大多数操作都是通过channel操作的| |Consumer|消费者对象|该对象里面有一些对消息到达后的调用方法|
#Connection and Channel
核心api类Connection、Channel分别代表AMQP 0-9-1协议中的connection和channel。
Connection是用来连接AMQP broker的。使用方式如下
ConnectionFactory factory = new ConnectionFactory(); // 创建一个连接工厂
factory.setUsername(userName); // 设置用户名
factory.setPassword(password); // 设置密码
factory.setVirtualHost(virtualHost); // 设置虚拟主机
factory.setHost(hostName); // 设置主机域名
factory.setPort(portNumber); // 设置端口
Connection conn = factory.newConnection(); // 创建一个连接
以上配置参数,都有默认的值。默认值是连接到本地的服务器。
其实你也何以通过URIs 来连接broker。
ConnectionFactory factory = new ConnectionFactory();
factory.setUri("amqp://userName:password@hostName:portNumber/virtualHost"); // 类似于http协议请求一样
Connection conn = factory.newConnection();
得到connection后,则可以通过connection打开channel
Channel channel = conn.createChannel();
现在你可以通过channel发送消息和接受消息了。
记住,但使用完毕后,要记得关闭连接。不过一般的程序,应该会一直发送消息或则等待消息。
channel.close();
conn.close();
注意。这儿手动关闭channel并不是严格必须的,但却是一个较好的编码习惯。当connection关闭的时候,也会自动去关闭channel。
#使用Exchange and Queue
客户端应用程序工作是需要exchange和queue的。exchange和queue在使用之前必须先定义。定义他们的类型,名字,在必要的时候创建他们,以确保他们是必须存在。
我们继续上面的例子,通过代码来定义一个exchange和queue,并把他们两者绑定在一起。
channel.exchangeDeclare(exchangeName, "direct",true);
String queueName = channel.queueDeclar().getQueue();
channel.queueBind(queueName, exchangeName, routingKey);
通过上面几行代码的参数配置,可以得到自定义的channel和queue。
- 一个可持久化,不会自动删除,的direct类型的exchange
- 一个可持久化,非独占,非自动删除的,服务器命名的队列。
##Channels and Concurrency Considerations(Thread Safety) Channel对象是不能在多线程之间共享的。应用程序应该保证一个channel对象只在一个线程中使用。
Publishing message
消息是先发送到exchange的。exchange再根据routingkey发送的对应的queue中去。 发送消息要用到Channel.basicPublish()方法。
byte[] messageBodyBytes = "Hello world!".getBytes("UTF-8");
channel.basicPublish(exchangeName, routingKey, null, messageBodyBytes);
Receiving messages by subscription
import com.rabbitmq.client.Consumer;
import com.rabbitmq.client.DefaultConsumer;
接收消息,需要用到Consumer接口及其子类。当有消息到达时,消息会自动的被投送到Consumer,而不是手动的去获取。
当执行消费者Consumer中的API方法时,代码中往往会用到消费的tags。一个channel中可以注册多个消费者,所以每个消费者要有自己的tag用于区分。
boolean autoAck = false; // 是否开启自动确认消息回复,此处的false需要手动回复消息确认。
// 下面的语句类似于在channel上注册了一个消费者。
channel.basicConsume(queueName, autoAck, "myConsumerTag",
new DefaultConsumer(channel) {
// Called when a basic.recover-ok is received
@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();
// (process the message components here ...)
channel.basicAck(deliveryTag, false); // 手动回复消息确认
}
});
通过上面的代码,我们就在channel中注册了一个消费者。当然你也可以明确的通过下面的一句代码放弃一个消费者。
channel.basicCannel(consumerTag);
完整的代码
消息生产者,Send类
package com.benny.amqp.rabbitmq_provider;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import com.rabbitmq.client.MessageProperties;
public class Send {
private final static String TASK_QUEUE_NAME = "hello_task";
public static void main(String[] argv) throws java.io.IOException {
try {
ConnectionFactory factory = new ConnectionFactory(); // 连接工厂对象
factory.setHost("localhost"); // 设置消息broker服务器的域名地址,其他配置项使用默认值
Connection connection = factory.newConnection(); // 创建一个连接
Channel channel = connection.createChannel(); // 通过connection得到一个channel
// 定义一个队列,
channel.queueDeclare(TASK_QUEUE_NAME, // 队列名
true, // 队列可持久化
false, // 队列非独占,如果是true则只被一个连接(connection)使用,而且当连接关闭后队列即被删除
false, // 当最后一个消费者退订后,队列立即删除
null // ther properties (construction arguments) for the queue,一些消息代理用他来完成类似与TTL的某些额外功能
);
String message = "Hello World .......";
// channel.basicPublish("", QUEUE_NAME, null, message.getBytes());
System.out.println(" [x] Sent '" + message + "'");
// message = getMessage(argv);
channel.basicPublish("", // exchangeName, 使用默认exchange,名字有服务器生产。
TASK_QUEUE_NAME, // routingKey, 这儿使用与队列名相同的名字
MessageProperties.PERSISTENT_TEXT_PLAIN, // 消息的属性, 内容是文本,并可以持久化
message.getBytes() // 消息的内容
);
System.out.println(" [x] Sent '" + message + "'");
channel.close(); // 关闭channel
connection.close(); // 关闭连接
} catch (Exception e) {
// TODO: handle exception
} finally {
}
}
}
消息消费者,Recv类
package com.benny.amqp.rabbitmq_provider;
import java.io.IOException;
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;
public class Recv {
private final static String TASK_QUEUE_NAME = "hello_task";
public static void main(String[] argv) throws java.io.IOException, java.lang.InterruptedException {
try {
ConnectionFactory factory = new ConnectionFactory();
factory.setHost("localhost");
Connection connection = factory.newConnection();
Channel channel = connection.createChannel();
channel.queueDeclare(TASK_QUEUE_NAME, false, false, false, null);
System.out.println(" [*] Waiting for messages. To exit press CTRL+C");
channel.basicQos(1); // 设置限速。在多个消费者共享一个队列的案例中,明确指定在收到下一个确认回执前每个消费者一次可以接受多少条消息
final Consumer consumer = 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(" [x] Received '" + message + "'");
try {
doWork(message);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}finally{
System.out.println(" [x] "+message+" Done");
channel.basicAck(envelope.getDeliveryTag(), false);
}
}
};
channel.basicConsume(TASK_QUEUE_NAME, false, consumer); // 把上面new出的消费者注册到channel中。
} catch (Exception e) {
// TODO: handle exception
}
}
private static void doWork(String task) throws InterruptedException {
for (char ch : task.toCharArray()) {
if (ch == '.')
Thread.sleep(3000);
}
}
}