有时候脑子真是就往一处使傻劲。看来直觉有时候是好的,但是有时候还是需要站在高处来看一下问题的特点,根据问题的特点来寻找解决方案。
以前做了一个项目,其实是个中间件,接受客户机的HTTP (get/post) 请求,然后按照一个比较复杂的逻辑,转换成特定格式的HTTP, SOAP或REST请求,最后发送到另外一个系统(blackbox),读取该系统的返回值,再返回给特定客户机。对那些特定的需要较长时间的请求(一个客户请求可能最终转换成包含上万个SOAP请求,并包含gpg, scp, ftp等子任务),为客户机程序提供callback接口,来查询工作状态。
简单图示:
最近,客户提出一个改动,说那个blackbox的系统,会定期down掉几个小时。这个downtime是可以提前预知的。现在要求,在黑盒子down的这段时间,对于那些不是需要长时间的请求,进行队列管理,也就是我的中间件(叫它MW)要对这些请求入队,等DOWNTIME过去以后,再dequeue,发送到黑盒子系统。
有几个需要注意的地方。
- 对于每个客户的请求是transaction like的。要一个接一个地发送。就是说,某客户发了请求1,2,3,向黑盒子发请求的时候,要先发1,1处理完毕,再发2,2结束再发送3。因为3个请求有可能是“创建数据”,“查询数据”,"更新数据“. 【顺便说一下,尽管specification上写的是transaction-like ,但是我觉得这个词用的并不合适。因为涉及到transaction 就有ACID,就是like起码也有rollback等特性,这些在这个项目中并没体现。其实 for the same Client, a request should not be dequeued and sent to BLACKBOX until receiving the response of previous request 的描述更恰当些】
- 集群环境队列的同步以及并行dequeue的调度。
- downtime过后,因为有可能我的MW仍在处理队列中的请求,所以从客户新进的请求仍然可能会进行入队操作,而不是直接转换,发送到黑盒子。
- jboss(任一或全部)down再start,不影响整个队列操作。
有点难的地方就是既要让集群最大发挥并行操作的作用,又要保证客户请求的transaction-like。另外还有个2个jboss的同步问题。
开始考虑可以用JBOSS的HASingleton服务来做队列管理。后来发现,HASingleton是server层上的一个服务,用于这种情况并不合适。
然后就想怎么才能解决上面提到的那个困难。2个JBOSS的同步,还是得通过数据库,否则要做的东西太多了。所以就一直打算数据库里有个全局的flag,根据它,MW来做enqueue还是直接发到黑盒子。但在细小的地方总不是太保险,主要体现在2个JBOSS状态同步上。而且,这样一来,其实同一时间,只有一个JBOSS在工作,另一个必须等它完成了才能取队列里下一个请求。 在这上费了些时间,总不能完美解决问题。
前天晚上陪儿子玩的时候一想,既然这个"transaction-like"是存在单一的客户机身上的,为什么不每个客户设2个FLAG,一个是是否enqueue,另一个是是否有JBOSS在对这个客户进行dequeue。如果一个客户的请求在队列里已经没有了,再有从这个客户来的请求,MW就可以直接发送到黑盒子了,尽管队列里可能还有其它客户发来的requests。更重要的是,这样,就可以按客户来取队列里的请求,2个JBOSS也就可以同时工作了,效率也提高了。于是就这么设计并实现了。
当然后来对一些小问题做了一些特殊的实现。
- 每个jboss有个heartbeat线程,这样,一个JBOSS通过heartbeat interval可以知道另外一个是不是还“活着”。如果DOWN了并且自己空闲(没有进一步的dequeuing任务),就接管当初正被它工作的那个客户的请求。
- 当一个JBOSS着手处理一个客户的请求的时候,数据库记录下这个客户的ID。这样当这个JBOSS DOWN了,再启动后,可以知道之前的工作情况,以便继续工作 (如果还没被另一个JBOSS接管)。
- DOWNTIME的标志由quartz来负责维护。它本身就支持cluster,所以省了一些事。

No comments:
Post a Comment