最近公司安排简单任务,在两套系统中做数据对接,使用到了RabbitMQ消息中间件,由于也是第一次在正式环境中使用,在网上也查询了很多资料,也有很多具体的代码,但也只知道那是别人实现,而不知道具体的实现流程,所以参照着看了一些源代码,写了一些注释:
package MessageQueue;
import java.io.IOException;
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;
public class MQTest {
public static void main(String[] args) {
//sendMessage();
receiveMessage();
}
//消息发送
public static void sendMessage(){
ConnectionFactory factory = null;
Connection connection = null;
Channel channel= null;
try {
// 创建MQ工厂类,并配置相关参数
factory = new ConnectionFactory();
factory.setHost("192.168.0.116");// MQip,默认为localhost
// 鬼才知道为什么是这个端口,明明是15672,然后在MQ服务器上看到了这个端口,试了一下就通了
factory.setPort(5673);// MQ端口,默认为5672(SSL状态下为5671)
// 用户名和密码默认为guest和guest,如果不一致,自行设置
// 虚拟IP默认为 /,如果不一致自行设置,这两个参数都可以在MQ服务器上找到
// factory.setUsername("guest");
// factory.setPassword("guset");
// factory.setVirtualHost("/");
// 启用断开恢复机制
// factory.setAutomaticRecoveryEnabled(true);
System.out.println("Virtual Host:" + factory.getVirtualHost());
System.out.println("Username:" + factory.getUsername());
System.out.println("Password:" + factory.getPassword());
// 最后可能还需要设置两个参数
// connectionTimeout,连接超时时间(表示超过该时限,建立的连接就失效了,默认为0,表示无限制)
// handshakeTimeout,握手超时时间(表示在建立连接时,超过该时限就报timeout的异常,默认为10秒,
// 但后续会/2,也就是说5秒没连接上就超时了)
// 其它参数可以暂时不用关注了
// 创建连接
connection = factory.newConnection();
// 创建信息通道
channel = connection.createChannel();
// 配置通道 SEND_DEMO: 队列名
// 接下来三个boolean: (1)队列是否可持久化,即重启后该队列是否依然存在,
// (2)该队列是否是独占的,即连接上来时它占用整个网络连接
// (3)是否自动销毁,即当这个队列不再被使用的时候即没有消费者对接上来时自动删除
// 最后一个map参数: 其它参数,如该队列的存活时间,
// 注意的是这里也申明了队列名,接下来又申明了一次,
// 这是因为在接收和发送之前,我们需要确保该队列名是存在的,要不然做得工作是徒劳的
channel.queueDeclare("SEND_DEMO", true, false, false, null);
System.out.println("通道建立成功!");
// 通过通道发送消息,消息以字符流的形式发送
// 第一个参数:消息发布的地方(因为在MQ服务器上,队列绑定了From exchange的)
// 第二个参数:队列名
// 第三个参数:参数的其它属性
// 第四个参数:发送消息的字符流
channel.basicPublish("", "SEND_DEMO", null, "This is a testing".getBytes());
System.out.println("[x] I have send the message of 'This is a testing'!");
} catch (Exception e) {
System.out.println(e.getMessage());
e.printStackTrace();
} finally {
try {
if(channel != null)
channel.close();
if(connection != null)
connection.close();
} catch (IOException e) {
e.printStackTrace();
} catch (TimeoutException e) {
e.printStackTrace();
}
}
}
// 消息接收
public static void receiveMessage(){
ConnectionFactory factory = null;
Connection connection = null;
Channel channel = null;
try {
// 创建MQ工厂类,并配置相关参数
factory = new ConnectionFactory();
factory.setHost("192.168.0.116");// MQip
// 鬼才知道为什么是这个端口,明明是15672,然后在MQ服务器上看到了这个端口,试了一下就通了
factory.setPort(5673);// MQ端口
// 用户名和密码默认为guest和guest,如果不一致,自行设置
// 虚拟IP默认为 /,如果不一致自行设置,这两个参数都可以在MQ服务器上找到
// factory.setUsername("guest");
// factory.setPassword("guset");
// factory.setVirtualHost("/");
System.out.println("Virtual Host:" + factory.getVirtualHost());
System.out.println("Username:" + factory.getUsername());
System.out.println("Password:" + factory.getPassword());
connection = factory.newConnection();
channel = connection.createChannel();
// 配置通道
// 第一个参数: 队列名
// 接下来三个boolean: (1)队列是否可持久化,即重启后该队列是否依然存在,
// (2)该队列是否是独占的,即连接上来时它占用整个网络连接
// (3)是否自动销毁,即当这个队列不再被使用的时候即没有消费者对接上来时自动删除
// 最后一个map参数: 其它参数,如该队列的存活时间,
// 注意的是这里也申明了队列名,接下来又申明了一次,
// 这是因为在接收和发送之前,我们需要确保该队列名是存在的,要不然做得工作是徒劳的
channel.queueDeclare("SEND_DEMO", true, false, false, null);
System.out.println("通道建立成功!");
// 正式服务器上推荐使用这种方式进行数据接收,下面一种方式只适合在main方法中测试,
// 如果使用这种方式在main方法中测试,注意在后面有句Thread.sleep(2000),
// 因为绑定接收操作类后,在finally中将channel和connection都关闭了,
// 此时数据还没有开始接收或者还没有接收完呢。而迁移至服务器的时候,可以单独写一个方法关闭连接
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("message:" + message);
getChannel().basicAck(envelope.getDeliveryTag(), false);
}
public void handleConsumeOk(String consumerTag) {
System.out.println("Consumer "+consumerTag +" registered");
}
};
channel.basicConsume("SEND_DEMO", false, consumer);
Thread.sleep(2000);
// QueueingConsumer consumer = new QueueingConsumer(channel);
// 通过消息通道接收消息
// 第一个参数: 队列名字
// 第二个参数: 是否自动应答,如果为真,消息一旦被接收到,服务端就知道该消息已经投递,
// 从而从队列中将消息剔除,否则,需要在接收端手工调用channel.basicAck()方法
// 通知服务端,如果没有调用,消息将会进入unacknowledged状态,并且当消费者连接断开
// 后变成ready状态重新进入队列(通俗来讲,就是在接收数据时,是否应答,如果没有自动
// 应道,就需要手工的签字确认,如果都没有做,那服务器就当该消息没有被接收,会一直存在
// 消息队列中)
// 第三个参数: 具体消息接收类。
// channel.basicConsume("SEND_DEMO", true, consumer);
// 接收消息队列里的记录
// QueueingConsumer.Delivery delivery = null;
//
// while (true) {
// delivery = consumer.nextDelivery();
// String message = new String(delivery.getBody());
// System.out.println("[x] I have received the message of '" + message + "'!");
// }
} catch (Exception e) {
System.out.println(e.getMessage());
e.printStackTrace();
} finally {
try {
if(channel != null)
channel.close();
if(connection != null)
connection.close();
} catch (IOException e) {
e.printStackTrace();
} catch (TimeoutException e) {
e.printStackTrace();
}
}
}
}
[1]: http://www.tuicool.com/articles/EJBrY3R RabbitMQ 连接断开处理-自动恢复
[2]: http://www.oschina.net/translate/getting-started-with-rabbitmq-in-java RabbitMQ 入门指南(Java)
[3]: http://blog.csdn.net/wangyonglin1123/article/details/24643277 Rabbitmq Java Demo详解