最近项目中有播放视频的需求,技术选型采用UMS播放器,免费版只能播放FLV格式的视频文件,因此需要对用户上传的视频进行格式转换,转换工具为FormatFactory,功能还是比较强大的。但是面临的一个问题,视频转换是非常耗时的,上传完直接转换是没法接受的,于是决定采用quartz,以任务调度的方式,在后台进行转换,具体步骤如下:

  1.定义一个任务队列,将待转换的视频文件信息放到队列中。采用单例模式,并且考虑到线程安全问题,采用线程安全的Vector作为队列容器:

  

/**
* 格式转换任务队列
* 队列中放的是ResourceInfo类型对象
* @author Administrator
*
*/
public class TransformTaskQueue {
</span><span style="color: #0000ff;">private</span> <span style="color: #0000ff;">static</span> TransformTaskQueue instance = <span style="color: #0000ff;">null</span><span style="color: #000000;">;

</span><span style="color: #008000;">//</span><span style="color: #008000;">实际存放转换对象信息的队列,采用线程安全的Vercor容器</span>
<span style="color: #0000ff;">public</span> <span style="color: #0000ff;">static</span> Vector&lt;ResourceInfo&gt; taskQueue = <span style="color: #0000ff;">new</span> Vector&lt;ResourceInfo&gt;<span style="color: #000000;">(); </span><span style="color: #0000ff;">public</span> <span style="color: #0000ff;">static</span><span style="color: #000000;"> TransformTaskQueue getInstance() {
</span><span style="color: #0000ff;">if</span> (instance == <span style="color: #0000ff;">null</span><span style="color: #000000;">) {
instance </span>= <span style="color: #0000ff;">new</span><span style="color: #000000;"> TransformTaskQueue();
}
</span><span style="color: #0000ff;">return</span><span style="color: #000000;"> instance;
} </span><span style="color: #008000;">/**</span><span style="color: #008000;">
* 向队列中添加对象
* </span><span style="color: #808080;">@param</span><span style="color: #008000;"> info
</span><span style="color: #008000;">*/</span>
<span style="color: #0000ff;">public</span> <span style="color: #0000ff;">static</span> <span style="color: #0000ff;">void</span><span style="color: #000000;"> add(ResourceInfo info) {
taskQueue.add(info);
} </span><span style="color: #008000;">/**</span><span style="color: #008000;">
* 从队列中删除对象
* </span><span style="color: #808080;">@param</span><span style="color: #008000;"> info
</span><span style="color: #008000;">*/</span>
<span style="color: #0000ff;">public</span> <span style="color: #0000ff;">static</span> <span style="color: #0000ff;">void</span><span style="color: #000000;"> remove(ResourceInfo info) {
</span><span style="color: #0000ff;">if</span>(taskQueue.size()&gt;0 &amp;&amp;<span style="color: #000000;"> taskQueue.contains(info)){
taskQueue.remove(info);
}
}

}

  2.用户上传视频文件之后,后台进行判断,如果不是flv格式,则将文件转换所需信息封装到ResuorceInfo对象,将该对象放入待转换队列:

// 如果源视频文件存在,则进行相应的转换,转换为FLV文件
if (new File(TransConfig.VIDEO_SOURCE_ROOT + path + fileName).exists()) {
        ResourceInfo info </span>= <span style="color: #0000ff;">new</span><span style="color: #000000;"> ResourceInfo();
info.setResourceId(resourceId);
info.setPath(path);
info.setFileName(fileName);
info.setStatus(</span>0<span style="color: #000000;">);
</span><span style="color: #008000;">//</span><span style="color: #008000;"> 添加到转换队列</span>

TransformTaskQueue.add(info);

    } </span><span style="color: #0000ff;">else</span><span style="color: #000000;"> {
System.out.println(</span>"源文件不存在!"<span style="color: #000000;">);
}</span></pre>

  3.执行单个具体文件转换的操作类代码如下:

