1、简介
MQ全称为Message Queue, 消息队列(MQ)是一种应用程序对应用程序的通信方法。应用程序通过读写出入队列的消息(针对应用程序的数据)来通信,而无需专用连接来链接它们。消息传递指的是程序之间通过在消息中发送数据进行通信,而不是通过直接调用彼此来通信,直接调用通常是用于诸如远程过程调用的技术。

如上图所示:
1.Server(broker): 接受客户端连接,实现AMQP消息队列和路由功能的进程。
2.Virtual Host:其实是一个虚拟概念,类似于权限控制组,一个Virtual Host里面可以有若干个Exchange和Queue,但是权限控制的最小粒度是Virtual Host
3.Exchange:接受生产者发送的消息,并根据Binding规则将消息路由给服务器中的队列。ExchangeType决定了Exchange路由消息的行为,例如,在RabbitMQ中, ExchangeType有direct、Fanout和Topic三种,不同类型的Exchange路由的行为是不一样的。
4.Message Queue:消息队列,用于存储还未被消费者消费的消息。
5.Message: 由Header和Body组成,Header是由生产者添加的各种属性的集合,包括Message是否被持久化、由哪个Message Queue接受、优先级是多少等。而Body是 真正需要传输的APP数据。
6.Binding:Binding联系了Exchange与Message Queue。Exchange在与多个Message Queue发生Binding后会生成一张路由表,路由表中存储着Message Queue所需消 息的限制条件即Binding Key。当Exchange收到Message时会解析其Header得到Routing Key,Exchange根据Routing Key与Exchange Type将Message路由到 Message Queue。Binding Key由Consumer在Binding Exchange与Message Queue时指定,而Routing Key由Producer发送Message时指定,两者的匹配方式由 Exchange Type决定。
7.Connection:连接,对于RabbitMQ而言,其实就是一个位于客户端和Broker之间的TCP连接。
8.Channel:信道,仅仅创建了客户端到Broker之间的连接后,客户端还是不能发送消息的。需要为每一个Connection创建Channel,AMQP协议规定只有通过Channel才能 执行AMQP的命令。一个Connection可以包含多个Channel。之所以需要Channel,是因为TCP连接的建立和释放都是十分昂贵的,如果一个客户端每一个线程都需要与 Broker交互,如果每一个线程都建立一个TCP连接,暂且不考虑TCP连接是否浪费,就算操作系统也无法承受每秒建立如此多的TCP连接。RabbitMQ建议客户端线程之间不· 要共用Channel,至少要保证共用Channel的线程发送消息必须是串行的,但是建议尽量共用Connection。
9.Command:AMQP的命令,客户端通过Command完成与AMQP服务器的交互来实现自身的逻辑。例如在RabbitMQ中,客户端可以通过publish命令发送消息,txSelect开 启一个事务,txCommit提交一个事务。
2、特点及应用场景
MQ是消费-生产者模型的一个典型的代表,一端往消息队列中不断写入消息,而另一端则可以读取或者订阅队列中的消息。MQ和JMS类似,但不同的是JMS是SUN JAVA消息中间件服务的一个标准和API定义,而MQ则是遵循了AMQP协议的具体实现和产品。
在项目中,将一些无需即时返回且耗时的操作提取出来,进行了异步处理,而这种异步处理的方式大大的节省了服务器的请求响应时间,从而提高了系统的吞吐量。
3、安装
RabbitMQ是基于Erlang的,所以首先必须配置Erlang环境。
从Erlang的官网 http://www.erlang.org/download.html 下载最新的erlang安装包,我下载的版本是 otp_src_R14B03.tar.gz 。
然后:
$ tar xvzf otp_src_R14B03.tar.gz
$ cd otp_src_R14B03
$ ./configure
编译后的输出如下图:

