如何设计一款高性能的队列通知应用

在做支付系统的时候,当用户完成支付,需要异步通知平台,告知第三方平台订单已经完成,由于订单量特别大,且有时候会有网络不稳定,丢包。第三方平台接收慢等情况,会引起充值回调发送很慢,导致队列不能快速发完。考虑过开启多个发送队列程序,不过还是有卡住的情况,还请多多指教。

Geo5
Geo5
463
编辑于2011-11-04
评论 (4)链接2011-11-04 
  • 0 支持
    支付系统涉及到安全性的问题,只能耐心的等吧 – 小飞 2011-11-04
  • 0 支持
    跟安全性没有关系,只是想如何增加并发能力 – 程序员1999 2011-11-04
  • 0 支持
    使用应答机制 ,如果发送后 短时间内没有得到应答, 再次发送消息。 当然 这个时候 也可能第一个是成功的 只是处理慢而已。 所以 接收对方 也要做相应的处理。 我们之前和银行通讯 都是设定时间 多次发送来处理的
    要建立完善的应答机制。 这个 不能使用并发来完成
    – wanghoney 2012-06-13
  • 0 支持
    @wanghoney 我通过扫描订单表的支付状态,发起回调通知。但订单量非常大的情况,性能应该会很差。 还有个想法是生产订单号的时候加入到队列中,但确想不到一个好的方法防止丢单。 请问支付系统一般如何处理这个阶段。 – AaronOTW 2013-09-23

楼上的理论太强大了,如果只是想增加并发能力,我觉得可以从以下几个方面着手:

1.通知分级,将一些重要的,以及一些没那么重要的信息,分到不同的发送队列中去,也可根据具体业务来划分多个级别,这样能尽量避免不重要的信息影响到重要信息的发送;

2.缩短每条信息发送处理时间,在一定时间内,如果信息接收端不能给出正确回应,就应该及时中断此次请求,把此信息的发送状态置为失败状态,立即去处理下一条信息,这样可以避免因某条信息卡住进而影响整个队列卡住的问题,同时要做好信息重发机制,用专门的进程来对发送失败的信息可尝试多次发送。

3.做好发送队列程序的多线程或者多进程处理机制,启动多进程或者使用多台服务器同时发送信息。在发送信息前,可以使用信息关键字以及信息发送状态作为条件去尝试update此条信息的状态为“发送中”,如果返回受影响的行数大于0,则可以对此信息执行发送操作,反之表示此条信息正在被别的进程所使用,这样就能避免信息的重复发送,再配合适当的获取发送信息队列中数据的方法,就能实现多进程的发送。

另外还有一种方法能够实现多进程的队列,在要发送的信息表里加上一个int型的字段,数据插入到这个表时,可以随机给这个字段插入某个范围内的数值,如0、1、2、3,这样我们就可以启动四个进程来同时发送信息,每个进程分别处理此字段对应的不同值的信息,这种方法是非常容易扩展发送性能的。

何远伦
编辑于 2011-11-11
该答案已被锁定,无法对其进行评论,编辑及投票。
()
评论 (0)链接 • 2011-11-07

个人认为你的问题是由于网络不稳定(或接收慢)、没考虑网络IO阻塞问题造成的。多进程发送并不能解决发送速度的问题,反而会慢。因为线程是操作系统调度的最小单位,进程只是承载资源的最小单位,进程间的切换是非常耗资源的。
Windows下的IOCP(完成端口)网络模型、Linux下的epoll+零拷贝可以很好地解决你的两个问题。当然不是所有的情况都适用,在网络很通畅的情况下,使用select模型更快。
下面分别介绍下IOCP(完成端口)、epoll、零拷贝以及为何要使用它们,以及它们的底层运作原理,最后介绍一下运用它们时所要考虑的问题。可能会觉得内容有点多,都是我经过消化、总结的,一个个字敲上来的。小弟不才,所学有限,有所疏漏请指教。

一、技术背景:
IOCP
1.windows提供了五种网络编程模式:选择(Select)、异步选择(WSAAsyncSelect)、事件选择(WSAEventSelect)、重叠I/O和完成端口(Completion Port)共五种I/O模型。每一种模型均适用于一种特定的应用场景。
2.其中完成端口模型能处理大规模并发通信,响应速度惊人,可同时处理十万、百万级的客户连接,很多基于windows的大型网络服务程序,如网络游戏、通讯程序都应用完成端口模型。
“完成端口”使用一组异步通信函数,如“WSASend”、“WSARecv”、“AcceptEx”、“ConnectEx”取代同步通信函数“send”、“recv”、“accept”、“connect”。
3.为什么异步通信函数比同步通信函数效率高呢?一层层往下分析,使用同步函数,当数据未到达时线程堵塞,而使用异步函数不管数据是否到达都立即返回。而线程堵塞时不能处理其他客户数据,这时只能靠增加线程,采用一条线程对应一个客户的方法,而随着线程增多,线程间的切换是非常耗CPU的,因为CPU需要来回切换保存线程上下文信息。而异步函数可以利用较少的线程数处理大量客户。
4.再往下看,同步函数导致的线程堵塞,异步函数直接返回,操作系统是如何实现的呢?我们举一个发送函数的例子,“send”和“WSASend”。send函数把要发送的数据用IRP逐层传到底层网卡小端口驱动中,由该驱动函数MPSendPackets发送数据,应用层send线程会一直等待由驱动返回的带“完成标志”的IRP,才会往下继续执行。而WSASend把要发送的IRP数据传送到驱动中后,驱动会设置一回调函数,并立即返回带“完成标志”的IRP,WSASend不会等待,继续往下执行。当驱动中回调函数执行“发送”操作后,会通知与WSASend绑定的“完成端口”,告诉数据已发送成功。
完成端口使用流程图

