以前在iteye上的一个帖子,讨论Web应用中开线程池做报表处理的一些思路
[ 需求]
甲方是个跨国500强企业,它要建立一套全球整合的CRM管理系统。
其中有一个功能是在Web上面打印报表。但是因为报表业务逻辑复杂,数据量大,在web上点击同步生成用户体验太差。所以决定采取异步的做法生成报表。
[ 甲方的架构师提出的设计]
甲方的架构师提出的思路是,
1. 定制servlet,在servlet的init方法里,初期化一个线程池。将线程池包装到一个ThreadController 单例里面去
2. 客户点击打印报表按钮,发送出一个http请求到服务器端
3. 服务器端将生成报表所需要的参数包装到一个Listener中,然后将Listener 的instance抛给ThreadController单例,在ThreadController中以Queue的模式保存。
4. ThreadController依次唤醒线程池中的线程,从Queue中取出Listener交给该线程执行打印报表。如遇线程池爆满,则在Queue中排队。
5. http响应返回成功给画面
6. 线程执行完毕以后,在DB中写入打印结果
7. 客户端用Ajax轮训WebService,察看打印结果,达到推送功能。
甲方的架构我觉得问题有。在Web应用的容器中开线程池,性能和安全性得不到保证。以后系统的横扩展也会困难。
[ 我提出的设计]
1. 专门搭建一个报表打印服务器
2. Web服务器接受到打印请求以后,发送一个JMS到打印服务器去
3. 打印服务器那边,接受JMS,排队,然后启动线程执行打印功能。
4. 打印服务器完毕以后,写DB
遗留的问题,
a. 用JMS通信好不好?据说很重。但是用http的话,又怕丢包
b. 这样设计,貌似横扩展比较困难。但是因为打印报表本身这个动作是有状态的,以后估计也只能上机器
一些经典的讨论和回帖
no1dog 写道 能说下不安全的理由是什么吗?性能问题又是什么? 我回答 |
某位大牛说 比如:实时性要求不高;要求能承受多大报表量,报表生成时间等等 建议不在web应用中做,分离出来一个单独的报表程序,单独运行,两系统间通过jms通信 如果放在web里的话,如果web程序挂掉了,那报表的模块不也就挂掉了。 也可用第三方成熟的报表程序还做。 缓存之类的也可以考虑。
|
rainmanyang 写道 对于这种前台Web压力比较大, 而又要实现这种实时性要求不高的处理负荷又重的任务, 不要在Web中开线程做. 用消息中间件通知其他程序来处理是一个比较好的架构, 各自完成各自的任务, 而且将来也便于扩展. |
某位大牛说到 甲方架构师的思路可能偏重于实现的策略,没有关注系统的整体规划和部署,实现有点复杂,可用性不强。 如果报表的可靠性要求很高,注意保证系统间通信消息的持久化就可以了,选择消息中间件也是个不错的办法,简单快捷。 |
vlinux 写道 从维护成本来看,用JMS通讯会存在消息延时甚至丢失的可能,JMS也是一台服务器啊~,然后再加上你的打印服务器。要知道这个瓶颈是在DB的CPU/MEM/IO上,有机器多拖几个读库或将DB的钱换成Hadoop之类的还差不多。可以想象这个打印服务器除了来了一个请求之后就开个线程在等待DB返回了,压力非常低。
JMS一台服务器、打印服务一台服务器,主备各一台,一共4台。 JMS的维护成本、联调成本,后期问题排查成本都得考虑进去。
作为后台系统,WEB层从来就不是瓶颈,如果不要求非常可靠,用甲方架构师的方案会更优。不过要集中精力解决下用户等得不耐烦,重复提交/F5、任务执行超时等问题。这些问题就算你做到打印服务器上,也是要考虑和解决的。 |