rabbitMQ的使用
协议
AMQP(Advanced Message Queuing Protocol),一个提供统一消息服务的应用层标准高级消息队列协议,是应用层协议的一个开放标准,为面向消息的中间件设计。基于此协议的客户端与消息中间件可传递消息,并不受客户端/中间件不同产品,不同的开发语言等条件的限制。Erlang中的实现有 RabbitMQ等。。。。好吧,这段话有些不舒服,简单来说,就是将JMS topic和P2P模式进行复制并整合为一套全新的框架,更灵活且更可靠,同时拥有成熟的集群方案。
相关概念
-
生产者(Producer):向Exchange发布消息的应用。
-
消费者(Consumer):从消息队列中消费消息的应用。
-
消息队列(Message Queue):服务器组件,用于保存消息,直到发送给消费者。
-
消息(Message):传输的内容。
-
交换器(exchange):路由组件,接收Producer发送的消息,并将消息路由转发给消息队列。
-
虚拟主机(Virtual Host): 一批交换器,消息队列和相关对象。虚拟主机是共享相同身份认证和加密环境的独立服务器域。
-
Broker :AMQP的服务端称为Broker。
-
连接(Connection):一个网络连接,比如TCP/IP套接字连接。
-
信道(Channel):多路复用连接中的一条独立的双向数据流通道,为会话提供物理传输介质。
-
绑定器(Binding):消息队列和交换器直接的关联。
通信过程
-
建立连接Connection。由producer和consumer创建连接,连接到broker的物理节点上。
-
建立消息Channel。Channel是建立在Connection之上的,一个Connection可以建立多个Channel。producer连接Virtual Host 建立Channel,Consumer连接到相应的queue上建立Channel。
-
发送消息。由Producer发送消息到Broker中的exchange中。
-
路由转发。exchange收到消息后,根据一定的路由策略,将消息转发到相应的queue中去。
-
消息接收。Consumer会监听相应的queue,一旦queue中有可以消费的消息,queue就将消息发送给Consumer端。
-
消息确认。当Consumer完成某一条消息的处理之后,需要发送一条ACK消息给对应的Queue。Queue收到ACK信息后,才会认为消息处理成功,并将消息从Queue中移除;如果在对应的Channel断开后,Queue没有收到这条消息的ACK信息,该消息将被发送给另外的Channel。 至此一个消息的发送接收流程走完了。消息的确认机制提高了通信的可靠性。
核心参数
-
mandatory
为true时,如果exchange根据自身类型和消息routeKey无法找到一个符合条件的queue,那么会调用basic.return方法将消息返回给生产者,要求有当前routekey
-
immediate
为true时,如果exchange在将消息路由到queue(s)时发现对应的queue上没有消费者,那么会调用basic.return方法返还给生产者,要求当前routekey有消费者,不好用
-
durable
在服务器重启时,能够存活
-
exclusive
是否为当前连接的专用队列,在连接断开后,会自动删除该队列,生产环境中很少用到
-
autodelete
当没有任何消费者使用时,自动删除该队列。
-
prefetchCount
会告诉RabbitMQ不要同时给一个消费者推送多于N个消息,即一旦有N个消息还没有ack,则该consumer将block掉,直到有消息ack
-
BasicProperties props
是否支持事物
一分钟构建JavaAPI
pom.xml
<dependency>
<groupId>com.rabbitmq</groupId>
<artifactId>amqp-client</artifactId>
<version>3.5.7</version>
</dependency>
MQConfig.java
public class MQConfig {
private static String MQ_IP_NODE;
private static int MQ_PORT_NODE;
private static String USER_NAME;
private static String PASSWORD;
static{
MQ_IP_NODE = "192.168.88.3";
MQ_PORT_NODE = 5672;
USER_NAME = "dev";
PASSWORD = "yanfa";
}
public static String getMQ_IP_NODE() {
return MQ_IP_NODE;
}
public static int getMQ_PORT_NODE() {
return MQ_PORT_NODE;
}
public static String getUSER_NAME() {
return USER_NAME;
}
public static String getPASSWORD() {
return PASSWORD;
}
}
MQSource
import java.io.IOException;
import java.util.concurrent.TimeoutException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import com.zgg.group2.common.pool.ThreadPoolFactory;
import com.zgg.group2.common.util.ExceptionUtil;
import com.zgg.group2.scheduler.mq.config.MQConfig;
public class MQSource {
private final static Logger logger = LoggerFactory.getLogger(MQSource.class);
private static MQSource mqSource = null;
private static ConnectionFactory factory = null;
private static Connection connection = null;
private static Channel channel = null;
static {
init();
}
public static MQSource getInstance() {
if (mqSource == null) {
synchronized (MQSource.class) {
if (mqSource == null)
return mqSource = new MQSource();
}
}
return mqSource;
}
private static void init(){
factory = new ConnectionFactory();
factory.setHost(MQConfig.getMQ_IP_NODE());
factory.setPort(MQConfig.getMQ_PORT_NODE());
factory.setUsername(MQConfig.getUSER_NAME());
factory.setPassword(MQConfig.getPASSWORD());
factory.setShutdownTimeout(10000);
factory.setSharedExecutor(ThreadPoolFactory.getInstance().getPool());
try {
connection = factory.newConnection();
channel = connection.createChannel();
} catch (IOException e) {
logger.error("rabbitmq factory created failed.", ExceptionUtil.exceptionStack(e));
} catch (TimeoutException e) {
logger.error("rabbitmq factory created failed.", ExceptionUtil.exceptionStack(e));
}
}
private MQSource() {
super();
}
public Channel getChannel() {
return channel;
}
public Connection getConnection() {
return connection;
}
public void destory() {
try {
channel.close();
connection.close();
} catch (IOException | TimeoutException e) {
logger.error("rabbitmq channel abort failed.", ExceptionUtil.exceptionStack(e));
}
}
}
MQPubHandler.java
import java.io.IOException;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.MessageProperties;
import com.rabbitmq.client.ReturnListener;
import com.zgg.group2.scheduler.mq.factory.MQSource;
public class MQPubHandler {
private static Channel channel = null;
static {
try {
channel = MQSource.getInstance().getConnection().createChannel();
//queue模式-向两个固定的queue发送消息, 由producer定义queue
channel.exchangeDeclare("exchangename1", "direct", true);
//topic模式-向绑定了当前exchange的所有queue发送消息, 由consumer定义queue
channel.exchangeDeclare("exchangename2", "fanout", true);
channel.queueDeclare("queuename 1", true, false, false, null);
channel.queueDeclare("queuename 2", true, false, false, null);
channel.queueBind("queuename 1", "exchangename1", "route key 1", null);
channel.queueBind("queuename 2", "exchangename1", "route key 1", null);
channel.basicQos(1);
} catch (IOException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
//向topic发送消息
// publishToQueueOrExchange("exchangename2", "route key 1", "message", true, false);
//向queue发送消息
// publishToQueueOrExchange("exchangename1", "route key 1", "message", true, false);
//向topic发送消息
// publishToQueueOrExchangeByPersistent("exchangename2", "route key 1", "message", true, false);
//向queue发送消息
publishToQueueOrExchangeByPersistent("exchangename1", "route key 1", "message1", true, false);
}
/**
* @desp 向queue/topic发送消息
* @param queueName
* @param message
*/
public static void publishToQueueOrExchange(String exchangeName,String routingKey, String message, boolean mandatory, boolean immediate){
try {
channel.addReturnListener(new ReturnListener() {
@Override
public void handleReturn(int replyCode, String replyText, String exchange, String routingKey, com.rabbitmq.client.AMQP.BasicProperties basicProperties, byte[] body)
throws IOException {
String message = new String(body);
System.out.println("Basic.return返回的结果是:"+message);
}
});
channel.basicPublish(exchangeName, routingKey, mandatory, immediate, null, message.getBytes());
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* @desp 向queue发送消息
* @param queueName
* @param message
*/
public static void publishToQueueOrExchangeByPersistent(String exchangeName,String routingKey, String message, boolean mandatory, boolean immediate){
try {
channel.addReturnListener(new ReturnListener() {
@Override
public void handleReturn(int replyCode, String replyText, String exchange, String routingKey, com.rabbitmq.client.AMQP.BasicProperties basicProperties, byte[] body)
throws IOException {
String message = new String(body);
System.out.println("Basic.return返回的结果是:"+message);
System.out.println(replyCode);
System.out.println(replyText);
System.out.println(exchange);
System.out.println(routingKey);
System.out.println(basicProperties);
}
});
channel.txSelect();
channel.basicPublish(exchangeName, routingKey, mandatory, immediate, MessageProperties.PERSISTENT_TEXT_PLAIN, message.getBytes());
channel.txCommit();
} catch (IOException e) {
try {
channel.txRollback();
} catch (IOException e1) {
e.printStackTrace();;
}
}
}
}
MQSubHandler.java
import java.io.IOException;
import com.rabbitmq.client.AMQP;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Consumer;
import com.rabbitmq.client.DefaultConsumer;
import com.rabbitmq.client.Envelope;
import com.zgg.group2.scheduler.mq.factory.MQSource;
public class MQSubHandler {
private static Channel channel = null;
static {
try {
channel = MQSource.getInstance().getConnection().createChannel();
//queue模式-向两个固定的queue发送消息, 由producer定义queue
channel.exchangeDeclare("exchangename1", "direct", true);
//topic模式-向绑定了当前exchange的所有queue发送消息, 由consumer定义queue
channel.exchangeDeclare("exchangename2", "fanout", true);
channel.queueDeclare("queuename 1", true, false, false, null);
channel.queueDeclare("queuename 2", true, false, false, null);
channel.queueBind("queuename 1", "exchangename1", "route key 1", null);
channel.queueBind("queuename 2", "exchangename1", "route key 1", null);
channel.basicQos(1);
} catch (IOException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
//消费topic(也是在消费queue)
// consumeFromQueueWithExchange("exchangename2", "route key 1");
//消费queue
consumeFromQueue("queuename 2");
consumeFromQueue("queuename 1");
}
/**
* @desp 消费queue
* @param queueName
*/
public static void consumeFromQueue(String queueName) {
try {
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");
doWork(message);
//手动回复给mq-server业务处理完毕可以清除,basicConsume(,false,)
// channel.basicAck(envelope.getDeliveryTag(), false);
//拒绝消息
// channel.basicReject(envelope.getDeliveryTag(), true);
}
};
channel.basicConsume(queueName, true, consumer);
} catch (IOException e) {
e.printStackTrace();
}
}
private static void doWork(String message){
System.out.println(message + " [x] Done");
try {
Thread.sleep(3000); //每3秒收一'条'消息
} catch (InterruptedException e) {
e.printStackTrace();
}
}
/**
* @desp 从订阅的topic获取消息
* @param exchangeName
* @param routingKey
*/
public static void consumeFromQueueWithExchange(String exchangeName, String routingKey) {
try {
//随机queue
String queueName = channel.queueDeclare().getQueue();
channel.queueBind(queueName, exchangeName, routingKey);
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 + "'");
String exchangeName = envelope.getExchange();
String routingKey = envelope.getRoutingKey();
long deliveryTag = envelope.getDeliveryTag();
System.out.println(consumerTag);
System.out.println(exchangeName);
System.out.println(routingKey);
System.out.println(deliveryTag);
}
};
channel.basicConsume(queueName, true, consumer);
} catch (IOException e) {
e.printStackTrace();
}
}
}