中国电信和中国移动的短信协议要求客户端主动连接到网关收取信息,并且接受的连接只能有一个。因此客户端的接收程序必须尽快处理收到的信息,好尽快接收下一个信息。而客户端的程序又有很多事情必须处理,比如写日志并分发给相关的应用。
开始我用多线程的方式,即接收程序收到从网关发来的信息后,启动一个新线程处理收到的信息,本身接着收下一个信息。新启动的线程负责写日志,然后分发给相关的应用。这种方式运行起来效果不错。
接着我又遇到一个问题,写日志和分发给应用的程序是两个不相关的东西,放在一起有点别扭。写日志的程序对实时性要求不高,只要保证记录了就可以;而分发程序需要尽量快,不应该等日志完了再执行。再起一个线程写日志,一个线程分发?这样线程多了反而更慢,不太好。
对
与从网关收到的信息我们可能有许多处理程序,比如一个是写日志,一个是分发给应用。就日志来说,就有写数据库、写文本文件、控制台输出等。所以我采用了消
息队列的方式,这样接收程序收到从网关来的信息后送出一个消息到消息队列中,需要处理的程序自己来取就是了。这个消息队列用的是
Topic/Subscribe模式,就是接收程序发出的同一个消息可能被多个处理程序接受。这样对信息的不同处理,我就可以写不同的消息处理程序。
接收完了,看看向网关发送信息的情况。短信网关一般也只允许一个连接进行发送,而且中国电信和中国移动使用的短信网关协议都是异步的,这样用消息队列的发送也很理想。
整体框架入下面这幅图,
Transport Service 是和网关通讯的程序。它负责保持连接、发送信息给网关、接收网关来的信息,并把信息发送到消息队列
Deliver Controller 是分发信息给相关应用的控制器。它从消息队列中收取Deliver信息,然后根据信息的内容或者用户的状态,用不同的Application Deliver把信息分发给应用
Application
Deliver
负责具体分发信息给应用。不同的应用可能要求不同的信息接受方式,比如多数是用HTTP方式,分发程序用POST方法把信息提交给应用,这中方式用的是
AppDeliverHttpPost;有的是用SOAP,用的就是AppDeliverSoap了。总之,要什么新方法,写一个Application
Deliver就可以了。
Logger 就是写日志的程序。它从消息队列中收取所以消息,然后写日志。如果要有不同的日志方法可以写不同的Logger,比如我有一个DBLogger,是向数据库里写日志的实现。
Sender 是通过Transport Service发送Submit信息给网关的程序。短信协议是异步的,但多数应该要求同步,就是要知道发送是否成功。因此Sender多提供了一个实现同步的方法。Sender发送后,从消息队列中等待发送的结果,然后返回结果。
Web Service 是接受应用发送信息的接口。为什么要这个呢?和接收一样,多数应用发送信息的时候是用HTTP的POST方法。而且这样应该不应该知道短信协议的数据包格式,这样同一个应该可以给不同的短信协议使用,比如中国电信的SMGP和中国移动的CMPP。
Application 就是最终处理短信的应用了。它和短信协议无关,比如我们有个游戏同时支持中国短信的小灵通和中国移动的GSM手机。另外给个Web的例子: http://www.hcmms.com.cn/greetings/
在实际实现中,我使用了J2EE。我本来对J2EE挺烦的,不过这次效果不错,特别是它的JMS,正好适合我的需要。开始我自己做消息队列管理,后来发现做Topic方式的实现比较麻烦,所以还是用现成的吧。Application Server选用了JRun或JBoss。因为这两个用JMX,我比较容易写自己的MBean放到作为应用服务的Service。
其
中Transport
Service就是一个JMX的MBean。另外根据JRun和JBoss的要求,做了一下扩展,很顺利的作为应用服务器中的一个服务执行了。为什么用
JMX?因为它比较容易管理,比如我要修改Transport
Service的连接超时参数,用HttpAgentAdapter直接修改就可以了,服务本身就不用重新启动。
Deliver Controller、Logger我都做成了MessageBean,好接收消息队列。Sender和Application Deliver是Stateless的SessionBean,方便别人调用。
用
了EJB还一个好处是,我Deploy和Undeploy其中的某些Bean的时候,不影响到其他Bean提供服务。比如新做了一个
Application Deliver,直接Deploy后,这个功能就可以用了。其他服务都不需要重新启动,有点Plug&Play的味道。
数据库我用的是MySQL,我个人还是比较喜欢这个数据库的。
Web CVS: http://cvs.dragonsoft.net/horde/chora/cvs.php/phs-smgp/
说明
实现
源代码
cvs -d:pserver:anoncvs@cvs.dragonsoft.net:/opt/cvsroot/dragonsoft/phs-smgp login
cvs -z3 -d:pserver:anoncvs@cvs.dragonsoft.net:/opt/cvsroot/dragonsoft/phs-smgp co phs-smgp