论坛首页 Java企业应用论坛

互联网网站架构升级----消息中间件的实现方案

浏览 20503 次
该帖已经被评为良好帖
作者 正文
   发表时间:2011-08-11   最后修改:2011-08-11

    最近一直比较忙,前面设计的架构完成了基本的实现,最近工作重心发生了点转变,偷闲来继续前面的话题;

    这一篇博客准备聊聊消息系统,消息中间件对目前大中型互联网来说是非常重要的,在业务数据流动中仅次于RPC服务调用,担负着越来越复杂的网站业务从主流程上解耦的重要责任;

    从目前互联网对消息中间件的需求来看应该分为两种类型,一种是和钱相关的需求,一种是和钱无关的需求;和钱相关的需求消息的可靠性是放在第一位的,和钱无关的需求是速度放在第一位的,但这两种需求又是矛盾的,很难设计出一种既可靠又高效的系统,除非将两套方案捏合成一个系统,通过配置来选择不同方案,但从实现上说还是两种实现。所以目前业界有几种不同的设计方式来满足不同的需求。

下面看看以下几种典型实现方案:

1、以ActiveMQ为代表的可靠性优先的设计原理:

    此种方案将所有的消息数据和消息的发送状态都存储在消息服务器上,可以在消息服务上通过多种手段来保证消息的可靠性,但增加了众多复杂的可靠性保证手段后,消息从发布者到订阅者的速度势必会受到影响;此种方式发布者将消息推向消息服务器,消息服务器再将消息推向订阅者,为消息发送策略提供了很好的可扩展性;

2、以KafKa为代表的速度优先的设计原理:



    此种方式将消息的发送状态保存在客户端,同时客户端用拉的模式从消息服务器上获取消息,由于是顺序读,同时还采取了很多保证速度的策略,如zero-copy,所以此种方案速度比较快,但牺牲了很多可靠性方面的保证,比较适合Web2.0网站,这些网站对消息的可靠性要求不是很高,同时由于产生了大量的和用户状态相关的消息,需要一个高效的系统来处理这些消息;另外这种方案也比较适合日志消息的收集;


3、传统的系解耦方案:



    此种方式是不用消息中间件直接用数据库存储做的解耦,随着消息中间件的出现,这种方式的使用越来越少,但现在由于MongoDB和Redis等的兴起,一些基于这些Nosql数据库的消息应用逐渐兴起,由于消息数据和消息状态都存储在DB上,所以效率和速度在上面两种方案之间;

 

    那么有没有一种更高效的方式呢?

 

    答案是肯定的,但那可能要进一步降低可靠性!

 

    看看Facebook的Scribe日志收集系统的原理:

 

 

    如果Scribe Server正常工作,Scribe Client将App的日志(可以看成一个消息)推送到Scribe Server端,如果不正常,则写到本地文件,当Scribe Server恢复正常时再将其推送过去;这里使用本地文件作为缓冲,那么能否综合Scribe和Kafka的优点来设计一个消息系统(或日志收集系统,改造一下可以互用),看下面的方案:

    上面的MQ暂且叫他FastMQ吧,中消息发布端直接写本地文件,同时消息订阅者通过Zookeeper协调,向相关消息发布者的Agent拉消息,并在本地记录消息指针状态;

    如果嫌在消息发布端开启MQ Agent麻烦,那么如中消息拉取协议使用SCP或FTP协议,用这些Linux必备的协议提供者作为Agent减少开发和部署的难度,在服务订阅端解析这些协议流,反序列化为消息供业务使用;

    如果觉得消息只存储在消息发布端的磁盘不可靠,那么如中可以在消息订阅者处理不及消息的时候,把消息数据缓冲在消息订阅端的磁盘上来备份数据,不过这样做就使系统变的复杂,虽然提高了可靠性,但是速度就会有所降低;

 

    此种方案可以使消息分散的存储在业务本身的磁盘上,避免集中存储相互影响的缺点,同时又可以有效利用业务机器的磁盘(大部分业务机器磁盘基本没使用),另外还可以减少一次网络通信的过程,使从发送到接收的速度更快;但其本身也有不少缺点,要做好监控,避免消息大量积压的时候业务磁盘过度使用,对业务造成影响。

 

     目前我们的监控日志收集系统使用的是和中类似的方案,消息系统使用的是3方案,后期可能会将可靠性要求高的向1方案过度,可靠性要求不高的向最下面介绍这种过度!

 

 

   发表时间:2011-08-11  
消息传输分点对点模式和发布订阅模式,前者是1:1,后者是1:N,系统集成中主要应用的是1:1模式。这里使用发布者/订阅者的概念并不恰当。
业务逻辑的可靠性和消息传输系统的可靠性不能等同,传统的JMS兼容的MQ产品,包括ActiveMQ,甚至是IBM的WebSphere MQ 的所谓可靠性也并不能满足金融业务对可靠性的要求。消息传输系统不是数据存储系统,就算是有了持久化和事务的支持也不意味着它有多可靠,尤其是在集群环境下的大规模应用。消息系统是异构的、异步的、跨网络的,总不能把发送端和接收端做到一个分布式事务里。消息的丢失、重复、和非正常的延时都应该被看做是一种常态。
要保证可靠性应该在两端下功夫,毕竟数据库系统能提供的可靠性远比任何传输系统的可靠性要高得多。

在保证基本的系统稳定性的情况下,尽量提高传输性能和响应时间,通过三次交互来完成一次交易的可靠性远比依赖传输系统的可靠性用一次交互来完成同一交易要可靠得多。

