在Web应用中,某些功能的实现逻辑很复杂、执行比较耗时[1],例如涉及外部系统调用、多数据源等;此时,希望可以让这些复杂的业务逻辑放在后台执行,而前台与用户的交互可以不用等待,从而提高用户体验;或者需要以一定时间间隔重复运行任务、或在每天的指定时间运行任务的情况。为此,需要控制大型任务对服务器资源的消耗,降低Web服务器的并发连接数目,这就需要将大型任务的提交和执行分开,使服务器接受任务后立即断开与客户端的连接,减少服务器的并发连接数,而任务则推迟到服务器资源许可时执行,以抑制服务器资源的峰值消耗。
为尽量减少耗时操作对执行的影响,本文提出了异步任务的处理,使用多线程来管理耗时任务,作为后台进程执行;同时把任务信息都持久化在数据库中,保证了异步任务处理的灵活性、可靠性。
1 多线程
1.1线程池
一个线程是程序中的一条执行流,是操作系统分配处理器的基本单位。并发是程序中多条执行流的同时推进,多任务并发对应多线程并发[2]。
但是为每个任务创建一个线程,当任务完成时撤消对应的线程存在明显的缺陷。线程的创建需要一定的时间,给任务请求的响应带来延迟,线程的创建和撤消也给操作系统带来额外的管理负担,若频繁“创建和撤消”,则将明显增加系统的额外开销。为有效降低线程重复创建和撤销方面的开支可以采用线程池技术。
线程池技术提供了一种较好的解决方案[3]:系统维护由若干个线程组成的线程池。当有任务请求到达时,由池中的一个线程为之运行,在任务完成后不是将该线程撤消而是将其归还线程池,使之能够为后续到达的任务服务;若线程池中没有空闲的线程,则任务进入等待状态直到有空闲的线程。
1.2 Java中的线程池实现机制
Java在语言级实现了功能丰富的多线程编程机制[4],对线程池的建立和维护提供了强大的支持。特别在JDK1.5及以后的版本中,任务执行抽象的首选不再是Thread,而是Executor。Executor虽是一个简单的接口,但它提供了异步任务执行框架并支持多种不同类型的任务执行策略,ExecutorService接口和ScheduledExecutorService接口对Executor进行了扩展,添加了管理线程执行和调度线程池的若干方法。通过Executors工具类提供的静态工厂方法可以创建符合特定需求的基于线程池执行框架。
newChachedThreadPool()方法用于创建可缓存线程池的执行框架。当新的请求任务到达时,执行框架将尽可能地重用池中的空闲线程,若此时池中没有空闲线程,则添加新线程,这个方法对池的大小没有限制。另一方面,该执行框架能够自动回收空闲时间超过60 s的线程,以合理使用系统资源。对于执行大量短异步任务的程序而言,这种方式的线程池通常可提高性能。
newFixedThreadPool(int nThreads)方法建立的执行框架中的线程池具有固定数量的线程。每提交一个任务它就创建一个线程,直到达到池的限定值nThreads,线程池的长度不再变化,新到达的任务在一个遵循先来先服务(FIFS)规则的无界队列中等待执行。
newScheduledThreadPool(int nThreads)方法建立的执行框架中的线程池也是定长的,它支持定时的以及周期性的任务的执行。
这些工厂方法返回的Executor 都是ThreadPoolExecutor()类的常用实例,能满足大部分线程池的应用需求。
2 设计思路
为保证异步任务处理的灵活性和可靠性,本文设计的思路为:任务持久化+Java线程池+任务调度。
2.1 任务持久化
将待处理的任务信息保存在可信任的数据库中,同时要确保当任务处理服务器出问题后这些未执行成功、或未开始执行的任务不会被丢失。
2.2 任务调度
当任务信息都持久化在数据库中之后,需要将这些信息读取出来执行具体的业务逻辑操作,本文通过ScheduledExecutorService来实现对任务的循环调度,例如可采取每隔2 min扫描一次待处理任务列表,若有记录则提取出来执行。
3 具体实现
异步任务处理中各组成部分在运行过程中的调用关系如图1。
当客户端访问服务器时,有耗时操作的任务,则把该任务放入数据库中。服务器每隔一段时间轮询存放待处理任务的表,若表中有任务,则任务调度线程池采用多线程机制来执行该任务。任务执行成功后,删除待处理任务表中的该任务信息,否则把该任务信息更新到任务失败表,进行人工干预。
3.1 任务数据表
建两张表,一张task表,用来存放待处理的任务;一张task_fail表用来存放失败的任务。两张表的结构一样,结构如表1所示。
task表主要用来保存所有待处理的任务,每条任务信息属于一种任务类型,由task_handle字段标识,任务类型值为该类型任务的具体实现类名。task_params 字段提供了执行该任务需要的所有参数,为字符串,需要在具体任务实现类中解析。handle_time字段提供了任务待执行的日期。
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