/**
* 执行具体转换操作的类,
* 采用多线程技术,继承了runnable接口
* @author Administrator
*
*/
public class TransformExecutor implements Runnable,Serializable{
</span><span style="color: #0000ff;">private</span> <span style="color: #0000ff;">static</span> <span style="color: #0000ff;">final</span> <span style="color: #0000ff;">long</span> serialVersionUID = 1L<span style="color: #000000;">;

</span><span style="color: #0000ff;">private</span> ResourceInfo info = <span style="color: #0000ff;">null</span><span style="color: #000000;"> ;

</span><span style="color: #0000ff;">public</span><span style="color: #000000;"> TransformExecutor(ResourceInfo info){
</span><span style="color: #0000ff;">this</span>.info =<span style="color: #000000;"> info;
} @Override
</span><span style="color: #0000ff;">public</span> <span style="color: #0000ff;">void</span><span style="color: #000000;"> run() { String resourceId </span>=<span style="color: #000000;"> info.getResourceId();
String path </span>=<span style="color: #000000;"> info.getPath();
String fileName </span>=<span style="color: #000000;"> info.getFileName(); String videoFilename </span>= TransConfig.VIDEO_SOURCE_ROOT +<span style="color: #000000;"> path
</span>+<span style="color: #000000;"> fileName;
String flvFilename </span>=<span style="color: #000000;"> path
</span>+ FileUtil.getFilePrefix(fileName) + ".flv"<span style="color: #000000;">; </span><span style="color: #008000;">//</span><span style="color: #008000;"> 转换成功,修改数据库中的is_transed字段为1</span>
<span style="color: #0000ff;">if</span> (Video2FLVTransfer.transform(videoFilename, flvFilename) == 1<span style="color: #000000;">) {
CRUDUtil.update(resourceId, </span>1<span style="color: #000000;">);
}
</span><span style="color: #008000;">//</span><span style="color: #008000;"> 转换失败,修改数据库中的is_transed字段为2</span>
<span style="color: #0000ff;">else</span><span style="color: #000000;"> {
CRUDUtil.update(resourceId, </span>2<span style="color: #000000;">);
} </span><span style="color: #008000;">//</span><span style="color: #008000;"> 将resourceInfo从转换队列中去除</span>

TransformTaskQueue.remove(info);

}

}

  4.下面是开启多线程转换的操作类,采用线程池技术,因为转换视频文件格式工作量比较大,因此规定每次最多开启3个线程:

/**
* 转换执行器服务类
* @author Administrator
*
*/
public class TransExecutorService {
</span><span style="color: #0000ff;">private</span> <span style="color: #0000ff;">final</span><span style="color: #000000;"> ExecutorService pool;

</span><span style="color: #0000ff;">private</span> <span style="color: #0000ff;">static</span><span style="color: #000000;"> TransExecutorService instance;
</span><span style="color: #008000;">//</span><span style="color: #008000;">线程池大小,即每次最多允许开启几个线程执行转换操作</span>
<span style="color: #0000ff;">private</span> <span style="color: #0000ff;">static</span> <span style="color: #0000ff;">final</span> <span style="color: #0000ff;">int</span> THREAD_SIZE = 3<span style="color: #000000;">; </span><span style="color: #0000ff;">public</span> <span style="color: #0000ff;">static</span><span style="color: #000000;"> TransExecutorService getInstance() {
</span><span style="color: #0000ff;">if</span> (instance == <span style="color: #0000ff;">null</span><span style="color: #000000;">) {
instance </span>= <span style="color: #0000ff;">new</span><span style="color: #000000;"> TransExecutorService();
}
</span><span style="color: #0000ff;">return</span><span style="color: #000000;"> instance;
} </span><span style="color: #0000ff;">private</span><span style="color: #000000;"> TransExecutorService() {

// pool = Executors.newCachedThreadPool();

pool = Executors.newFixedThreadPool(THREAD_SIZE);

}

</span><span style="color: #008000;">/**</span><span style="color: #008000;">
* 开启新线程,执行转换操作
* </span><span style="color: #808080;">@param</span><span style="color: #008000;"> info
</span><span style="color: #008000;">*/</span>
<span style="color: #0000ff;">public</span> <span style="color: #0000ff;">void</span><span style="color: #000000;"> execute(ResourceInfo info) {
</span><span style="color: #0000ff;">try</span><span style="color: #000000;"> {
pool.submit(</span><span style="color: #0000ff;">new</span><span style="color: #000000;"> TransformExecutor(info));
} </span><span style="color: #0000ff;">catch</span><span style="color: #000000;"> (Exception e) {
e.printStackTrace();
}
} </span><span style="color: #0000ff;">public</span> <span style="color: #0000ff;">synchronized</span> <span style="color: #0000ff;">void</span><span style="color: #000000;"> shutdown() {
pool.shutdown();
}

}

  5.调度任务实现类,即每次执行调度,执行的操作

