rabbitmq是一个开源的AMQP实现,服务器端用erlang语言编写,支持多种客户端。用于在分布式系统中存储转发消息,在易用性、扩展性、高可用性等方面表现不俗。
4369:是erlang端口/节点名称映射程序,用来跟踪节点名称监听地址,在集群中起到一个类似DNS的作用。
5672/5671:AMQP 0-9-1和1.0客户端端口,没有使用SSL和使用SSL的端口。
25672:用于rabbitMQ节点间和CLI工具通信,配合4369使用。
15672:HTTP_API端口,管理员用户才能访问,用于管理rabbitmq,需要启用management插件。
61613/61614:当STOMP插件启用的时候打开,作为STOMP客户端端口(根据是否使用TLS选择)
1883/8883:当MQTT插件启用的时候打开,作为MQTT看客户端端口(根据是否使用TLS选择)
15674:基于websocket的STOMP客户端端口(当插件web STOMP启用的时候打开)
15674:基于websocket的MQTT客户端端口(当插件web MQTT启用的时候打开)
RabbitMQ绑定端口安装完之后,如果访问不了有可能是防火墙的问题。
RabbitMQ安装包中带有管理插件,但是需要手动激活
rabbitmq-plugins enable rabbitmq_management
RabbitMQ有一个默认的用户“guest”,但这个用户默认只能用本机访问,要让其他机器访问,需要穿件一个新用户,为其分配权限
#添加用户
rabbitmqctl add_user admin admin
#为用户分配权限
rabbitmqctl set_user_tags admin administrator
#为用户分配资源权限
rabbitmqctl set_permissions -p /admin ".*" ".*" ".*"
不能访问management plugin
用户可以通过AMQP做的任何事外加:
列出自己可以通过AMQP登入的vitual hosts
查看自己的virtual hosts中的queues、exchanges和bindings
查看和关闭自己的channels和connections
查看有关自己的virtual hosts的“全局”的统计信息,包括其他用户在这些virtual hosts中的活动
management可以做的任何事外加:
查看、创建、删除自己的virtual hosts所属的policies和parameters
management可以做的任何事外加:
列出所有virtual hosts,包括他们不能登录的virtual hosts
查看其他用户的connection和channels
查看节点级别的数据如clustering和memory使用情况
查看真正的关于所有virtual hosts的全局的统计信息
policymaker和monitoring可以做的任何事外加:
创建和删除virtual hosts
查看、创建、删除users
查看、创建、删除permissions
关闭其他用户的connections
高级消息队列协议,是应用层协议的一个开放标准,为面向消息的中间件设计。
producer在发送消息前建立链接connection ,在connection 里可以建立多个channel。producer发送的消息有一个路由键(routerKey),将这个消息先发送到交换机exchange,然后从exchange再发送到queue,而exchange与对应的queue是通过bind绑定在一起的。
fanout:交换机会把接收到的消息发送到所有queue
direct:根据bindkey和routekey的匹配来分发消息到指定的queue
默认交换机:在producer没有指定交换机的情况下,是会把消息发送到默认的交换机上,默认交换机会根据消息内queue的名字来分发到指定的queue上
Topic:可以模糊匹配的交换机,在bind的时候是使用com.*这种模式来绑定queue,当producer发送的消息内routekey是com.*格式的时候就会匹配到topic绑定的queue
header:根据消息的内容来匹配,如消息内有x=1,header交换机绑定queue时也用的x=1来作为规则,那么这条消息会被header发送到指定queue里
在控制台里找到exchange
也可以在queue里选择exchange
在rabbitmq的发布订阅模式下,channel的basicQos属性可以设置同一时刻,服务器发送几条消息给消费者。这个值不能太小,那样会造成consumer与broker交互频繁,也不能设置过大,因为如果consumer消费速度比较慢,那么拿到了大量消息后,其他consumer就处于闲置状态,因为大部分消息已经分发给了那个处理慢的consumer。
消息体:
在实际应用中,消息体一般是一个带有业务逻辑结构的数据,比如json串。当然也可以进一步对这个消息体进行序列化
附加信息:
用来标书这条消息,如目标交换机名字、路邮键、一些自定有属性等
对于rabbitmq来说,一个broker可以看成一个rabbitmq服务节点,,或者服务实例。也可以将一个broker看做一台rabbitmq服务器
虚拟主机,表示一批交换机、消息队列和相关对象。虚拟主机是共享相同的身份认证和加密环境的独立服务器域。就如同一个mysql里面有多个database一样。每个vhost本质上就是一个mini版的rabbitmq服务器,拥有自己的队列、交换机、绑定和权限机制。vhost是AMQP概念的基础,必须在链接时指定,rabbitmq默认的vhost是“/”
频道或通道,是建立在connection连接上的一种轻量级连接。
大部分操作实在channel接口中完成的,包括定义队列的声明queueDeclare、交换机的声明exchangeDeclare、队列的绑定queueBind、发布消息basicPublish、消费消息basicConsume等。如果把connection比作一条光纤电缆的话,那么channel就是光缆中的一束光纤。connection可以创建任意数量的channel
路邮键。生产者将消息发给交换机的时候,一般会指定一个路邮键,用来指定这个消息的路由规则。routing需要与交换机类型和绑定建(bindingkey)联合使用。在交换机类型和绑定建固定的情况下,生产者可以在发送消息给交换机时,通过指定routingkey来决定消息流向哪里。
交换机,生产者将消息发送到exchange,有交换机将消息路由到一个或多个队列。如果路由不到,或返回生产者,或直接丢弃。
rabbitmq的内置对象,用于存储消息
将exchange与对应的queue进行绑定
消费者,接收消息的一方。消费者链接到rabbitmq服务器,并订阅到队列上。
当消费者消费一条消息时,只是消费消息的消息体。在消息路由的过程中,消息的标签会丢弃,存入到队列中的消息只有消息体,消费者专业只会消费到消息体,也就不知道消息的生产者是谁,当然消费者也不需要知道。
rabbitmq实现高可用集群部署需要以一台服务器的.erlang.cookie文件为标准,复制到其他服务器上,因为erlang语言必须有一个相同的cookie文件才能进行分布式的通信。
集群内节点之间的消息是以同步元数据的方式来同步的。每个节点不会同步其他节点的消息内容,但是会知道哪条消息的元数据在哪个节点上。如node1内存储着消息A,这时有一个请求访问node2,需要消费消息A,那么这时node2会从node1上把A拿出来给这个请求。
一个大的集群,可能有多个用户,这些用户代表不同的部门或不同的业务应用,他们之间是毫无关联的。那么我们需要将这些用户使用的账户权限及其使用的rabbitmq资源分开。这时就可以构建多个虚拟主机。每个用户对应一个虚拟主机。虚拟主机之间是相互隔离的,这样就可以让用户之间互不影响。
进入admin用户给order-user用户创建虚拟主机
选择一个虚拟主机
设置权限,这里是根据queue的名称来过滤,配置成"."代表所有queue都有权限
如果此处设置得是“eq-.”,那么向这个虚拟主机发送消息时,选择的queue必须都是"eq-"开头。
由于集群内的单点容易出现故障。如果将数据存放在单节点内,其他节点只是同步元数据的话,一旦单点出现故障,则会导致数据丢失。那么解决这种问题就需要数据同步。rabbitmq提供镜像队列模式用于queue和exchange的数据同步
打开policies
virtual host=选择虚拟主机
name=镜像队列名字
pattern=模式使用正则表达式(^)
apply to=应用到哪里
priority=优先级(数值越大优先级越高)
definition=目标(什么样的内容需要同步)在下图中选择
之后点击add policy,添加成功之后可以看到queue里面有一个+1
这里显示的是这个queue在node2上,但是在node3上有一个镜像同步队列
rabbitmq本身提供了节点之间的通信,但是还可以使用负载均衡的方式来配置集群,使用lvs来负载均衡,只暴露一个负载均衡地址,就可以实现client与集群之间的通信。
#查看节点状态
rabbitmqctl status
#停止运行rabbitmq的erlang虚拟机和rabbitmq服务应用
rabbitmqctl stop [pid_file]
如果指定了pid_file,还需要等待指定进程的结束。pid_file是通过调用rabbitmq-server命令启动rabbitmq服务时创建的,默认情况下存放于Mnesia目录中。
如果使用rabbitmq-server -detach
这个带有-detach后缀的命令来启动rabbitmq服务则不会生成pid_file文件。
#停止rabbitmq服务应用,但是erlang虚拟机还是处于运行状态。
#此命令执行优先于其他管理操作(这些管理操作需要先停止rabbitmq应用),比如rabbitmqctl reset
rabbitmqctl stop_app
#启动rabbitmq应用。
#此命令典型的用途是在执行了其他管理操作之后,重新启动之前停止的rabbitmq应用,,比如rabbitmqctl reset
rabbitmqctl start_app
#节点重置还原到最初状态
#包括从原来所在的集群中删除此节点,从管理数据库中删除所有的配置数据,如配置的用户、vhost等,以及删除所有持久化信息。
执行此命令前必须停止rabbitmq应用(比如可以先执行rabbitmqctl stop_app)
rabbitmqctl reset
#强制还原。此命令不论当前管理数据库的状态和集群配置是什么,都无条件的重置节点,只能在数据库或集群配置已损坏的情况下使用。
rabbitmqctl force_reset
#将节点加入指定集群。在这个命令执行前要停止rabbitmq应用并重置节点
#-n nodename:指定需要操作的目标节点,如rabbit@node1
#cluster_node:需要加入的集群节点名,格式同上
#--ram:集群节点类型,有两种:ram|disc,默认disc
#ram;内存节点,所有的元数据都存储在内存中
#disc:磁盘节点,所有的元数据都存在磁盘中
#磁盘节点好处是不会丢失数据,缺点是慢,单节点可以使用disc,集群模式因为已经有元数据同步,所以使用ram提高效率。
rabbitmqctl [-n nodename] join_cluster {cluster_node} [--ram]
#查看集群状态
rabbitmqctl cluster_status
#修改集群节点类型,使用此命令钱要停止rabbitmq应用
rabbitmqctl change_cluster_node_type {disc|ram}
#将节点从集群中删除,允许离线执行
rabbitmqctl forget_cluster_node [--offiine]
#在集群中的节点应用启动前咨询cluster_node节点的最新信息,并更新响应的集群信息。这个和join_cluster不同,它不加入集群
rabbitmqctl update_cluster_nodes {clusternode}
#确保节点可以启动,即使它不是最后一个关闭的节点
#当集群异常宕机了,那么应该从最后一台关闭的节点开始重启
rabbitmqctl force_boot
#设置集群名称。
#集群名称爱客户端连接时会通报给客户端
#集群名称默认是集群中第一个节点的名称,通过此命令可以重置
rabbitmqctl set_cluster-name {name}
元数据包含以下内容:
队列元数据:队列名称及属性
交换机:交换机名称及属性
绑定关系元数据:交换机与队列或者交换机与交换机
vhost元数据:为vhost内的队列、交换机和绑定提供命名空间及安全属性之间的绑定关系
多机多借点部署
设计目标是使rabbitmq在不同的broker借点之间进行消息传递而无须建立集群,该功能在一下场景非常有用:
各个借点运行在不同版本的erlang和rabbitmq上
网络环境不稳定,比如广域网中
这个插件的同步原理是在本地创建一个localqueue,broker需要同步queue信息时先把数据同步到本地创建的这个localqueue上,这样就保证数据不会丢失,等到网络稳定时会自动同步到其他的broker插件上。
与上一个插件相比有一点小小差别,shovel能够可靠且持续的从一个broker中的队列(作为源source)拉取数据并转发至另一个broker中的交换机
松耦合:可以移动位于不同管理域中的broker或者集群上的消息,这些broker或者集群可以包含不同的用户和vhost,也可以使用不同的rabbitmq和erlang版本
支持广域网:同样基于AMQP协议在broker之间进行通信,被设计成可以容忍时断时续的连通情形,并且能够保证消息的可靠性
高度定制当shovel成功连接后,可以对其进行配置以执行相关的AMQP命令
持久化分为队列持久化、消息持久化、交换器持久化不管是持久化的消息是非持久化的消息都可以被写入到磁盘。
持久化消息放在内存里是为了处理速度快,放在磁盘是为了将消息持久化。
非持久化消息在内存中存储,当内存不够用时才会存入磁盘。
队列持久化是在定义队列时的durable参数来实现的,durable为true时,队列才会持久化
持久化的队列在管理页面可以看到有个“D”
消息持久化通过消息的属性deliveryMode来设置是否持久化,在发送消息时通过basicPublish的参数传入。通过传入MessageProperties.PERSISTENT_TEXT_PLAIN实现持久化
与队列一样,交换器也需要在定义时设置持久化标志。
durable=true则开启持久化
exchangeDeclare(String exchange,String type,boolean durable)
rabbitmq有一个最大内存阈值,可以通过配置来实现。当接收的消息达到最大阈值时,rabbitmq不会再接收消息,并进入阻塞状态。
rabbitmq有一个内存换页阈值,当达到换页阈值时,将当前内存内的消息放入到磁盘内,而此时会区分持久化消息和非持久化消息。持久化消息因为在接收到消息后就已经存入磁盘,所以在换页时不会重复存入磁盘。此时只会将非持久化消息存入到磁盘内。
当内存使用超过配置的阈值或者磁盘剩余空间低于配置的阈值时,rabbitmq会出现告警。此时会暂时阻塞客户端连接,并停止接收客户端发来的消息,以避免服务器崩溃,客户端与服务端的心跳检测也会失效。
当出现告警时可以通过管理命令临时调整内存大小
rabbitmqctl set_vm_memory_high_watermark
fraction为内存阈值,rabbitmq默认为0.4,表示当rabbitmq使用内存超过40%时,就会产生告警并阻塞所有生产者链接。
通过此命令修改阈值在broker重启后将会失效,通过修改配置文件的方式设置得阈值则不会再重启后消失,但需要重启broker才会生效。
配置文件 /etc/rabbitmq/rabbitmq.conf
vm_memory_high_watermark.relative=0.4
#vm_memory_high_watermark.absolute=1GB
rabbitmq提供了relative和absolute两种配置方式
relative相对值,即前面的fraction,建议取值在0.4-0.66之间,不建议超过0.7
absolute绝对值,单位为kb、mb、gb。可以做临时调整,命令是rabbitmqctl set_vm_memory_high_watermark absolute
在某个broker节点内存降到达到阈值并阻塞生产者之前,它会尝试将队列中的消息换页到磁盘,以释放内存空间。持久化和非持久化的消息都会被转入磁盘中,其中持久化消息本身就在磁盘中有一份副本,这里会将持久化的消息从内存中清楚掉。
默认情况下,在内存达到阈值的50%时会进行换页
也就是说,在默认的内存阈值为0.4的情况下,当内存超过0.4*0.5=0.2时会进行换页操作。
可以通过在配置文件中配置vm_memory_high_watermark_paging_ratio项来修改此值
vm_memory_high_watermark.relative=0.4
vm_memory_high_watermark_paging_ratio=0.75
以上配置将会在rabbitmq内存使用率达到30%时进行换页动作,并在40%时阻塞生产者。当vm_memory_high_watermark_paging_ratio的值大于1时,相当于禁用了换页功能。
当磁盘剩余空间低于确定的阈值时,rabbitmq同样会阻塞生产者,这样可以避免因非持久化的消息持续换页而耗尽磁盘空间导致服务器崩溃。
默认情况下,磁盘阈值为50mb,表示当磁盘剩余空间低于50mb时会阻塞生产者并停止内存中消息的换页动作。
这个阈值的设置可以减小,但不能完全消除因磁盘耗尽而导致崩溃的可能性。比如在两次磁盘空间检测期间内,磁盘空间从大于50mb被耗尽到0mb。
一个相对谨慎的做法是将磁盘的阈值设置为与操作系统所显示的内存大小一致。这样最大存入磁盘消耗的空间=内存。
通过命令可以临时调整磁盘阈值
rabbitmqctl set_disk_free_limit
rabbitmqctl set_disk_free_limit mem_relative
disk_limit为固定大小,单位为KB、MB、GB
fraction为相对值,建议取值为1.0-2.0之间。对应配置如下
disk_free_limit.relative=2.0
#disk_free_limit.absolute=50mb
At most once:最多一次,消息可能会丢失,但不会重复传输
At least once:最少一次,消息不会丢失,但可能重复传输
Exactly once:恰好一次,保证每条消息都被传输到broker且仅传输一次
RabbitMQ支持:At most once和At least once
At least once实现:
1.消息生产者需要开启事物机制或者publisher confirm机制,确保消息可靠的传输到rabbitmq中。
2.消息生产者要配合使用mandatory参数或备份交换机来确保消息能够从交换机到路由到队列中,进而能够保存下来不会丢失。
消息从BIZDB中取出到sender,然后将消息发送给rabbitmq,broker会返回一个回执确认(confirm),然后有两个分支操作
1.如果producer收到confirm,则说明消息发送成功,消息状态status将会变为成功,MSGDB(消息数据库)则会把状态修改为已发送成功。
2.如果producer没有收到confirm,则说明消息发送不成功。此时消息状态status不会被修改,MSGDB中的消息会通过定时任务进行重发,并记录消息重发次数(Step7)。
consumer在消费消息是需要将autoAck设置为false,这样就能手动确认消息消费是否成功,以免消息丢失。
当消费消息出现异常时(nack),可以选择重发,也可以不重发。不重发消息可以有两种处理方式
1.将异常消息加入死信队列。并创建一个单独的消费者专门处理这些异常消息。
2.直接丢弃
支持插件,通过插件可以扩展多种核心功能:支持多种协议、系统状态监控、其他AMQP 0-9-1交换类型、节点联合等。许多功能都是通过插件实现的;
通过命令可以查看rabbitmq的插件列表
rabbitmq-plugins list
通过命令启用和禁用插件
rabbitmq-plugins enable plugin-name 启用plugin-name插件
rabbitmq-plugins disable plugin-name 禁用plugin-name插件
实例插件
rabbitmq_auth_mechanism_ssl:身份验证插件,允许RabbitMQ客户端使用x509证书和TLS(PKI)证书进行身份验证
rabbitmq_event_exchange:事件分发插件,使客户端可以接受到Broker的queue .deleted、exchange.created、binding.created等事件
rabbitmq_management:rabbitmq web端控制台,基于web页面的管理/监控插件
传输数据代码
无论接口调用是否成功都会出现分布式事物问题:
1.接口调用成功,订单系统数据库提交失败(saveorder失败),运单系统没有回滚,导致数据不一致
2.接口调用超时,订单系统数据库回滚,但运单系统收到接口继续执行,导致数据不一致
在消息发送时记录发送到MQ的消息及其状态,将这些信息存入本地表
利用RabbitMQ消息发送确认机制(confirm)
开启发布确认机制后,MQ在受理消息之后会返回回执,发送成功的消息会将消息状态改为成功。
如果MQ处理消息失败或响应超时,则会在一定时间后重发消息。注:MQ消息存入本地表之后不是由订单系统实现发送消息,而是通过定时任务扫表方式来实现向MQ发送消息。
开启手动ACK模式。由消费者控制消息的重发、清楚、丢弃。
幂等性。防止重复处理,,一次用户操作,只对应一次数据处理。根据订单ID或业务数据,判断是否重复处理,重复的消息直接忽略。
当运单中心处理成功后ACK通知MQ,MQ清楚已处理的消息。
当运单系统出现异常,则运单系统NACK通知MQ重发消息
当重发次数过多时,需要控制重发次数,达到重发次数阈值时,选择直接丢弃或者进入死信队列DLQ。通知处理这些异常信息的运维部门。