提示没有wxWidgets和fop,但是问题不大。继续:
$ make
$ sudo make install
安装完Erlang,开始安装RabbitMQ-Server。
主要参考官方文档:http://www.rabbitmq.com/build-server.html
需要安装一个比较新的Python版本。安装略。
需要安装simplejson。从此处下载最新的版本: http://pypi.python.org/pypi/simplejson#downloads 。我下载的版本是 simplejson-2.2.1.tar.gz
$ tar xvzf simplejson-2.2.1.tar.gz
$ cd simplejson-2.2.1
$ sudo python setup.py install
然后安装RabbitMQ Server。从此处下载源代码版本的RabbitMQ: http://www.rabbitmq.com/server.html。我下载的版本是 rabbitmq-server-2.6.1.tar.gz
$ tar xvzf rabbitmq-server-2.6.1.tar.gz
$ cd rabbitmq-server-2.6.1
$ make
# TARGET_DIR=/usr/local SBIN_DIR=/usr/local/sbin MAN_DIR=/usr/local/man make install
在sbin/目录下出现了三个命令:
rabbitmqctl rabbitmq-env rabbitmq-server
安装成功。
运行
找到sbin/目录,运行程序:
/usr/local/sbin/rabbitmq-server –detached
停止程序:
/usr/local/sbin/rabbitmqctl stop
4、java 实现rabbitMQ---生产者
public interface EventTemplate {
void send(String queueName,String exchangeName,Object eventContent) throws SendRefuseException;
void send(String queueName,String exchangeName,Object eventContent,CodecFactory codecFactory) throws SendRefuseException;
}
public class DefaultEventTemplate implements EventTemplate {
private static final Logger logger = Logger.getLogger(DefaultEventTemplate.class);
private AmqpTemplate eventAmqpTemplate;
private CodecFactory defaultCodecFactory;
// private DefaultEventController eec;
//
// public DefaultEventTemplate(AmqpTemplate eopAmqpTemplate,
// CodecFactory defaultCodecFactory, DefaultEventController eec) {
// this.eventAmqpTemplate = eopAmqpTemplate;
// this.defaultCodecFactory = defaultCodecFactory;
// this.eec = eec;
// }
public DefaultEventTemplate(AmqpTemplate eopAmqpTemplate,CodecFactory defaultCodecFactory) {
this.eventAmqpTemplate = eopAmqpTemplate;
this.defaultCodecFactory = defaultCodecFactory;
}
@Override
public void send(String queueName, String exchangeName, Object eventContent)
throws SendRefuseException {
this.send(queueName, exchangeName, eventContent, defaultCodecFactory);
}
@Override
public void send(String queueName, String exchangeName, Object eventContent,
CodecFactory codecFactory) throws SendRefuseException {
if (StringUtils.isEmpty(queueName) || StringUtils.isEmpty(exchangeName)) {
throw new SendRefuseException("queueName exchangeName can not be empty.");
}
// if (!eec.beBinded(exchangeName, queueName))
// eec.declareBinding(exchangeName, queueName);
byte[] eventContentBytes = null;
if (codecFactory == null) {
if (eventContent == null) {
logger.warn("Find eventContent is null,are you sure...");
} else {
throw new SendRefuseException(
"codecFactory must not be null ,unless eventContent is null");
}
} else {
try {
eventContentBytes = codecFactory.serialize(eventContent);
} catch (IOException e) {
throw new SendRefuseException(e);
}
}
// 构造成Message
EventMessage msg = new EventMessage(queueName, exchangeName,
eventContentBytes);
try {
eventAmqpTemplate.convertAndSend(exchangeName, queueName, msg);
} catch (AmqpException e) {
logger.error("send event fail. Event Message : [" + eventContent + "]", e);
throw new SendRefuseException("send event fail", e);
}
}
}
5、java实现rabbitMQ---消费者
public interface EventProcesser {
public void process(Object e);
}
为了能够将不同类型的消息交由对应的程序来处理,我们还需要一个消息处理适配器
/**
* MessageListenerAdapter的Pojo
* <p>消息处理适配器,主要功能:</p>
* <p>1、将不同的消息类型绑定到对应的处理器并本地缓存,如将queue01+exchange01的消息统一交由A处理器来出来</p>
* <p>2、执行消息的消费分发,调用相应的处理器来消费属于它的消息</p>
*
*/
public class MessageAdapterHandler {
private static final Logger logger = Logger.getLogger(MessageAdapterHandler.class);
private ConcurrentMap<String, EventProcessorWrap> epwMap;
public MessageAdapterHandler() {
this.epwMap = new ConcurrentHashMap<String, EventProcessorWrap>();
}
public void handleMessage(EventMessage eem) {
logger.debug("Receive an EventMessage: [" + eem + "]");
// 先要判断接收到的message是否是空的,在某些异常情况下,会产生空值
if (eem == null) {
logger.warn("Receive an null EventMessage, it may product some errors, and processing message is canceled.");
return;
}
if (StringUtils.isEmpty(eem.getQueueName()) || StringUtils.isEmpty(eem.getExchangeName())) {
logger.warn("The EventMessage's queueName and exchangeName is empty, this is not allowed, and processing message is canceled.");
return;
}
// 解码,并交给对应的EventHandle执行
EventProcessorWrap eepw = epwMap.get(eem.getQueueName()+"|"+eem.getExchangeName());
if (eepw == null) {
logger.warn("Receive an EopEventMessage, but no processor can do it.");
return;
}
try {
eepw.process(eem.getEventData());
} catch (IOException e) {
logger.error("Event content can not be Deserialized, check the provided CodecFactory.",e);
return;
}
}
protected void add(String queueName, String exchangeName, EventProcesser processor,CodecFactory codecFactory) {
if (StringUtils.isEmpty(queueName) || StringUtils.isEmpty(exchangeName) || processor == null || codecFactory == null) {
throw new RuntimeException("queueName and exchangeName can not be empty,and processor or codecFactory can not be null. ");
}
EventProcessorWrap epw = new EventProcessorWrap(codecFactory,processor);
EventProcessorWrap oldProcessorWrap = epwMap.putIfAbsent(queueName + "|" + exchangeName, epw);
if (oldProcessorWrap != null) {
logger.warn("The processor of this queue and exchange exists, and the new one can't be add");
}
}
protected Set<String> getAllBinding() {
Set<String> keySet = epwMap.keySet();
return keySet;
}
protected static class EventProcessorWrap {
private CodecFactory codecFactory;
private EventProcesser eep;
protected EventProcessorWrap(CodecFactory codecFactory,
EventProcesser eep) {
this.codecFactory = codecFactory;
this.eep = eep;
}
public void process(byte[] eventData) throws IOException{
Object obj = codecFactory.deSerialize(eventData);
eep.process(obj);
}
}
}
6、Python实现rabbitMQ---消费者(个人在项目中使用的)
#!/usr/bin/env python
#coding=utf8
import pika
import ConfigParser
import string, os, sys
'''读取配置项'''
cf = ConfigParser.ConfigParser()
cf.read("agent.conf")
host = cf.get("agent", "host")
exchangeName = cf.get("agent", "exchangeName")
queueName = cf.get("agent", "queueName")
command = cf.get("agent", "command")
connection = pika.BlockingConnection(pika.ConnectionParameters(host))
channel = connection.channel()
#定义交换机
channel.exchange_declare(exchange=exchangeName, type='fanout')
#生成队列,并绑定到交换机上
result = channel.queue_declare(queue=queueName,durable=True,exclusive=False)
queue_name = result.method.queue
channel.queue_bind(exchange=exchangeName, queue=queue_name)
#回调函数
def callback(ch, method, properties, body):
ch.basic_ack(delivery_tag = method.delivery_tag)
os.system(command+' '+body)
print " [x] Received %r" % (body,)
channel.basic_consume(callback, queue=queue_name)
print ' [*] Waiting for messages. To exit press CTRL+C'
channel.start_consuming()
下面我们具体分析一下代码:
rabbitmq的python实例工作队列:

1、连接到rabbitmq服务器。
connection = pika.BlockingConnection(pika.ConnectionParameters(host)) 2、声明消息队列,消息将在这个队列中进行传递。如果将消息发送到不存在的队列,rabbitmq将会自动清除这些消息。
result = channel.queue_declare(queue=queueName,durable=True,exclusive=False) queue_name = result.method.queue
来源:https://www.cnblogs.com/shufy/p/5372055.html