zoukankan      html  css  js  c++  java
  • quartz结合多线程处理后台业务

      最近项目中有播放视频的需求,技术选型采用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和多线程的简单用法,留个笔记,以便日后深究^_^

  • 相关阅读:
    自动换行的两种代码(C#)
    C#的SubString(int start,int end);
    php数组添加元素的方法
    php通过生成动态变量(变量名中还有变量)
    php定义空的数组
    php的$GLOBALS例子
    WINCRIS的使用
    java的PreparedStatement中使用like时的问题
    php发送get请求
    在HTML中使用object和embed标签插入视频
  • 原文地址:https://www.cnblogs.com/jpfss/p/9766831.html
Copyright © 2011-2022 走看看