消息中间件
消息中间件:利用高效可靠的消息传递机制进行平台无关的数据交流,并基于数据通信来进行分布式系统的集成。通过提供消息传递和消息排队模型,它可以在分布式环境下扩展进程间的通信。使用了消息传送机制或消息队列的技术来进行技术交流的插件。
1.消息中间件的作用:分布式系统之间消息的传递。
2.使用场景:
- 系统之间的通信
- 异步处理消息
- 应用之间的解耦
- 日志处理
- 流量削峰(消息的限流)
3.常见消息中间件之间的比较:
特性 | ActiveMq | RabbitMq | RocketMq | kafka |
---|---|---|---|---|
PRODUCER_COMSUMER | 支持 | 支持 | 支持 | 支持 |
PUBLISH_SUBSCRIBE | 支持 | 支持 | 支持 | 支持 |
REQUEST_REPLY | 支持 | 支持 | ||
多语言支持 | 支持(java优先) | 语言无关 | 不支持(仅java) | 支持(java优先) |
吞吐量(单机) | 万 | 万 | 万 | 10万 |
消息延迟 | 微秒 | 毫秒 | 毫秒 | |
可用性 | 高(主从) | 高(主从) | 非常高(分布式) | 非常高(分布式) |
消息丢失 | 低 | 低 | 不会丢失(理论上) | 不会丢失(理论上) |
消息重复 | 可控制 | 有重复(理论上) | ||
部署难度 | 低 | 中 | ||
社区活跃度 | 高 | 高 | 中 | 高 |
成熟度 | 成熟 | 成熟 | 比较成熟 | 成熟(日志领域) |
特点 | 功能齐全,被大量开源项目使用 | 性能很好(由于Erlang语言的并发能力) | 各个环节用分布式扩展设计,主从HA;支持上万个队列,多种消费模式,性能很好。 | |
支持协议 | OpenWire, STOMP, REST, XMPP, AMQP | AMQP | 自己定义的一套(社区提供jms-不成熟) | |
持久化支持 | 内存,文件,数据库 | 内存,文件 | 磁盘文件 | |
事务支持 | 支持 | 支持 | 支持 | |
负载均衡 | 支持 | 支持 | 支持 | |
部署方式 | 独立,嵌入 | 独立 | 独立 | |
评价 | 优点:成熟的产品,已经在很多公司得到应用(非大规模场景),文档完善,支持协议多,以及有多种语言的成熟的客户端。 缺点:根据用户反馈,可能会出现问题,可能会丢失消息。目前社区不活跃,对5.x维护较少;ActiveMq不适用于上千个队列的应用场景
| 优点:由于erlang语言的特性,性能方面比较好;管理界面丰富,在互联网公司也有较大规模的应用;支持AMQP,有多重语言且支持AMQP的客户端; 缺点:erlang语言难度较大,集群不支持动态扩展。 | 优点:模型简单,接口易用(jms的接口很多场合不太实用)。在阿里大规模应用。集群规模大概在50台左右,单日处理消息上百亿;性能非常好,可以大量堆积消息在broker中;支持多种消费,包括集群消费、广播消费等。版本更新快。 缺点:产品较新文档比较缺乏,没有在mq核心中去实现jms等接口,对一有系统而言不能兼容。阿里内部还有一套未开元的MQAPI,这一层API可以将上层应用和下层Mq的实现解耦,(阿里内部有多个mq的实现,如notify,metaq1.xmetaq2.xrockemq )等,使得下面mq可以很方便的进行奇幻和升级而对应用无任何影响,目前这一套东西未开源。
|
JMS:
java平台消息中间件API规范,java应用程序之间进行消息交换。并且通过提供标准的产生、发送、接收消息的接口简化企业应用开发。
1.JMS对象模型要素:
- 连接工程:创建一个jms连接。
- jms连接:客户端和服务端之间的一个连接。
- jms会话:客户端和服务器会话的状态,是建立在连接上的
- jms目的:消息队列
- jms生产者:消息的生成
- jms消费者:接收消息
- broker:消息中间件的实例(如:activemq)、
2.点对点(Point-to-Point(P2P) )模式:队列,一个消息只有一个消费者(即使有多个接受者监听队列),消费者要向队列应答成功。
![](https://i-blog.csdnimg.cn/blog_migrate/ef7948b7b9e1c8c4d1e4fcb8ce6fda76.png)
3.主体模式/发布订阅(Publish/Subscribe(Pub/Sub) ):发布到topic的消息会被当前主体所有的订阅者消费。
![](https://i-blog.csdnimg.cn/blog_migrate/6ad62bac478c6f9c57c110424a8b55d8.png)
4.Request-Response模式:
在前面的两种模式中都是一方负责发送消息而另外一方负责处理。而我们实际中的很多应用相当于一种一应一答的过程,需要双方都能给对方发送消息。于是请求-应答的这种通信方式也很重要。它也应用的很普遍。
注:请求-应答方式并不是JMS规范系统默认提供的一种通信方式。
![](https://i-blog.csdnimg.cn/blog_migrate/6e433f05a6c634f79ad99af2d988df36.png)
jms规范中的消息类型:TextMessage,MapMessage,ObjectMessage,BytesMessage,StreamMessage;
事务:
客户端成功接收一条消息的标志是这条消息被签收。
成功接收一条消息一般包括如下三个阶段:
1.客户端接收消息;
2.客户端处理消息;
3.消息被签收。
ACKNOWLEDGE:
1.Session.AUTO_ACKNOWLEDGE
当客户端从 receive 或onMessage 成功返回时,Session 自动签收客户端的这
条消息的收条。在AUTO_ACKNOWLEDGE 的Session 中,同步接收receive 是上述三个阶
段的一个例外,在这种情况下,收条和签收紧随在处理消息之后发生。
2.Session.CLIENT_ACKNOWLEDGE
客户端通过调用消息的 acknowledge 方法签收消息。在这种情况下,签收发生。
在Session 层面:签收一个已消费的消息会自动地签收这个Session 所有已消费消息的
收条。
3.Session.DUPS_OK_ACKNOWLEDGE
此选项指示 Session 不必确保对传送消息的签收。它可能引起消息的重复,但
是降低了Session 的开销,所以只有客户端能容忍重复的消息,才可使用(如果Activ
eMQ 再次传送同一消息,那么消息头中的JMSRedelivered 将被设置为true)。
对队列来说,如果当一个Session 终止时它接收了消息但是没有签收,那么Activ
eMQ 将保留这些消息并将再次传送给下一个进入队列的消费者。
对主题来说,如果持久订阅用户终止时,它已消费未签收的消息也将被保留,直到
再次传送给这个用户。对于非持久订阅,AtiveMQ 在用户Session 关闭时将删除这些消
息。
ActiveMQ 支持两种消息传送模式:
1.PERSISTENT(持久性消息)
这是 ActiveMQ 的默认传送模式,此模式保证这些消息只被传送一次和成
功使用一次。对于这些消息,可靠性是优先考虑的因素。可靠性的另一个重要方面是确
保持久性消息传送至目标后,消息服务在向消费者传送它们之前不会丢失这些消息。这
意味着在持久性消息传送至目标时,消息服务将其放入持久性数据存储。如果消息服务
由于某种原因导致失败,它可以恢复此消息并将此消息传送至相应的消费者。虽然这样
增加了消息传送的开销,但却增加了可靠性。
2.NON_PERSISTENT(非持久性消息)
保证这些消息最多被传送一次。对于这些消息,可靠性并非主要的考虑因素。
此模式并不要求持久性的数据存储,也不保证消息服务由于某种原因导致失败后消息不
会丢失。
在支持事务的session中,producer发送message时在message中带有transaction ID。broker收到message后判断是否有transaction ID,如果有就把message保存在transaction store中,等待commit或者rollback消息。所以ActiveMq的事务是针对broker而不是producer的,不管session是否commit,broker都会收到message。
如果producer发送模式选择了persistent,那么message过期后会进入死亡队列。在message进入死亡队列之前,ActiveMQ会删除message中的transaction ID,这样过期的message就不在事务中了,不会保存在transaction store中,会直接进入死亡队列。具体删除transaction ID的地方是在
org.apache.activemq.util.BrokerSupport的doResend,将transaction ID保存在了originalTransactionID中,删除了transactio
on 签收模式的设置。
在带事务的 Session 中,签收自动发生在事务提交时。如果事务回滚,所有已经接
收的消息将会被再次传送。
在不带事务的Session 中,一条消息何时和如何被签收取决于Session 的设置。
1.Session.AUTO_ACKNOWLEDGE当客户端从 receive 或onMessage 成功返回时,Session 自动签收客户端的这条消息的收条。在AUTO_ACKNOWLEDGE 的Session 中,同步接收receive 是上述三个阶段的一个例外,在这种情况下,收条和签收紧随在处理消息之后发生。
2.Session.CLIENT_ACKNOWLEDGE
客户端通过调用消息的 acknowledge 方法签收消息。在这种情况下,签收发生。
在Session 层面:签收一个已消费的消息会自动地签收这个Session 所有已消费消息的
收条。
3.Session.DUPS_OK_ACKNOWLEDGE
此选项指示 Session 不必确保对传送消息的签收。它可能引起消息的重复,但是降低了Session 的开销,所以只有客户端能容忍重复的消息,才可使用(如果ActiveMQ 再次传送同一消息,那么消息头中的JMSRedelivered 将被设置为true)。
使用本地事务
在事务中生成或使用消息时,ActiveMQ 跟踪各个发送和接收过程,并在客户端发出
提交事务的调用时完成这些操作。如果事务中特定的发送或接收操作失败,则出现异常。
客户端代码通过忽略异常、重试操作或回滚整个事务来处理异常。在事务提交时,将完
成所有成功的操作。在事务进行回滚时,将取消所有成功的操作。
本地事务的范围始终为一个会话。也就是说,可以将单个会话的上下文中执行的一
个或多个生产者或消费者操作组成一个本地事务。
不但单个会话可以访问 Queue 或 Topic (任一类型的 Destination ),而且单
个会话实例可以用来操纵一个或多个队列以及一个或多个主题,一切都在单个事务中进
行。这意味着单个会话可以(例如)创建队列和主题中的生产者,然后使用单个事务来
同时发送队列和主题中的消息。因为单个事务跨越两个目标,所以,要么队列和主题的
消息都得到发送,要么都未得到发送。类似地,单个事务可以用来接收队列中的消息并
将消息发送到主题上,反过来也可以。
由于事务的范围只能为单个的会话,因此不存在既包括消息生成又包括消息使用的
端对端事务。(换句话说,至目标的消息传送和随后进行的至客户端的消息传送不能放
在同一个事务中。)
ActiveMQ安装:
其它:https://blog.csdn.net/my550117634/article/details/82782785
AMQP:
1.amqp:
- 开发标准,支持不同语言和不同产品。
- 生产者:消息的创建者,发送到AMQP的消息中间件。
- 消费者:连接到AMQP的消息中间件,订阅到队列上,进行消息的消费,分为持续订阅(basicConsumer)和单条订阅(basicGet)
- 消息:包括有效载荷和标签。有效载荷就是要传输的数据。标签描述有效载荷的属性,rabbitMq用标签来决定谁获得当前消息。消费者只能拿到有效载荷。
- 信道:虚拟的连接,建立在真实的tcp连接之上的。信道的创建没有限制。
2.交换器、队列、绑定、路由键
队列通过路由键(routing key,某种确定的规则)绑定到交换器,生产者把消息发送到了交换器,交换器根据绑定的路由键将消息路由到特定的队列,定于队列的消费者进行接收。
如果消息到了无人订阅的队列:消息会一直在队列中等待,rabbitMq会默认队列是无限长度的。
多个消费者订阅到同一队列:消息会轮询的方式发送给消费者,每个消息只会发送给一个消费者。
消息路由到了不存在的队列:被忽略,当消息不存在,消息丢失。
消息的确认机制:消费者收到的每一条消息都必须进行确认(分为自动确认和消费者自行确认)。
消费者在声明队列时,制定autoAck参数,true自动确认,false时,rabbitMq会等到消费者显示的发回一个ack信号才会删除消息。
autoAck=false,有足够时间让消费者处理消息,直到消费者显示调用basicAck为止。
RabbitMq中消息分为了两部分:1.等待投递的消息;2.已经投递,但是没有收到ack信号的。ack的消息是没有超时的。
明确的拒绝消息:
1.消费者断连
2.消费者使用reject命令(requeue=false,重新发布消息,false移除消息),
3.nack命令(批量拒绝)
创建队列:
(生产/消费)declareQueue。消费者订阅了队列,不能再声明队列了。相关参数(exclusive 队列为应用程序私有,auto-delete最后一个消费者取消订阅时,队列会自动删除,durable队列持久化)
检测队列是否存在:Declare 时的passive参数
3.四中交换器:
direct,fanout,topic,headers
direct:路由键完全匹配时,消息投放到对应队列。AMQP实现都必须有一个direct交换器(默认交换器),名称为空白字符。队列不声明交换器,会自动绑定到默认交换器,队列的名称作为路由键。
fanout:可以理解为广播,不处理路由键,发送到所有队列上。
topic:主体,匹配路由键,使来自不同源头的消息达到同一个队列
headers:不处理路由键。而是根据发送的消息内容中的headers属性进行匹配,匹配消息头,其余与direct一样,实用性不大。
在绑定Queue与Exchange时指定一组键值对;headers属性是一个键值对,可以是Hashtable,键值对的值可以是任何类型。
Broker:即消息队列服务器实体
Exchange:消息交换机,它指定消息按什么规则,路由到哪个队列。
Queue:消息队列载体,每个消息都会被投入到一个或多个队列。
Binding:绑定,它的作用就是把exchange和queue按照路由规则绑定起来。
Routing Key:路由关键字,exchange根据这个关键字进行消息投递。
vhost:虚拟主机,一个broker里可以开设多个vhost,用作不同用户的权限分离。
producer:消息生产者,就是投递消息的程序。
consumer:消息消费者,就是接受消息的程序。
channel:消息通道,在客户端的每个连接里,可建立多个channel,每个channel代表一个会话任务。
工作机制
生产者:
生产者连接到RabbitMQ服务器上,打开一个消息通道(channel);
生产者声明一个消息交换机(exchange),并设置相关属性。
生产者声明一个消息队列(queue),并设置相关属性。
生产者使用routing key在消息交换机(exchange)和消息队列(queue)中建立好绑定关系。
生产者投递消息都消息交换机(exchange)上
生产者关闭消息通道(channel)以及和服务器的连接。
消费者:
exchange接收到消息后,根据消息的key以及设置的binding,进行消息路由,将消息投递到一个或多个消息队列中。
exchange的几种类型
(1). Direct交换机:完全根据key进行投递。
例如,绑定时设置了routing key为abc,客户端提交信息提交信息时只有设置了key为abc的才会投递到队列;
(2).Topic交换机:在key进行模式匹配后进行投递。
例如:符号”#”匹配一个或多个字符,例如"abc.#"可以匹配abc.def.hig
符号”*”匹配一串连续的字母字符,例如”abc.*”只可以匹配”abc.def”。
(3).Fanout交换机:它采取广播模式,消息进来时,将会被投递到与改交换机绑定的所有队列中。
注意:如果消息交换机(exchange)和消息队列(queue)都是持久化的话,那么他们之间的绑定(Binding)也是持久化的。
如果消息交换机和消息队列之间一个持久化、一个非持久化,那么就不允许绑定
应答机制:
在下边的demo中,你会发现消息只能被消费一次,如果某个消费者在消费消息时,突然奔溃了,这条消息无法继续被消费,而消息服务器中也没有了这条消息,那么也就意味着这条消息和这个消费者无法继续完成这条消息的内容.为保证消息永不丢失,rabbitMQ支持消息应答机制,即消费之接收到消息并完成对应的消息内容,然后想消息服务器发送一条确认命令,消息服务器才会把这条消息删除掉,如果这个消费者同消息服务器断开连接,name消息服务器会把发送给这个消费者的消息并且没有返回状态的消息发送给其他的消费者,
注意:消费者退出后消息将会被重发,但是由于一些未能被确认消息不能被释放,RabbitMQ将会消耗掉越来越多的内存。我们需要调用一下basicAck方法.
日志处理场景:
1.有交换器(topic)log_exchange,日志级别有error,info,warning,应用模块有User,order,email,路由键规则是 日志级别+"."+应用模块名称(如:info.user)
2.发送邮件失败,报告一个email的error,basicPublic(message,'log-exchange','error.email')
队列的绑定:queueBind('email-log-queue','log-exchange','*.email')
监听所有模块的所有级别日志:queuebind('all-log-queue','log-exchange','#')
'.'会把路由键分为好几个标识符,'*'匹配一个标识符(点分开的部分为一个),'#'匹配一个或多个(xxx.yyyy.zzzz---》 xxx.*.zzz, xxx.#, #.zzzz)
、
4.虚拟主机:
Vhost,真实RabbitMq服务器上的mini型虚拟的mq服务器,有自己的权限机制,Vhost提供了一个逻辑上的分离,可以区分客户端,避免队列和交换器的名称冲突。RabbitMq包含了一个缺省的vhost:“/”,用户名guest,口令guest(guest用户只能在本机访问)。
5.消息持久化:
1.队列是必须持久化的
2.交换器也必须是持久化
3.消息的投递模式必须(int 型)
以上条件全部满足,消息才能持久化
持久化缺点:性能(下降10倍)
6.AMQP和JMS区别
| JMS | AMQP |
定义 | Java api | 协议 |
Model | P2P Pub/Sub | Direct Fanout Topic headers |
支持消息类型 | 5种 | Byte[] 自行消息序列化,Json化 |
综合评价 | Java系统,模型满足要求,跨平台较差 | 协议,天然跨平台,跨语言 |
事务和发送发确认:
事务:
这里首先探讨下RabbitMQ事务机制。
RabbitMQ中与事务机制有关的方法有三个:txSelect(), txCommit()以及txRollback(), txSelect用于将当前channel设置成transaction模式,txCommit用于提交事务,txRollback用于回滚事务,在通过txSelect开启事务之后,我们便可以发布消息给broker代理服务器了,如果txCommit提交成功了,则消息一定到达了broker了,如果在txCommit执行之前broker异常崩溃或者由于其他原因抛出异常,这个时候我们便可以捕获异常通过txRollback回滚事务了。
RabbitMQ为我们提供了两种方式:
方式一:通过AMQP事务机制实现,这也是从AMQP协议层面提供的解决方案;
Spring AMQP做的不仅仅是回滚事务,而且可以手动拒绝消息,如当监听容器发生异常时是否重新入队。
持久化的消息是应该在broker重启前都有效。如果在消息有机会写入到磁盘之前broker宕掉,消息仍然会丢失。在某些情况下,这是不够的,发布者需要知道消息是否处理正确。简单的解决方案是使用事务,即提交每条消息。
方式二:通过将channel设置成confirm模式来实现;
producer端confirm模式的实现原理
生产者将信道设置成confirm模式,一旦信道进入confirm模式,所有在该信道上面发布的消息都会被指派一个唯一的ID(从1开始),一旦消息被投递到所有匹配的队列之后,broker就会发送一个确认给生产者(包含消息的唯一ID),这就使得生产者知道消息已经正确到达目的队列了,如果消息和队列是可持久化的,那么确认消息会将消息写入磁盘之后发出,broker回传给生产者的确认消息中deliver-tag域包含了确认消息的序列号,此外broker也可以设置basic.ack的multiple域,表示到这个序列号之前的所有消息都已经得到了处理。
confirm模式最大的好处在于他是异步的,一旦发布一条消息,生产者应用程序就可以在等信道返回确认的同时继续发送下一条消息,当消息最终得到确认之后,生产者应用便可以通过回调方法来处理该确认消息,如果RabbitMQ因为自身内部错误导致消息丢失,就会发送一条nack消息,生产者应用程序同样可以在回调方法中处理该nack消息。
在channel 被设置成 confirm 模式之后,所有被 publish 的后续消息都将被 confirm(即 ack) 或者被nack一次。但是没有对消息被 confirm 的快慢做任何保证,并且同一条消息不会既被 confirm又被nack 。
开启confirm模式的方法
生产者通过调用channel的confirmSelect方法将channel设置为confirm模式,如果没有设置no-wait标志的话,broker会返回confirm.select-ok表示同意发送者将当前channel信道设置为confirm模式(从目前RabbitMQ最新版本3.6来看,如果调用了channel.confirmSelect方法,默认情况下是直接将no-wait设置成false的,也就是默认情况下broker是必须回传confirm.select-ok的)。
confirm的工作机制:
Confirms是增加的一个确认机制的类,继承自标准的AMQP。这个类只包含了两个方法:confirm.select和confirm.select-ok。另外,basic.ack方法被发送到客户端。
confirm.select是在一个channel中启动发布确认。注意:一个具有事务的channel不能放入到确认模式,同样确认模式下的channel不能用事务。
当confirm.select被发送/接收。发布者/broker开始计数(首先是发布然后confirm.select被记为1)。一旦channel为确认模式,发布者应该期望接收到basic.ack方法,delivery-tag属性显示确认消息的数量。
当broker确认了一个消息,会通知发布者消息被成功处理;
basic的规则是这样的:
一个未被路由的具有manadatory或者immediate的消息被正确确认后触发basic.return;
另外,一个瞬时态的消息被确认目前已经入队;
持久化的消息在持久化到磁盘或者每个队列的消息被消费之后被确认。
关于confirm会有一些问题:
首先,broker不能保证消息会被confirm,只知道将会进行confirm。
第二,当未被确认的消息堆积时消息处理缓慢,对于确认模式下的发布,broker会做几个操作,日志记录未被确认的消息
第三,如果发布者与broker之间的连接删除了未能得到确认,它不一定知道消息丢失,所以可能会发布重复的消息。
最后,如果在broker中发生坏事会导致消息丢失,将会basic.nack那些消息
总之,Confirms给客户端一种轻量级的方式,能够跟踪哪些消息被broker处理,哪些可能因为broker宕掉或者网络失败的情况而重新发布。
确认并且保证消息被送达,提供了两种方式:发布确认和事务。(两者不可同时使用)在channel为事务时,不可引入确认模式;同样channel为确认模式下,不可使用事务。
发送方确认
为什么要有个发送方确认模式?
生产者不知道消息是否真正到达RabbitMq,也就是说发布操作不返回任何消息给生产者。
AMQP协议层面为我们提供的事务机制解决了这个问题,但是事务机制本身也会带来问题:
1、严重的性能问题
2、使生产者应用程序产生同步
RabbitMQ团队为我们拿出了更好的方案,即采用发送方确认模式,该模式比事务更轻量,性
能影响几乎可以忽略不计。
发送方确认模式的机制。
注意:发送方确认模式和消费者对消息的确认是不同的。
RabbitMq在Windows下安装和运行
1、下载Erlang:
http://www.erlang.org/downloads/19.2
2、下载Windows版RabbitMq:
http://www.rabbitmq.com/releases/rabbitmq-server/v3.6.6/rabbitmq-server-3.6.6.exe
3、安装并配置环境变量:
ERLANG_HOME C:\Program Files\erl8.2
path下添加 %ERLANG_HOME%\bin
RABBITMQ_BASE C:\Program Files\RabbitMQ Server\rabbitmq_server-3.6.6
path下添加 %RABBITMQ_BASE%\sbin;%RABBITMQ_BASE%\ebin
4、在开始菜单中启动服务
5、在安装目录的sbin下运行rabbitmqctl.bat status
客户端Jar包和源码包下载地址:
http://repo1.maven.org/maven2/com/rabbitmq/amqp-client/5.0.0/amqp-client-5.0.0.jar
http://repo1.maven.org/maven2/com/rabbitmq/amqp-client/5.0.0/amqp-client-5.0.0-sources.jar
还需要slf4j-api-1.6.1.jar
如果是Maven工程加入:
<dependency>
<groupId>com.rabbitmq</groupId>
<artifactId>amqp-client</artifactId>
<version>5.0.0</version>
</dependency>
注意:5系列的版本最好使用JDK8及以上, 低于JDK8可以使用4.x(具体的版本号到Maven的中央仓库查)的版本
与Spring集成
配置文件中增加命名空间:
xmlns:rabbit="http://www.springframework.org/schema/rabbit"
http://www.springframework.org/schema/rabbit
http://www.springframework.org/schema/rabbit/spring-rabbit-2.0.xsd
配置文件中的配置
- 连接工厂配置
- <rabbit:admin>
- 声明队列
- 声明交换器
- 队列和交换器进行绑定
- 生产者端要声明RabbitmqTemplate
异步处理
场景:
用户注册,写入数据库成功以后,发送邮件和短信。
串行模式 ************spend time : 251ms
并行模式 ************spend time : 153ms
消息队列模式:************spend time : 66ms
应用解耦
场景:
SpringBoot整合RabbitMQ
在Linux(以CentOS7为例)下安装RabbitMq:
1、wget https://packages.erlang-solutions.com/erlang-solutions-1.0-1.noarch.rpm
2、rpm -Uvh erlang-solutions-1.0-1.noarch.rpm
3、yum install epel-release
4、yum install erlang
5、wget http://www.rabbitmq.com/releases/rabbitmq-server/v3.6.6/rabbitmq-server-3.6.6-1.el7.noarch.rpm
6、yum install rabbitmq-server-3.6.6-1.el7.noarch.rpm
RabbitMq集群
內建集群的设计目标:
客户端在节点崩溃的情况下可以运行,线性扩展来扩充消息的吞吐量
可以保证消息的万无一失吗?
当一个节点崩溃了以后,节点所有队列上的消息都会丢失。默认不会将队列的消息在集群中复制。
集群中的队列
在集群中不会复制,其他节点只会保存队列所处的节点和元数据,消息的传递给队列的所有者节点。
集群中的交换器
会进行复制。本质就是一个类似于hashmap的映射表。
集群中的节点
两种:内存节点,磁盘节点。单机情况下,一定是个磁盘节点。集群里面,要求每个集群必须有至少以一个磁盘节点,出于高可用考虑,建议配两个。
本机集群(不建议安装,有条件应在多个服务器上安装):
RABBITMQ_NODE_PORT=5672 RABBITMQ_NODENAME=rabbit rabbitmq-server -detached
RABBITMQ_NODE_PORT=5673 RABBITMQ_NODENAME=rabbit_1 rabbitmq-server -detached
RABBITMQ_NODE_PORT=5674 RABBITMQ_NODENAME=rabbit_2 rabbitmq-server -detached
rabbitmqctl -n rabbit_1@centosvm stop_app
rabbitmqctl -n rabbit_1@centosvm reset
rabbitmqctl -n rabbit_1@centosvm join_cluster rabbit@centosvm
rabbitmqctl -n rabbit_1@centosvm start_app
rabbitmqctl cluster_status
rabbitmqctl -n rabbit_2@centosvm stop_app
rabbitmqctl -n rabbit_2@centosvm reset
rabbitmqctl -n rabbit_2@centosvm join_cluster rabbit@centosvm --ram
rabbitmqctl -n rabbit_2@centosvm start_app
rabbitmqctl cluster_status
从外部要访问虚拟机中的mq记得在防火墙中打开端口
firewall-cmd --add-port=5673/tcp --permanent
firewall-cmd --add-port=5674/tcp --permanent
rabbitmqctl add_user mq mq
rabbitmqctl set_permissions mq ".*" ".*" ".*"
rabbitmqctl set_user_tags mq administrator
rabbitmq-plugins -n rabbit_1@centosvm enable rabbitmq_management
多机下的集群
- 修改 /etc/hosts
192.168.1.1 node1
192.168.1.2 node2
192.168.1.3 node3
2、Erlang Cookie 文件:/var/lib/rabbitmq/.erlang.cookie。将 node1 的该文件复制到 node2、node3,由于这个文件权限是 400,所以需要先修改 node2、node3 中的该文件权限为 777,然后将 node1 中的该文件拷贝到 node2、node3,最后将权限和所属用户/组修改回来。
3、运行各节点
4、在node2、node3上分别运行
[root@node2 ~]# rabbitmqctl stop_app
[root@node2 ~]./rabbitmqctl reset
[root@node2 ~]# rabbitmqctl join_cluster rabbit@node1
[root@node2 ~]# rabbitmqctl start_app
内存节点则是rabbitmqctl join_cluster rabbit@node1 –ram
移除集群中的节点
[root@node2 ~]# rabbitmqctl stop_app
[root@node2 ~]./rabbitmqctl reset
[root@node2 ~]# rabbitmqctl start_app
镜像队列
什么是镜像队列
如果RabbitMQ集群是由多个broker节点构成的,那么从服务的整体可用性上来讲,该集群对于单点失效是有弹性的,但是同时也需要注意:尽管exchange和binding能够在单点失效问题上幸免于难,但是queue和其上持有的message却不行,这是因为queue及其内容仅仅存储于单个节点之上,所以一个节点的失效表现为其对应的queue不可用。
引入RabbitMQ的镜像队列机制,将queue镜像到cluster中其他的节点之上。在该实现下,如果集群中的一个节点失效了,queue能自动地切换到镜像中的另一个节点以保证服务的可用性。在通常的用法中,针对每一个镜像队列都包含一个master和多个slave,分别对应于不同的节点。slave会准确地按照master执行命令的顺序进行命令执行,故slave与master上维护的状态应该是相同的。除了publish外所有动作都只会向master发送,然后由master将命令执行的结果广播给slave们,故看似从镜像队列中的消费操作实际上是在master上执行的。
RabbitMQ的镜像队列同时支持publisher confirm和事务两种机制。在事务机制中,只有当前事务在全部镜像queue中执行之后,客户端才会收到Tx.CommitOk的消息。同样的,在publisher confirm机制中,向publisher进行当前message确认的前提是该message被全部镜像所接受了。
镜像队列的使用
添加policy
Rabbitmqctl set_policy Name Pattern Definition
Name:策略的名字
Pattern:队列匹配模式(正则表达式)
Definition:镜像的定义:ha-mode,ha-params,ha-sycn-mode
ha-mode: all/exactly/nodes
ha-params: n表示几个节点上复制/节点名称
ha-sycn-mode:automatic manual
对队列名称以“queue_”队列进行镜像,只在两个节点上完成复制
Rabbitmqctl set_policy ha_queue_two “^queue_” ‘{“ha-mode”:”exactly”,”ha-params”:2,”ha-sycn-mode“:“atuomatic”}’
在代码中也要进行编写
使用HAProxy(以下步骤仅供参考)
作用:
安装配置:
1.下载最新haproxy安装包,官网:http://www.haproxy.org
2.上传到linux的haproxy用户根目录下,并解压:
tar -zxvf haproxy-1.5.8.tar.gz
创建目录/home/haproxy/haproxy
3.安装
cd haproxy-1.5.8
make TARGET=linux26 ARCH=x86_64 PREFIX=/home/haproxy/haproxy #将haproxy安装到/home/haproxy/haproxy ,TARGET是指定内核版本
make install PREFIX=/home/haproxy/haproxy
进入/home/haproxy/haproxy 目录,创建/home/haproxy/haproxy/conf目录,复制配置examples
cp /home/haproxy/haproxy-1.5.8/examples/haproxy.cfg /home/haproxy/haproxy/conf/
4、配置修改(以haproxy rabbitmq 配置为关键字搜索)
互联网时代的消息中间件
消息发送一致性
Void busi{
//业务操作
//写库
//发送消息
}
业务成功,消息发送也要成功
业务失败,消息不应该发送
消息的重复
- 让处理消息的服务具有幂等性
Update a set zz = 12; Update a set zz = zz+12(无幂等性);
- db或者缓存保存消息的处理状况,消息id作为唯一性索引
消息中间件与RPC的关系
两者并不是水火不容的关系,两者可以很好的进行融合,结合起来使用。Rpc客户端调用rpc服务,或者rpc服务返回处理结果,就完全可以通过消息中间件进行。使用消息中间件做rpc有何好处:自动将消息路由到合适的地方,通过消息中间件可以在rpc服务集群中做到负载均衡,甚至当rpc服务中某台服务挂了,可以做到自动重发。
消息的数据量不能太大。