/**
* 调度任务具体执行类
* @author Administrator
*
*/
public class TransformJob implements Job {
@Override
</span><span style="color: #0000ff;">public</span> <span style="color: #0000ff;">void</span> execute(JobExecutionContext ctx) <span style="color: #0000ff;">throws</span><span style="color: #000000;"> JobExecutionException { </span><span style="color: #008000;">//</span><span style="color: #008000;">获取当前待转换视频文件队列</span>
Vector&lt;ResourceInfo&gt; infos =<span style="color: #000000;"> TransformTaskQueue.getInstance().taskQueue;
System.out.println(</span>"size:"+<span style="color: #000000;">infos.size()); </span><span style="color: #008000;">//</span><span style="color: #008000;">如果任务队列中存在待转换对象,则进行转换</span>
<span style="color: #0000ff;">if</span> (infos.size() &gt; 0<span style="color: #000000;">) {
</span><span style="color: #0000ff;">for</span> (<span style="color: #0000ff;">int</span> i=0;i&lt;infos.size();i++<span style="color: #000000;">) {
</span><span style="color: #008000;">//</span><span style="color: #008000;">status为0,表示不是正在转换中的</span>
<span style="color: #0000ff;">if</span> (infos.get(i).getStatus() == 0<span style="color: #000000;">) {
infos.get(i).setStatus(</span>1<span style="color: #000000;">);
</span><span style="color: #008000;">//</span><span style="color: #008000;">新开线程,执行转换操作</span>

TransExecutorService.getInstance().execute(infos.get(i));

}

}

}

}

}

  6.任务调度管理类,规定了调度执行的一些规则,其中定时表达式请自行网上搜索,这里采用的是每10秒执行一次。

/**
* 格式转换任务调度管理类
*
* @author Administrator
*
*/
public class SchedulManager {
</span><span style="color: #0000ff;">private</span> <span style="color: #0000ff;">static</span> SchedulManager instance = <span style="color: #0000ff;">new</span><span style="color: #000000;"> SchedulManager();
</span><span style="color: #0000ff;">private</span><span style="color: #000000;"> Scheduler scheduler;
</span><span style="color: #0000ff;">private</span> <span style="color: #0000ff;">volatile</span> <span style="color: #0000ff;">boolean</span> start = <span style="color: #0000ff;">false</span><span style="color: #000000;">; </span><span style="color: #0000ff;">private</span><span style="color: #000000;"> SchedulManager() {
</span><span style="color: #0000ff;">try</span><span style="color: #000000;"> {
SchedulerFactory factory </span>= <span style="color: #0000ff;">new</span><span style="color: #000000;"> StdSchedulerFactory();
scheduler </span>=<span style="color: #000000;"> factory.getScheduler();
} </span><span style="color: #0000ff;">catch</span><span style="color: #000000;"> (SchedulerException e) {
e.printStackTrace();
}
} </span><span style="color: #0000ff;">public</span> <span style="color: #0000ff;">static</span><span style="color: #000000;"> SchedulManager getInstance() {
</span><span style="color: #0000ff;">return</span><span style="color: #000000;"> instance;
} </span><span style="color: #008000;">/**</span><span style="color: #008000;">
* 开始执行,将加载调度配置并启动每个调度。
*
* @注意: 一般在程序启动时调用该方法。
</span><span style="color: #008000;">*/</span>
<span style="color: #0000ff;">public</span> <span style="color: #0000ff;">void</span><span style="color: #000000;"> execute() {
</span><span style="color: #0000ff;">try</span><span style="color: #000000;"> {
</span><span style="color: #008000;">//</span><span style="color: #008000;"> 加载调度配置,并启动每个调度。</span>

scheduleJobs();

scheduler.start();

} catch (Exception e) {

e.printStackTrace();

}

}

</span><span style="color: #008000;">/**</span><span style="color: #008000;">
* 加载调度配置并启动每个调度
*
* @注意: TODO
</span><span style="color: #008000;">*/</span><span style="color: #000000;">
@SuppressWarnings(</span>"static-access"<span style="color: #000000;">)
</span><span style="color: #0000ff;">private</span> <span style="color: #0000ff;">void</span><span style="color: #000000;"> scheduleJobs() { Vector</span>&lt;ResourceInfo&gt; infos =<span style="color: #000000;"> TransformTaskQueue.getInstance().taskQueue;
System.out.println(</span>"size:" +<span style="color: #000000;"> infos.size());
</span><span style="color: #0000ff;">if</span> (infos.size() &gt; 0<span style="color: #000000;">) {
start();
}
start </span>= <span style="color: #0000ff;">true</span><span style="color: #000000;">;
} </span><span style="color: #008000;">/**</span><span style="color: #008000;">
* 根据ResourceInfo启动一个调度
*
* @注意: 内部方法,外部不能调用
* </span><span style="color: #808080;">@param</span><span style="color: #008000;"> ResourceInfo
* 资源信息
</span><span style="color: #008000;">*/</span>
<span style="color: #0000ff;">private</span> <span style="color: #0000ff;">void</span><span style="color: #000000;"> start() { </span><span style="color: #0000ff;">try</span><span style="color: #000000;"> {
</span><span style="color: #008000;">//</span><span style="color: #008000;"> String id = info.getResourceId();
</span><span style="color: #008000;">//</span><span style="color: #008000;"> 构造方法中 第一个是任务名称 ,第二个是任务组名,第三个是任务执行的类</span>
JobDetail jobDetail = <span style="color: #0000ff;">new</span> JobDetail("video_trans_id"<span style="color: #000000;">,
Scheduler.DEFAULT_GROUP, TransformJob.</span><span style="color: #0000ff;">class</span><span style="color: #000000;">);
String cronExpr </span>= "0/10 * * * * ?"<span style="color: #000000;">;
String triggerName </span>= "video_trans_trigger"<span style="color: #000000;">;
Trigger trigger </span>= <span style="color: #0000ff;">new</span><span style="color: #000000;"> CronTrigger(triggerName,
Scheduler.DEFAULT_GROUP, cronExpr); scheduler.scheduleJob(jobDetail, trigger); } </span><span style="color: #0000ff;">catch</span><span style="color: #000000;"> (Exception e) {
System.out.println(</span>"出错"<span style="color: #000000;">);
e.printStackTrace();
}
} </span><span style="color: #0000ff;">public</span> <span style="color: #0000ff;">void</span><span style="color: #000000;"> init() { SchedulManager sm </span>=<span style="color: #000000;"> SchedulManager.getInstance();
</span><span style="color: #008000;">//</span><span style="color: #008000;"> sm.start(info);
</span><span style="color: #008000;">//</span><span style="color: #008000;"> sm.scheduleJobs();</span>

sm.start();

try {

sm.scheduler.start();

} catch (SchedulerException e) {

// TODO 自动生成的 catch 块

e.printStackTrace();

}

}

