一、rabbitmq服务器的安装
rabbitmq是使用erlang语言编写的
①rabbitmq的默认安装位置:/usr/lib/rabbitmq/lib/rabbitmq_server-3.4.1
一、rabbitmq服务器的安装
rabbitmq是使用erlang语言编写的
①rabbitmq的默认安装位置:/usr/lib/rabbitmq/lib/rabbitmq_server-3.4.1/sbin/rabbitmq-server
②rabbitmq的日志文件位置:/var/log/rabbitmq/[email protected]
/var/log/rabbitmq/[email protected]
③安装教程参考:http://bbs.chinaunix.net/thread-4146675-1-1.html
④启动rabbitmq:$/usr/lib/rabbitmq/lib/rabbitmq_server-3.4.1/sbin/rabbitmq-serverXIN
检查服务器的状态:$/usr/lib/rabbitmq/lib/rabbitmq_server-3.4.1/sbin/rabbitmqctl status
⑤验证python是否安装成功:直接输入命令python
二、对rabbitmq的基本了解
④AMQP(Advanced Message Queuing Protocol,高级消息队列协议),能以一对多的广播方式进行路由
⑤应用程序和Rabbitmq代理服务器是由TCP连接,
每个信道是一个私密的线程,每个AMQP连接都是一个TCP连接
⑥AMQP消息有必须的三部分组成:交换器、队列和绑定
⑦basic.consume:通过basic.consume命令订阅,这样做会将信道置为接收模式,直到取消对队列的订阅为止。
⑧basic.get:向队列请求单条消息是通过AMQP的basic.get命令实现,如果想获得更多消息,不要将basic.get放到循环里代替basic.consume,会影响rabbit性能,消费则理应使用basic.consume来实现高吞吐量
⑨basic.ack:当rabbit队列拥有多个消费者时,队列收到的消息将以循环的方式发送给消费者。每条消息都只会发送给一个订阅者,消费者通过AMQP的basic.ack命令显式的想rabbitmq发送一个确认,或则在订阅到队列的时候就将auto_ack参数设置为true,当设置了auto_ack时,一旦消费者接收消息,rabbitmq会自动视其确认了消息。需要记住的是,消费则对消息的确认和告诉生产者消息已经被接收了这两件事毫不相关。消费者通过确认命令告诉rabbitmq已经被正确接收,rabbitmq才能安全的把消息从队列删除
⑩rabbitmq的特性:消费者收到消息时,和rabbit断开连接而没有确认,rabbitmq会认为这条消息没有分发,会重新发给下一个订阅者,这样可以确保你的程序崩溃了,下一个消费者可以订阅消息,同时rabbitmq也不会给你的应用程序发送消息了,好的一方面来说,如果消息处理他别耗时,这样也可以防止rabbit持续不断的消息涌向你的应用而导致过载
⑪basic.reject:允许消费者拒绝Rabbitmq发送的消息。把reject命令的requeue参数设置成true,将会把消息重新发送给下一个订阅的消费者,将requeue参数设置成false,消息会被移除队列,在将来的rabbitmq的版本中会有一个特殊的“死信”队列,存放被拒绝的而不重新入队列的消息
⑫queue.declare:用来创建队列,如果消费者在同一条信道上订阅了另一个队列的话,就无法声明队列,必须首先取消订阅,将信道置为“传输”模式。可以为队列命名,若不命名使用declare命令时会返回一个随机的名字。(当创建一个同名的队列,参数相同,rabbit什么都不做成功返回,若参数不同,则返回失败,检测队列是否存在,使用queue.declare的passive设置为true,如果存在成功返回,若不存在,返回错误)
参数:exclusive:设置true,队列为私有,只有一个消费者使用。
auto-delete:当最后一个消费者取消订阅,队列就自动移除。
⑬是该有消费者创建队列还是消费者创建队列:如果能过实现重新发布未处理的消息的话,可以让消费者来声明队列,否则生产者和消费者都需要创建队列。因为发出去的消息如果路由到不存在的队列,rabbit会忽略他们。
⑭队列:1.为消息提供住所,消息在此等待消费
2.对负载均衡来说,队列是绝佳的方案。只需附加一堆消费者,让rabbitmq以循环的方式均匀的分配发来的消息。
⑮路由键:把消息投递到队列时,把消息发给交换器来完成,根据确定的规则,这些规则乘坐路由键,队列通过路由键绑定交换器
⑯协议中定义不同类型的交换器,一共四种:direct,fanout,topic和headers。
headers允许匹配AMQP消息的header而非路由键。headers交换器和direct交换器完全一致,但性能会差很多。因此不太实用,几乎用不到。
direct交换器:
服务器必须实现direct类型交换器,包含一个空白字符串的默认交换器,当声明一个队列时,会自动绑定到默认交换器,以队列名称作为路由键。用以下代码发送消息到这个队列:
$channel->basic_publish($msg,' ','queue-name');//第一个参数是消息内容,第二参数是空字符串指定了默认交换器,第三个参数是队列名称(路由键)
exchange.declare:声明自己的交换器,只需发送exchange.declare命令并设置合适参数就行。
fanout交换器:
当发送一条消息到fanout交换器时,它会把消息投递给所有附加在此交换器上的队列。
topic交换器:
它使得来自不同源头的消息能够到达同一个队列
例如:假设声明一个msg-inbox-errors队列,将其绑定到交换机来接收消息
$channel->queue_bind('msg-inbox-errors',‘logs-exchang’,'error.msg-inbox');//第一个参数是队列,第二个参数是交换机,第三个参数是消息路由键
例如2:让msg-inbox-logs队列接收从msg-inbox模块发来的所有error、warning、和info的日志信息。
$channel->queue_bind("msg-inbox-logs",'logs-exchang','*.msg-inbox');
例如3:匹配所有规则
$channel->queue_bind('all-logs','logs-exchange','#');
⑰vhost:vhost之于Rabbit就像虚拟机之于物理服务器一样:他们通过在各个实例间提供逻辑上分离,允许你为不同应用程序安全保密的运行数据。它既能将同一Rabbit的众多客户区分开来,又可以避免队列和交换机的命名冲突。否则你可能不得不运行多个rabbit,以致带来头疼的管理问题。运行一个rabbit,可以按需启动或关闭vhost。
vohost要在连接时进行指定,rabbitmq包含开箱急用的默认vhost:“/”,默认的用户名是 :guest,密码:“guest”,权限控制可以在vhost级别或者在服务器端级别实现。留给服务器的开发则去决定。权限控制是以vhost为单位。
⑱创建vhost:通过rabbitmq的安装路径下./sbin目录下的rabbitmqctl工具来创建
例如:运行rabbitmqctl add_vhost[vhost_name] //vhost创建成功,可以链接上去添加队列和交换器
删除vhost: rabbitmqctldelete_vohost[vhost_name]
查看rabbit服务器上运行的vhost:rabbitmqctl list_vhosts
查看远程的rabbitmq:通过指定-n rabbit@[server_name](注意:要确保运行的rabbit节点的服务器和运行rabbitmqctl的工作站安装了相同的Erlang cookie)
⑲AMQP消息持久化:实现方式是它消息写到磁盘上一个持久化日志文件
1.把队列和交换器的durable设置成true
2.把它的投递模式选项设置为2
3.发送到持久化的交换器
4.到达持久化的队列
注意:如果消息路由到非持久化队列的话,他会自动从持久性日志中移除,使用持久化机制会导致消息吞吐量降低至少10倍的情况.
AMQP的事物:如果事物中的首次发布成功了,那么信道会在事物中完成其他AMQP命令,如果发送失败的话,其他AMQP命令将不会执行。(事物几乎吸干了rabbit的性能,降低大约2-10倍的消息吞吐量,使生产者应用程序产生同步,而使用消息通信就是要避免同步)
发送方确认模式取代AMQP的事物:告诉rabbit将信道设置成confirm模式,信道消息会被指派一个唯一的ID,一旦消息被投递给所匹配的队列后,信道会发送一个发送模式给生产者,包括消息的唯一ID。如果是消息和队列是可持久话的,将队列消息写入磁盘后才会发出。优点,发送确认模式是异步的。如果rabbit发生内部错误导致消息丢失,rabbit会发送nack(not acknowledged,未确认)消息。
三.第一个helloword
消息的创建-->发布-->消费
python环境的准备:(在管理员权限下执行)
①$ wget http://peak.telecommunity.com/dist/ez_setup.py
②python ez_setup.py
③easy_install pika
创建发布者:
①连接到rabbitmq
②获取信道
③声明交换器
④创建消息
⑤发布消息
⑥关闭信道
⑦关闭链接
生产者脚本代码:
生产者发布消息
import pika,sys
# coding = utf-8
credentials = pika.PlainCredentials("guest","guest")
conn_params = pika.ConnectionParameters("localhost",credentials = credentials)#建立到代理服务器的链接,默认运行在本机>的5672端口上,使用默认的guest用户名和密码
conn_broker = pika.BlockingConnection(conn_params)#获得链接
channel = conn_broker.channel()#获得信道
channel.exchange_declare(exchange="hello-exchange",type="direct",passive=False,durable=True,auto_delete=False)#声明交>换器,消息将发送到这里,第一个参数是交换器>的名称,第二个是交换器的类型direct,第三个是检测对对列是否存在,第四个持久化>交换器,第五个不会自动删除队列,当最后一个订阅者取消订阅也不删除队列
msg = sys.argv[1]#传递给hello_world_producer.py脚本的第一个参数
msg_props = pika.BasicProperties()
msg_props.content_type = "text/palin" #消息内容的类型,创建纯文本消息
channel.basic_publish(body=msg,exchange="hello-exchange",properties=msg_props,routing_key="hola")#发布消息,把消息发布给hello-exchange交换器,消息的路由键是“hola”
消费者脚本:
消费者消费消息
import pika,sys
# coding = utf-8
credentials = pika.PlainCredentials("guest","guest")
conn_params = pika.ConnectionParameters("localhost",credentials = credentials)
conn_broker = pika.BlockingConnection(conn_params) #建立到代理服务器的连接
channel = conn_broker.channel() #获得信道
channel.exchange_declare(exchange="hello-exchange",type="direct",passive=False,durable=True,auto_delete=False) #声明交换器,如果没有就创建,否则继续
channel.queue_declare(queue="hello-queue") #声明队列
channel.queue_bind(queue="hello-queue",exchange="hello-exchange",routing_key="hola") #通过“hola”将队列和交换器绑定起来
def msg_consumer(channel,method,header,body):#用来处理传入的消息的函数,回调函数
channel.basic_ack(delivery_tag=method.delivery_tag)#消息确认
if body=="quit":
channel.basic_cancel(consumer_tag="hello-consumer")
channel.stop_consuming()
else:
print body
return
channel.basic_consume(msg_consumer,queue="hello-queue",consumer_tag="hello-consumer")#订阅消费者,第一个参数是回调函数,第二个是队列,第三个是消费者标记
channel.start_consuming() #开始消费
总结:想要发送消息,就要声明交换器,以保证有地方发布消息,然后消费者声明了相同名字的交换器和一个队列,并通过相同的路由键把队列绑定到交换器上,用来接收消息
执行python脚本的命令:python ./hello.py
Python编码错误的解决办法SyntaxError: Non-ASCII character '\xe5' in file
解决办法:
加上
# coding = utf-8 【注:这句代码蛋疼的必须放在第二行啊,而且多个空格都不行啊!】
带有确认功能的生产者
import pika,sys
# coding = utf-8
credentials = pika.PlainCredentials("guest","guest")
conn_params = pika.ConnectionParameters("localhost",credentials = credentials)#建立到代理服务器的链接,默认运行在本机>的5672端口上,使用默认的guest用户名和密码
conn_broker = pika.BlockingConnection(conn_params)#获得链接
channel = conn_broker.channel()#获得信道
def confirm_handler(fram):
if type(frame.method) == spec.Confirm.SelectOk:
print "Channel in 'confirm' mode."
elif type(fram.method) == spec.Basic.Ack:
if frame.method.delivery_tage in msg_ids:
print "Message lost!"
elif type(frame.method) == spec.Basic.Ack:
if frame.method.delivery_tag in msg_ids:
print "Confirm received!"
msg_ids.remove(frame..method.delivery_tag)
channel.confirm_delivery(callback=confirm_handler) #将信道设置为confirm模式
channel.exchange_declare(exchange="hello-exchange",type="direct",passive=False,durable=True,auto_delete=False)#声明交>换器,消息将发送到这里,第一个参数是交换器>的名称,第二个是交换器的类型direct,第三个是检测对对列是否存在,第四个持久化>交换器,第五个不会自动删除队列,当最后一个订阅者取消订阅也不删除队列
msg = sys.argv[1]#传递给hello_world_producer.py脚本的第一个参数
msg_props = pika.BasicProperties()
msg_props.content_type = "text/palin" #消息内容的类型,创建纯文本消息
msg_ids=[] #重设消息ID追踪器
channel.basic_publish(body=msg,exchange="hello-exchange",properties=msg_props,routing_key="hola")#发布消息,把消息发布给hello-exchange交换器,消息的路由键是“hola”
msg_ids.append(len(msg_ids)+1) #将ID添加到追踪列表中
channel.close()
四.运行和管理rabbit
erlang节点:
①当运行java程序时,JVM的一个实例就启动了,并开始执行指定的java程序。与之相似,Erlang也有虚拟机,而虚拟机的每个实例我们称之为节点。多个Erlang应用程序可以运行在同一个节点之上。节点之间可以进行本地通信(不管是否在同一台机器上)。
②本地通讯,例如a节点上的应用程序可以调用b节点上的应用程序方法,就像本地方法一样。当应用程序由于某些原因崩溃,Erlang节点会自动尝试重启应用程序。
管理命令:
在rabbitmq的安装目录下的./sbin目录
启动rabbitmq:
$ rabbitmq-server start 或者在sbin目录中,./rabbitmq-server来启动服务
或者以守护方式启动./rabbitmq-server -detached(建议使用/etc/init.d
目录下的命令)
查看日志:/var/log/rabbitmq/目录下找到rabbit@[hostname].log的日志文件,查看启动状况
关闭rabbitmq节点:
有两种方式:干净的方式和肮脏的方式,
①肮脏的方式是用ctrl+c,可能会对持久化队列造成影响。
②干净的方式,本地的使用./sbin/rabbitmqctl stop ,远程的使用 -n rabbit@[hostname],这种方式会把节点和应用程序都关闭掉
③关闭掉应用程序而不关闭节点:./rabbitmqctl stop_app
解决不让rabbitmq吞噬掉整个内存的方法:
rabbitmq的配置文件:
配置文件的位置是:/etc/rabbitmq/目录下面rabbitmq.config
内容如下:[ {mnesia, [{dump_log_write_threshold, 1000}]}, {rabbit, [{tcp_listeners, [5673]}]} ].
注意:rabbitMQ中的每个队列、交换器和绑定的元数据(除了消息的内容)都保存到Mnesia中。Mnesia通过将RabbitMQ元数据首先写入一个仅限追加的日志文件,以确保 其完整性,再定期将日志内容转储到真实的Mnesia数据库文件中。例如:dump_log_write_threshold选项控制找转储的频度,设置1000告诉Mnesia,每1000个条目就转储日志内容到数据库文件。
rabbitmq权限的控制:
rabbitmq权限系统一个好的地方在于单个用户可以跨越多个vhost进行授权。
用户是访问控制的基本单位,一个用户可以有多个访问控制条目,所以首先对用户进行操作
①添加用户
命令:./rabbitmqctl add_user cashing-tier cashMel
说明:创建一个名为cashing-tier 密码为cashMel的用户
②删除用户
命令:./rabbitmqctl delete_user cashing-tier
说明:删除名叫cashing-tier的用户
③查看当前存在哪些用户
命令:./rabbitmqctl list_users
说明:当前存在哪些用户
④更改用户的密码
命令:./rabbitmqctl chang_password cashing 123456
权限种类:
读———有关消费消息的任何操作,包括“清除”整个队列
写———发布消息
配置——队列和交换器的创建和删除
控制条目的组成:
①被授予权限的用户
②权限控制应用的vhost
③需要授予的读/写/配置权限的组合
④权限范围——客户端或者服务端
注意:访问控制权限是无法跨越vhost的,如果在把一个用户在两个vhost上赋予相同的权限,则要创建两个访问权限分别赋予到两个vhost上。
赋权限命令:假设为cashing-tier赋予完全的访问权限(配置、读、写)
./rabbitmqctl set_permissions -p sycamore cashing-tier ".*" ".*" ".*"
sycamore是vhost的名字,cashing-tier是用户的名字
第一个".*",映射配置,第二个“.*”映射写,第三个“.*” 映射读
./rabbitmqctl set_permissions -p sycamore cashing-tier "" "checks.*" ".*"
第一个“”,是表示不匹配队列和交换器,第二个"checks.*"值匹配名字以“checks”开头的队列和交换器
第三个匹配任何队列和交换器
查看权限:
./rabbitmqctl list_permissions -p sycamore
清除权限:
./rabbitmqctl clear_permissions -p sycamore cashing-tier
清除在vhost sycamore上的用户为cashing-tier的权限
查看用户的所有权限:
./rabbitmqctl list_user_permissions cashing-tier
查看用户cashing-tier的在所有vhost上的权限
查看统计信息:rabbitmactl -p ,-p指定了虚拟主机vhost或则路径信息,如果忽略该选项,则将“/”作为默认路径。
rabbitmqctl示例代码:
require_once('./php-amqplib/amqp.inc');
define('HOST','localhost');
define('PORT','5672');
define('USER','guest');
define('PASS','guest');
$conn = new AMQPConnection(HOST,PORT,USER,PASS);#获得连接和信道
$channel = $conn->channel();
$channel->exchange_declare('logs-exchange','topic',false,true,false);#声明交换器
$channel->queue_declare('msg-inbox-errors',false,true,false,false);
$channel->queue_declare('msg-inbox-logs',false,true,false,false);
$channel->queue_declare('all-logs',false,true,false,false);#声明队列
$channel->queue_bind('msg-inbox-errors','logs-exchange','error.msg-inbox');
$channel->queue_bind('msg-inbox-logs','logs-exchange','*.msg-inbox');#把队列绑定到交换器上
?>
运行php脚本:php ./rabbimqctl-examples.php
Install the amqp
wget http://pecl.php.net/get/amqp-1.2.0.tgz tar zxvf amqp-1.2.0.tgz cd amqp-1.2.0 phpize ./configure --with-php-config=/path/to/your/php-config --with-librabbitmq-dir=/var/share/software/rabbitmq-csudo make sudo make install
搜索php-config的位置
rabbitmq-c-0.4.1的安装位置
和队列相关查询命令:
./rabbitmqctl list_queues
可以查看队列名字及队列上消息的总数
./rabbitmqctl list_queues-p sycamore
查看vhost为sycamore上的队列名字及消息数
和交换器相关的查询命令:
./rabbitmqctl list_exchanges
返回交换器的名字和类型,最上面是自己定义的交换器,最下面是默认交换器
和绑定相关的命令:
./rabbitmqctl list_bindinigs
上面是默认交换器的绑定,下面是自定义交换器的绑定,改命令不接收除-p(指明vhost路径)意外的参数。返回的是交换器名,队列名称,路由键和参数。
Rabbitmq的日志
日志的默认位置,LOG_BASE=/var/log/rabbitmq
两个日志文件:RABBITMQ_NODENAME-sasl.log和RABBITMQ_NODENAME.log,这里的RABBITMQ_NODENAME指的是_rabbit@localhost_或者是rabbit,这取决于如何配置系统。
sasl.log(System Application Support Libraries,系统应用程序支持库)是库的集合,记录的是Erlang相关信息,例如Erlang的崩溃报告。
rabbit.log对调试生产者和消费者很有帮助,可以查看连接是否正常,是否有未经允许的地址连接到服务器,还能看到对用户、交换器、队列等的操作事件,编码请求失败,声明队列有冲突等等。
轮换日志
./rabbitmqctl rotate_logs suffix
例如:./rabbitmqctl rotate_logs .1
在日志文件夹下可以看到:
通过AMQP实时访问日志
在交换器日志列表中有一个叫作amq.rabbitmq.log的topic交换器,rabbitmq把日志信息发布到该交换器上,并以严重级别作为路由键————error、worning、和info
php版的,对日志的消费者:
2include("config.php");
3list($errors_queue,,) = $ch->queue_declare();
4list($warnings_queue,,) = $ch->queue_declare();
5list($info_queue,,) = $ch->queue_declare();
6 $exchange = 'amq.rabbitmq.log';
7 $ch->queue_bind($errors_queue,$exchange,"error");
8 $ch->queue_bind($warnings_queue,$exchange,"warning");
9 $ch->queue_bind($info_queue,$exchange,"info");
10 $error_callback = function($msg){
11echo 'error:',$msg->body,"\n";
12$msg->delivery_info['channel']->basic_ack($msg->delivery_info['delivery_tag']);
13
14 };
15 $warning_callback = function($msg){
16echo'warning:',$msg->body,"\n";
17$msg->delivery_info['channel']->basic_ack($msg->delivery_info['delivery_tag']);
18 };
19 $info_callback = function($msg){
20echo 'info:',$msg->body,"\n";
21$msg->delivery_info['channel']->basic_ack($msg->delivery_info['delivery_tag']);
22 };
23 $ch->basic_consume($errors_queue,"",false,false,false,false,$error_callback);
24 $ch->basic_consume($warnings_queue,"",false,false,false,false,$warning_callback);
25 $ch->basic_consume($info_queue,"",false,false,false,false,$info_callback);
26 while(count($ch->callbacks)){
27$ch->wait();
28 }
有两种方式来检测服务器的状态:其中之一是通过传统的文件日志;另一种是更为先进的AMQP交换器。
rabbitmq的运行原理
Mnesia和主机名:
Mnesia是Erlang数据库,用来存储队列,交换器,绑定等信息,导致MNESIA启动失败的两个原因:
一.MNESIA_BASE目录的权限问题,运行RabbiMQ服务器的用户需要对改文件夹的写权限
二.错误信息:{timeout_waiting_for_tables[……]},主机名改了
原因:Mnesia会基于机器的主机名创建数据库schema,如果由于网络重新配置的原因,主机名修改了,Mnesia就无法载入就得schema,RabbitMq使用rabbit这个单词作为节点,使用Erlang sname选项更改它,Mnesia又会遇到同样的问题。
rabbit@hostname:rabbit是Erlang的节点
Erlang命令维护:使用Erlang REPL
$ erl -sname test //以test作为节点没那个启动了Erlang节点
$ node() . //常看当前节点的信息
(test@bogon)1> node() .
test@bogon
(test@bogon)2> net_adm:names() . //查看机器上海运行着哪些节点
{ok,[{"rabbit",25672},{"test",44154}]}
(test@bogon)3>
(test@bogon)3> net_adm:ping('rabbit@bogon'). //如果应答就是pang,使一切工作正常,必须共享同样的Erlang cookie
pong
(test@bogon)4> nodes(). //连接上的节点列表
[rabbit@bogon]
(test@bogon)5> rpc:call('rabbit@bogon',erlang,system_info,[process_count]). //获得该节点上的Erlang的进程的数量,原则上进程数量最多是1048576个
127
(test@bogon)6> rpc:call('rabbit@bogon',mnesia,info,[]).//打印关于Mnesia的若干信息
(test@bogon)7> q() . //退出命令
ok
对于交换器的回调函数:会传入消息的信道、消息头、消息体和消息的方法
RabbitMQ:编码与模式
初始需求
正如我们在使用RabbitMQ中,我们的初始需求并不是单纯的想要寻找一个消息队列,而我们的需求是解耦应用程序之间的耦合或者是将一个耗时的操作从应用中剥离出来,异或者整合不同的语言编写出来的应用程序,但是这些问题的本质就是解耦与操作。或者换种方法进行理解是我们可以把同步的应用程序转换为异步的操作,主程序进行其他操作时,我们可以将信息来发送到队列中,然后去做其他的事情,然后等待消息的返回,而不用堵塞线程。
谁向我们推送消息
对于一个单应用同步程序,当一份数据需要做多分处理,或者数据的操作或者用户的其他操作比较耗时,这是我们最好的解决办法就是异步,比如我们在用滴滴的时候,前台负责接收用户的请求,但是如果前台同时寻找司机,那么无论是服务器还是应用程序都很难进行解决,程序相互耦合很难进行扩展,单台服务器承载过大程序容易宕机。为了解决程序之间的耦合以及将同步变为异步,我们学习致用,采用RabbitMQ,前台将用户的请求发送至队列,后台负责从队列消费请求同时将结构返回给前台。至此我们将程序的同步变为异步,将程序之间的耦合变为程序与队列的耦合。
这样进行扩展的好处为:
前台的业务处理请求比较简单,可以有效的增加前台的处理能力。 当我们的请求过高时,只需要在不部署一份消费应用到其他服务器来增加并发访问处理能力即可。 RabbitMQ会在消费处理上进行轮询的派发消息,而不需要进行负载均衡 程序变为可扩展,我们可以在加程序对消息进行应用处理
发后即忘模型
在RabbitMQ中,消费者进行消费数据,消费者发送ack信号到服务器,服务器认为消费者成功消费数据,然后从队列中删除数据。
并行处理
在并行处理中,我们有个图片上传的功能,在图片上传完成之后,然后在前台显示,但是老板发现流量消耗过大,需要将用户的图片以缩略图的方式进行展现,但是如果我们直接在程勋中加代码,会导致用户的相应时间过长,但是服务器的并发量大幅下降,因此我们采用RabbitMQ的方式,用户上传图片将压缩信息发送至队列,然后后台自行进行压缩,而不需要用户进行等待。
同时如果我们添加用户上传增加积分,只需要从队列中进行消费消息,而不需要在源代码中更改,从而提高了应用的并发量以及可扩展性
私有队列和发送确认
RPC将消息结果返回给原始客户端:在每个AMQP消息头里有个字段叫做reply_to,消息的生产者可以通过该字段来确认队列名称,并监听队列等待应答。RPC服务器检查reply_to字段,创建包含应答内容的新消息,以队列名作为路由键。通过路由键找到应答队列,路由键就是应答队列。
Rabbitmq的集群架构
rabbitmq会始终记录以下四种类型的内部元数据:
①队列元数据——队列名称和它们的属性(是否可持久化,是否自动删除)
②交换器元数据——交换器名称、类型和属性(可持久话等)
③绑定元数据——一张简单的表格展示了如何将消息路由到队列
④vhost元数据——为vhost内的队列、交换器和绑定提供命名空间和安全属性
在单一节点内,rabbitmq会将所有这些信息存储到内存中,同时将那些标记为可持久化的队列和交换器(以及它们的绑定)存储到硬盘上。
集群的队列:集群只会在单个节点而不是在所有节点上创建完整的队列信息(元数据、状态、内容),结果是只有队列的所有者节点知道有关队列的所有信息。所有其它非所有者节点只知道队列的元数据和指向该队列存在的那个节点的指针。
rabbitmq不将队列内容和状态复制到所有节点的原因:1、存储空间 2、性能
分布交换器:当消息发布到交换器时,是加上是由你所连接到的信道将消息上地路由键通交换器的绑定队列进行比较。
交换器只不过是一张查询表,而非实际上的消息路由器,将交换器在真个集群中进行复制会更加简单。
Rabbitmq节点:单节点系统只允许磁盘类型的节点,否则,每次重启rabbitmq之后,所有关于系统的配置信息都会丢失。在集群中,可以选择配置部分节点为内存节点,因为它使得想队列和交换器之类的操作更加快速。
集群节点:如果集群中唯一的磁盘节点崩溃的话,集群仍然可以保持运行但是直到将节点恢复到集群前,你无法更改任何东西。解决方案是在集群中设置两个磁盘节点,当添加内存节点时,确保告知其所有的磁盘节点(内存节点唯一存储的磁盘的元数据信息是集群中磁盘节点的地址)
rabbitmq集群的配置:
①通常来说,使用rabbitmq-server命令启动节点之后就大功告成了,但是如果不用额外参数的话,改命令会默认使用节点名称rabbit和监听端口号5672,如果尝试在同台机器用这个方法启动三个节点的话,那么第二个和第三个节点会因为节点名称和端口冲突导致启动失败。每次调用rabbitmq-server命令前,通过设置RABBITMQ_NODENAME和RABBITMQ_NODE_PORT环境变量来明确指定唯一的节点名称和端口号。
以端口号5677,节点名为rabbit7来启动一个节点
# RABBITMQ_NODE_PORT=5677RABBITMQ_NODENAME=rabbit7 \
> ./rabbitmq-server -detached
以端口号5678,节点名为rabbit8来启动一个节点
RABBITMQ_NODE_PORT=5678 RABBITMQ_NODENAME=rabbit8 ./rabbitmq-server -detached
以端口号5679,节点名为rabbit9来启动一个节点
RABBITMQ_NODE_PORT=5679 RABBITMQ_NODENAME=rabbit9 ./rabbitmq-server -detached
②要加入第二个和第三个节点,首先需要停止Erlang节点上运行的rabbitmq应用程序,并重设它们的元数据,这样它们才可以被加入并且获取集群的元数据。
停止第二个节点的命令:./rabbitmqctl -n rabbit8@bogon stop_app
重设第二个节点的元数据和状态为清空: ./rabbitmqctl -n rabbit8@bogon reset
在从节点 rabbit8@bogon上集成主节点rabbit7@bogon
./rabbitmqctl -n rabbit8@bogon join_cluster rabbit7@bogon
原始是:./rabbitmqctl -n rabbit8@bogon cluster rabbit7@bogon rabbit8@bogon(有些版本不支持cluster参数)标绿色的节点为磁盘节点,若不降节点作为磁盘节点,则不要把这个节点加入到cluster参数后面,那么这个节点就成为了内存节点
然后启动节点:./rabbitmqctl -n rabbit8@bogon start_app
查看集群的命令:# rabbitmqctl -n rabbit3 cluster_status
执行结果:
其它节点类似
我配置的rabbitmq的用户名:admin 密码:13752219649xiao
启用rabbitmq的插件:./rabbitmq-plugins enable rabbitmq_management
禁用rabbitmq的插件:./rabbitmq-plugins disable rabbitmq_management
./rabbitmqctl -n rabbit7@bogon cluster_status
Cluster status of node rabbit7@bogon ...
[{nodes,[{disc,[rabbit7@bogon,rabbit8@bogon,rabbit9@bogon]}]},
{running_nodes,[rabbit9@bogon,rabbit8@bogon,rabbit7@bogon]},
{cluster_name,<<"rabbit7@bogon">>},
{partitions,[]}]
把磁盘节点改成内存节点的命令
??????待完善
分布式集群:
需要把主机器的/var/lib/rabbitmq/.erlang.cookie中的cookie字符串复制在其他机器的相应目录中中的.erlang.cookie中,然后运行sudo /etc/init.d/rabbitmq-server restart命令重启另外两个节点Rabbitmq进程。
移除集群中的节点:
./rabbitmqctl -n rabbit8@bogon stop_app
./rabbitmqctl -n rabbit8@bogon reset
./rabbitmqctl -n rabbit8@bogon start_app
镜像队列和保留消息:
内建的双活冗余选项:镜像队列,像普通队列那样,镜像队列的主拷贝在集群中的其他节点(主对列,master)上,镜像节点在集群中的其他节点上拥有从队列的拷贝,一旦队列主节点不可用,最老的从队列将被选为新的主队列。
一种做法使用镜像队列:将所有节点都作为镜像节点,进行拷贝队列
在声明队列的时候:
queue_args={"x-ha-policy":all} //表示把队列拷贝到所有的镜像队列中去
channel.queue_declare(queue="hello-queue",arguments=queue_args)
一般推荐这么做
另外一种做法是,指定几个节点作为镜像节点
queue_args={"x-ha-policy":"nodes","x-ha-policy-params":["rabbit@bogon"]}
channel.queue_declare(queue="hello-queue",arguments=queue_args)
检测新增的从拷贝是否和镜像队列拥有相同的消息的命令
rabbitmqctllist_queues name pid slave_pidssynchronized_save_pids
导出rabbitmq配置json:
以上表格列:交换器所在虚拟主机,交换器名称,交换器类型,参数列表,消息进入速率,消息出去速率(D代表是持久的(durable))
创建交换器
Jackson2JsonMessageConverter
以上配置ip,用户名,密码,vhost是rabbitmq的服务器
控制台和以上对应
ecms.mq.queque的名字和队列Id名对应