epoll:linux后期版本的内核中为替代select和poll所采用的,原理和windows的IOCP差不多,不过多介绍。原理也就是,不用那么多次的线程切换,不用select检测应用层缓冲区中是否有数据(其实是网卡小端口驱动中的缓冲队列往上逐级传上来的)。

零拷贝:是Linux中的概念,指直接从内核层把数据拷贝到应用层,省去了中间的种种操作(端口驱动->类驱动->应用层,等待端口驱动中的数据到达一定数量一起拷贝上来,等等)。但零拷贝的使用不是必须的,视情况而定。

二、具体应用:
1.单线程多线程:虽然说IOCP和epoll属于异步调用,可以减少线程数量。但如果单线程的话,线程去干把数据写到缓冲区中的活的时候,此时线程是处于阻塞状态的,CPU闲置了。此时CPU完全可以去处理接收的。
所以这里也使用多线程,程序启动时,就开启多条线程,参与异步调用。windows下的话把线程绑定到完成端口句柄,默认开启60多条线程。Linux下更具实际情况效仿。
2.接收和发送线程分开:由于平台做的支付系统相当于中间代理,既要接收银行的支付成功通知,又要把此通知转发给用户。所以接收线程在收到数据后,立马投递给发送线程发送。这样省去了中间保存数据缓冲区的读、写锁,锁同步是要话时间的(也是个阻塞和调度的过程)。

以上IOCP模型在以前公司做的大面积种植机(种警用木马用的)中得到应用,表现出色,我测试过同时种植200台机器丝毫没感觉处理不过来。而epoll+零拷贝的方案是一个大型旁路解析还原系统,接在全国各地省厅、地市,解析还原所有网民的数据。

@毛杭军,这次满意不,哈哈哈...

Geo5
Geo5
463
编辑于 2011-11-05
该答案已被锁定,无法对其进行评论,编辑及投票。
()
评论 (1)链接 • 2011-11-05
  • 0 支持
    想的太多 从哪里拷贝过来的 – wanghoney 2012-06-13

答案已经很多了,说点我自己的见解吧:
首先把回调的消息队列拿出来,单独作为一个app项目:
1.采用多个消息队列处理,这样,我们可以将一个队列中的内容做一个hash的分发,这样就相当于负载。
2.采用文件映射与内存读取相结合的方式来保证消息队列中数据的安全性
3.对于消息队列承载的服务器也可以进行多个服务器进行负载
4.增加有限次的消息重发以及定时的心跳机制来客服网络带来的问题

该答案已被锁定,无法对其进行评论,编辑及投票。
()
评论 (0)链接 • 2012-01-17

处理完后,把数据保存到数据库中,状态send为未发送。
通过一个后台定时任务,读取这些send=0(未发送的数据),启用新线程去通知第三方,假如第三方发送过来消息表明接收成功,就把send设为1
如果没有获取到第三方的消息或是,消息不对,则sendCount++
当然设定最大发送次数为10,如果10次仍然不成功,则通过人工监控联系第三方

该答案已被锁定,无法对其进行评论,编辑及投票。
()
评论 (0)链接 • 2012-02-27

为什么不直接用RabbitMQ这样的成熟消息队列应用呢?没有必要重新发明轮子嘛。RabbitMQ可以处理每秒数万条消息,如果这样的性能都还不够,还可以进行分布式部署来提升性能。RabbitMQ是用Erlang写的,天然支持分布式。

该答案已被锁定,无法对其进行评论,编辑及投票。
()
评论 (0)链接 • 2012-05-09

你這個我看了以後,跟性能沒關係,你這個瓶頸是卡在網絡上,網路吞吐不解決的話,肯定是卡住的。

该答案已被锁定,无法对其进行评论,编辑及投票。
()
评论 (3)链接 • 2011-11-08
  • 0 支持
    太经典了,一语惊醒梦中人! – 黄文彬 2011-11-08
  • 0 支持
    网络吞吐暂时是无法解决的,各个机房线路不一样。 – 程序员1999 2011-11-08
  • 0 支持
    重发机制 即可解决 – wanghoney 2012-06-13

由于网络原因太慢的话,只有设置超时时间,做好重发机制;

队列的话,可以选用 beanstalk,gearman;

该答案已被锁定,无法对其进行评论,编辑及投票。
()
评论 (0)链接 • 2011-11-05

我觉得这个和并发已经没有太直接的关系了,网络不好,你再怎么发也是一样啊。

上面说的都很对,在并发的基础上,你更多的是要去关心你们两边的发送机制及规则。如何去规范在发送过程中的成功与失败,然后做出相应的处理方法。

如果觉得不好,可以的话,再加上你老有钱的话,拉专线吧 ,这样就行了(PS:哈哈..)

该答案已被锁定,无法对其进行评论,编辑及投票。
()
评论 (0)链接 • 2012-07-31
德问是一个专业的编程问答社区,请 登录注册 后再提交答案