  7.通过上述6个步骤,已经可以通过quartz以任务调度的形式来进行格式转换了,接下来的问题,是写一个listener类,以实现在服务器启动的时候,任务调度自动启动。

首先需要在web.xml中加入如下配置:

    <listener>
<listener-class>com.yunda.web.EventTransformStartupListener</listener-class>
</listener>

之后就是实现配置文件中的实现监听功能的类,非常简单,就是调用SchedulManager中的init()方法即可,代码如下:

public class EventTransformStartupListener implements ServletContextListener {
@Override
</span><span style="color: #0000ff;">public</span> <span style="color: #0000ff;">void</span><span style="color: #000000;"> contextDestroyed(ServletContextEvent arg0) {
} @Override
</span><span style="color: #0000ff;">public</span> <span style="color: #0000ff;">void</span><span style="color: #000000;"> contextInitialized(ServletContextEvent arg0) { System.out.println(</span>"init..."<span style="color: #000000;">); SchedulManager sm </span>=<span style="color: #000000;"> SchedulManager.getInstance();
</span><span style="color: #008000;">//</span><span style="color: #008000;">sm.start(info);</span>

sm.init();

}

}

  至此,后台进行格式转换的功能全部完成,通过做这个功能,发现quartz采用的任务调度机制,跟linux的crontab差不多,也是采用定时扫描的方法来完成,连定时表达式的规则都长的差不多。先写这么多吧,就是学习了quartz和多线程的简单用法,留个笔记,以便日后深究^_^

quartz结合多线程处理后台业务的更多相关文章

  1. .netcore+vue+elementUI 前后端分离---支持前端、后台业务代码扩展的快速开发框架

    框架采用.NetCore + Vue前后端分离,并且支持前端.后台代码业务动态扩展,框架内置了一套有着20多种属性配置的代码生成器,可灵活配置生成的代码,代码生成器界面配置完成即可生成单表(主表)的增 ...

  2. 在后台业务管理系统中使用Autofac实现微信接口的处理

    在后台业务管理系统中使用Autofac实现微信接口的处理,我们只需要把相关使用到的DLL放到BIN目录里面即可,通过IOC控制反转方式实现对接口的调用.在实现在业务系统里面,我们本身程序可能已经依赖了 ...

