消息中间件想法

     当两个系统需要进行交互的时候,一般都webservice,rmi,或者rpc的调用,这些都是常用的,但是两个系统之间耦合很大,之间多是同步的调用,一个系统的不健康可能会影响到另外一个系统,j2ee规范中还有jms可以使用,利用jms两个系统之间就可以进行异步通信了.
     我厂在jms的思想上自己研发了一个高可靠性的,高性能的消息中间件,实现了自己的分布式事务处理



一般是消息发布者发送到消息服务器,消息服务器保存在队列或者文件或者DB中,之后转发给订阅者

高可靠性

由于是分布式应用,可靠性就有如下方面的要求:

1.       发布者发送消息到message servermessage server能不能收到,收到后怎么做,message sever明确收不到怎么办.      

2. message server投递消息给订阅者,消息投递成功,订阅者怎么做;消息投递失败,message server 怎么做.


message server一般的处理方法:

1)       发布者发送消息到message server,当message server收到消息,首先保存消息,然后发送成功响应给发布者,这样正常情况下发布者会收到message的回执

2)       当网络异常时(网络中断,或者网络延迟),在发布者发送消息到message server的过程中就有问题,此时message server跟这条消息没有任何的关系,发布者会收到发送失败的回执(message client发的,即发布者底层的message依赖的jar包中的网络层,message client和message server建立socket连接后才可能发送消息),这个时候发布者的业务处理代码可以根据这个发送失败的回执进行相应的处理:继续发送消息或者忽略.

 

3)       当发布者发送消息给message server,message server保存消息时有问题的话,message server会发送一个失败的回执给发布者,发布者接收到回执后,处理方法同2),其中又有问题,这个回执发送给发布者的时候,网络异常,这个时候在发布者这端的message client是有超时设置的,当超时一段时间没有接收到回执消息的时候,message client就认定此次发送是失败了,自己生产失败消息返回给业务代码,业务代码处理同2)

 

4)       当发布者发送消息给message server,message server保存消息成功,message server发送成功回执给发布者,这个时候网络异常,没有发送成功,这个时候发布者这端的message client有超时设置,产生失败消息给业务代码,业务代码处理同2)

 

5)      message   server投递给订阅者时,发送成功的话,订阅者业务处理成功后,发送回执给message servermessage server收到成功回执会删除保存的消息的

 

6)       message server投递给订阅者时,发送不成功的话,message server有超时处理的,超时后会重新发送,重试的策略是时间间隔越来越长的,所以先到达message server的消息不一定在后到达的消息前面被投递成功。

 

7)       message server投递给订阅者时,发送成功了,但是订阅者业务处理失败发送失败回执或者订阅者发送成功回执时网络异常(比如超时)了,message server会认为发送失败,这样就会重新发送,策略同6)

 

从上面的分析可知,message server的可靠性是要发布者和订阅者一同保证的。当出现上面的2) 3)

的情况时,发布者的业务代码要检查消息回执,然后在出错的情况下继续发送消息。在出现6) 7)的情况时,订阅者不能吃掉异常,不能提前返回。

 

因为要保证出现上面的几点问题时的可靠性,那么重复消息和发送的顺序就不能得到保证了。


分布式事务

   一般可以使用两阶段提交,达到最终一致性


在发布端是有个callback接口,在下图第③步中决定是提交事务还是回滚事务.

当第①,②步处理完后,这个时候网络出现异常或者其他情况,第③步操作没

有成功,notify server轮询事务消息,如果发现事务了超过时间阀值,就发起第④步

操作,主动询问发布者是否需要提交还是回滚事务。发布者发起第⑤步操作作为响应

高性能

底层网络交互使用nio socket.
使用NIO,而不是BIO,这个可以认为是为了节省服务器资源,避免了一个请求一个线程的开销,单线程处理了多任务(IO多路复用)
是非阻塞的
IO,并不是说NIOBIO快,只是NIO的连接数上限大于BIO,对于连接数大,并发大的情况下,因为占用资源少,线程上下文切换少,所以服务器更加的稳定.

NIO socket配合的是java.nio.buffer,byteBufferDirectByteBufferNO_DirectByteBuffer,

DirectByteBuffer就是操作系统的内存直接分配,而NO_DirectByteBufferjvm里面堆里面分配的,

所以DirectByteBuffer会比NO_DirectByteBuffer稍快,因为NO_DirectByteBuffer多了一次从osjvm heap的拷贝。

其它的还有tcp几个影响性能的选项已经操作系统参数的调整


nio
框架
NIO框架大多是典型的Reactor模式的,核心是Event loop+事件分发+事件处理的业务代码,MinaNetty也是这样做的.


上图中Acceptor是接受NIO socket中的连接事件,ReactorNIO socket读写事件的loop,然后loop中根据各种事件分发到事件处理的业务代码,具体的可以看:

http://gee.cs.oswego.edu/dl/cpjslides/nio.pdf


NIO socket本质上面是多路复用非阻塞IOjava里使用Selector类表示多路复用器,在此上面注册的SocketChannel都对应有一个SelectionKey,所以如果有多个连接的话Selector内部就有多个SelectionKey组成一个集合,程序需要遍历这个集合,检查有没有需要处理的key,这里需要处理的意思是至少一个连接注册了且其对应的连接通道已为 I/O 操作准备就绪。

所以这个遍历还是被阻塞的。

随着并发数量的提高,nio框架采用一个Selector来支撑大量连接事件的管理和触发可能就会遇到瓶颈问题(毕竟是单线程在遍历一个大的集合),所以可以使用多个Selector并存的结构,一般Selector数量是cpu*2个,在一个连接需要建立的时候,他通过计数器加1之后取Selector数量的模作为Selector数组的索引取得Selector。这样多个selector可以在多个线程里面遍历自己的集合,效率会好很多。

mina里面有一个专门的Selector来处理OP_ACCEPT事件,cpu*2+1Selector来处理OP_READOP_WRITE,这样连接事件和读写事件就相互不影响了(个人理解读写事件往往有后端的业务代码,所以处理的时间会久一点,对于有好多连接事件要处理的代码来说,这种时间的影响还是比较严重的)

 

可以把每个连接抽象成每个session,在session上面还做了心跳检测,心跳检测主要是为了保持长连接,这样的话连接不会断掉重建,毕竟tcp的建立需要3次握手,关闭要4次握手,再加上tcp建立后是慢启动的,不能很快的达到最高传输速率,所以长连接的优势比较明显,而且message server也是比较适合长连接的

 

因为有心跳检测和网络异常,所以断掉的连接必须能自动重新链接,可以收集了这些连接做成队列,然后放在一个线程池中进行重新链接.

当然还需要做发布者和订阅者得订阅管理实现,流量控制等其它东西.



 

posted on 2011-08-23 15:08 nod0620 阅读(1671) 评论(0)  编辑  收藏 所属分类: 小打小闹


只有注册用户登录后才能发表评论。


网站导航:
 
<2024年12月>
24252627282930
1234567
891011121314
15161718192021
22232425262728
2930311234

导航

统计

常用链接

留言簿

随笔分类

随笔档案

文章分类

文章档案

搜索

最新评论

阅读排行榜

评论排行榜