我对MQ产品接触得比较多,千万不要以为把消息成功发送到MQ上就万事大吉。举个例子:如果在接收方的集群环境中某个节点出现异常,消息可能会丢失,也可能在该节点恢复后由于其持久化存储,该消息被恢复,从而造成不确定的延迟、也有可能在异常瞬间该消息被重新派发到其他路由节点,从而造成消息重复。

我的意思是说要保证应用逻辑的可靠性不要去依赖消息系统的可靠性,不是说MQ产品的那些保证可靠性的机制没有意义。
0 请登录后投票
   发表时间:2011-08-11  
你说的有道理,我这里消息的发布订阅是广义的,可以理解为消息的生产者和消费者,在我的想法里,始终觉得queue应该是一个特殊的topic;

消息的可靠性保证确实需要整个生态系统来保证,但我们这里只讨论MQ的可靠性,MQ一样可以做到非常高的可靠性,在1方案中有很多手段可以用,你也可以进行二次提交,一旦提交成功将在多分存储设备上存储,这样首先保证消息不丢失,然后再通过一些列的发生操作保证消息一定能够发送成功,记录状态,如果一直发生不成功的可以人工干预,但这样势必影响速度,我上面的几个方案是根据不同的需求来选择不同的方案,互联网除了电子商务这块,一般的web2.0对可靠性要求没那么高,但数据是海量的,对处理的效率就有比较高的要求,这样就要牺牲一点可靠性了。

一次交互如果能满足可靠的需求的话,获得的高效率将是一个很好的回报!
0 请登录后投票
   发表时间:2011-08-11  
tcp保证不了数据库级事务的可靠,数据库的日志一样保证不了业务级的可靠
前向日志和日志要记录事务成功的提交标记,业务层要保证可靠最终还得自己实现这么一套东西,不可能单独靠下层的保障机制就可以
除非业务力度和数据库事务力度和划分完全一致
文件系统,数据库都有log机制来保障,但都只能加强本层的可靠,无法实现上层的可靠
但是显然数据库应该建立在tcp,而事务架在数据库上,这些会让事情更简单,上层实现下层的可靠成本巨大,没人会用udp做可靠的应用层协议
消息栈或者说实现某种有序列化安全保障的异步机制使用db应该说是实现成本最低的,唯一的问题在于扩缩上,即使使用提供分布式的nosql,也会在转发节点遇到瓶颈
这些分布式系统,实际上传输从来不是问题和麻烦所在,而是最大限度满足写、读的视图强一致性和事务、扩缩的平衡
0 请登录后投票
   发表时间:2011-08-11  
    这里不是要保证生产者、MQ和消费者一整套的事务,用MQ的目的就是解耦,如果做多方事务的话那就失去了意义;另外MQ也不应该保证多个提交消息的事务问题,它的处理规则是一条消息的可靠性;
    MQ的可靠是只能保证数据的最终一致,而不能保证数据的强实时一致,它可以做到如果你提交给我成功(这个是否成功的通知可以通过同步,也可以通过异步),那么我一定给你最终发送成功,但我不能保证你可以立刻就获得一致的数据;
0 请登录后投票
   发表时间:2011-08-11  
redis全部存在内存中,应该不会比第1,2种方案慢吧?
除非第一二种方案不是分布式的,全部走内部内存,不经过网络。
0 请登录后投票
   发表时间:2011-08-11  
akandfxs 写道
redis全部存在内存中,应该不会比第1,2种方案慢吧?
除非第一二种方案不是分布式的,全部走内部内存,不经过网络。

MQ天生不太适合用 nosql做存储。
内存型的nosql,快不过同一jvm内的内存。越追求性能,越丢了MQ最重要的可靠性。
0 请登录后投票
   发表时间:2011-08-11  
实例化文件其实也是一种持久化的方案,消息发送出去后,并不一定意味着成功,我觉得可以设计成如果需要可靠性,则提供回查接口,保证消息的最终可靠性。对于不要求可靠的消息,则直接投递即可。本身就有一定的容错性。感觉1,2点主要是将服务端主动推送改成了订阅端主动找发布端拉取消息而已,而投递消息这个过程并不会产生多大的延迟。主动拉取消息并不是最优的方案
0 请登录后投票
   发表时间:2011-08-11  
akandfxs 写道
redis全部存在内存中,应该不会比第1,2种方案慢吧?
除非第一二种方案不是分布式的,全部走内部内存,不经过网络。


我的意思是如果作为一个DB看Redis的话不仅仅是内存,还要有持久化,甚至还要有master-slave结构,这样才能一定程度保证可靠性,如果完全用内存那可靠性就太差了,一般应用不太能接受;最起码也要Snapshotting持久!  这样就引起一系列问题了!
0 请登录后投票
   发表时间:2011-08-11  
cdredfox 写道
实例化文件其实也是一种持久化的方案,消息发送出去后,并不一定意味着成功,我觉得可以设计成如果需要可靠性,则提供回查接口,保证消息的最终可靠性。对于不要求可靠的消息,则直接投递即可。本身就有一定的容错性。感觉1,2点主要是将服务端主动推送改成了订阅端主动找发布端拉取消息而已,而投递消息这个过程并不会产生多大的延迟。主动拉取消息并不是最优的方案


投递那一瞬间确实不会花太多时间,但在投递前和投递中要做一些列的可靠性措施,比如备份消息,尽可能减少主备之间消息的不一致发生,消息的状态记录,状态的同步,失败消息的重复发送,如果不是顺序消息的话需要有随机存储和查询的机制,很多很多,最终将导致发送速度慢下来!
0 请登录后投票
论坛首页 Java企业应用版

跳转论坛:
Global site tag (gtag.js) - Google Analytics