  3. Quartz.Net 作业调度后台管理系统,基于Extjs

    Quartz.Net是一个开源的.非常灵活的作业调度框架,具体使用方法和教程:http://www.cnblogs.com/shanyou/archive/2007/08/25/quartznettu ...

  4. EEPlat 的 后台业务处理模型

    后台处理包括数据处理.业务逻辑及业务流程等服务端操作的部分.相关的元模型包括业务对象元模型.业务对象属性元模型.服务元模型.參数元模型.业务规则元模型.工作流元模型.例如以下图所看到的:   业务对象 ...

  5. 使用node来搭建简单的后台业务

    现在作为一个前端开发人员,越来越多的技术需要学习,近几天学习了下node.js,在很多前端以及后端应用了该技术,现在记录下自己摸索的一些简单的知识记录下来. 我的博客都是直接分享应用方法,没有说明一些 ...

  6. 【Yii系列】最佳实践之后台业务框架

    缘起 上面的几章都讲概念了,没有怎么讲到实践的东西,可能会有些枯燥,这很正常的,概念还是需要慢慢啃的,尤其是官网其他的部分,需要狠狠的啃. 什么,你啃不动了?看看官网旁边的那个在线用户吧. 你不啃的时 ...

  7. Spring+Quartz实现定时任务的配置方法

    1.Scheduler的配置 <bean class="org.springframework.scheduling.quartz.SchedulerFactoryBean" ...

  8. Spring+Quartz 实现定时任务的配置方法

    Spring+Quartz 实现定时任务的配置方法 整体介绍 一.Quartz介绍 在企业应用中,我们经常会碰到时间任务调度的需求,比如每天凌晨生成前天报表,每小时生成一次汇总数据等等.Quartz是 ...

  9. RESTful API后台系统架构设计(Java)

    最近设计和实现了一个JAVA的RESTful API的后台业务系统架构,主要基于Java平台.设计要求是: 性能:平均响应时间(RESTful API)小于2s(平均负载的情况下),并发访问200个以 ...

随机推荐

  1. 未能加载文件或程序集“System.Collections.Concurrent”或它的某一个依赖项。

    未能加载文件或程序集“XXXXXX”或它的某一个依赖项.试图加载格式不正确的程序.   在本机WIN7机器上的WebService部署到Win2008R2上发现错误 “/”应用程序中的服务器错误. 未 ...

  2. Altium Designer线如何跟着原件走

  3. Surging Demo 项目之一

    原文:Surging Demo 项目之一 开发与运行环境 IDE Visual Stadio 2017/Visual Stadio 2019 Visual Stadio Core Docker 和 D ...

  4. STL algorithm算法mov,move_backward(38)

    move原型: std::move template <class InputIterator, class OutputIterator> OutputIterator move (In ...

  5. js进阶 12-15 jquery如何实现点击button显示列表,点击其它位置隐藏列表

    js进阶 12-15 jquery如何实现点击button显示列表,点击其它位置隐藏列表 一.总结 一句话总结:在button中阻止事件冒泡. 1.如何咋button中阻止事件冒泡(两种方法)? ev ...

  6. matlab无法使用

    mathlab前一段时间使用不了,网上找到了一些出来方法. (1)修改本地时间,将本地时间改都较前的时间,就可以了, (2)使用网上的一个文件,将自己的文件替换掉就可以了 文件放在我的百度云盘里面ht ...

  7. 【Codeforces Round #437 (Div. 2) C】 Ordering Pizza

    [链接]h在这里写链接 [题意]     给你参赛者的数量以及一个整数S表示每块披萨的片数.     每个参数者有3个参数,si,ai,bi;     表示第i个参赛者它要吃的披萨的片数,以及吃一片第 ...

  8. 20、在PC上测试虚拟驱动vivi

    在Ubuntu上测试 准备工作:安装xawtv(是一个应用程序,用来在Ubuntu上捕获摄像头数据并显示)sudo apt-get install xawtv 源码xawtv-3.95.tar.gz: ...

  9. 前端css常用的选择小汇

    要使用css对HTML页面中的元素实现一对一,一对多或者多对一的控制,这就需要用到CSS选择器.选择器就是选择器用来指定样式的作用范围. 类选择器: 类选择器在css中比较常见,首先要在普通标签中设置 ...

  10. Codeforces 467C. George and Job

    DP.... C. George and Job time limit per test 1 second memory limit per test 256 megabytes input stan ...