Tomcat启动完毕后启动方法任务

        Tomcat启动完成后再执行一个指定的方法,不影响Tomcat的启动时间。

        本文主要介绍Tomcat启动真正完成后(即在eclipse的控制台上出现类似于Server started in 2300ms这样的消息后)执行一个操作。 

        如下的3种方法都是在Tomcat启动过程中执行的,这样会影响Tomcat的启动时间,从而造成Tomcat不能启动成功:  

        1).配置一个Servlet默认自动启动。  

        2).配置一个Listener来启动  

        3).实现Spring的InitializingBean接口  

        要想不影响Tomcat的启动,便联想到了异步调用 。即无非就是新创建了一个线程来单独执行,这样Tomcat执行到相应的操作就可以直接继续下去了,不会处于等待的状态,避免了启动超时。基于这样的思想,可以有两种方法来完成:

方法一:

        使用如上三种方式中的任何一种来在启动Tomcat的过程中执行相应的方法,然后在执行的过程中使用另一个线程来执行:比如说将要执行的方法所在的类继承HttpServlet并在web.xml中配置,然后在该Servlet的init中去调用想要执行的方法时(假设这个方法名叫start()),启动另一个线程来执行,具体代码如下。

package com.bijian.web.video;


import static com.bijian.util.RedisUtils.getPool;

import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.FutureTask;

import javax.annotation.Resource;

import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Controller;

import redis.clients.jedis.Jedis;
import ayou.util.DOC;
import ayou.util.StringUtil;

import com.cc.ovp.dao.M3u8ContentDAO;
import com.cc.ovp.dao.M3u8_db;
import com.cc.ovp.domain.M3u8Content;
import com.cc.ovp.service.MongoSearchService;
import com.cc.ovp.util.JsonUtil;
import com.cc.ovp.util.Pager;


/**
 * 定时清理一下videojson 的redis缓存
 * 需求:
 * 查询m3u8_content的status字段不为8,
 * 就清除redis 缓存,重新set值到redis,
 * cleanRedisVideoJSon(String vid),
 * 更新m3u8_content的status 字段为8
 */
@Controller
public class CleanVideoJsonRedis extends HttpServlet{
    
    private static final Logger logger = LoggerFactory.getLogger(CleanVideoJsonRedis.class);
    
    private final int sleepTime=5*60*1000;
    
    @Resource(name = "m3u8ContentDAO")
    private M3u8ContentDAO m3u8ContentDAO;
    
    @Resource(name="videoJson")
    private VideoJson videoJson;
    
    
    // Servlet的init方法会在Tomcat启动的时候执行
    @Override
    public void init() throws ServletException {
      FutureTask<String> task = new FutureTask<String>(new Callable<String>(){
           @Override
           public String call() throws Exception {
               taskstart(); //使用另一个线程来执行该方法,会避免占用Tomcat的启动时间
              return "Collection Completed";
           }
      });
      
      new Thread(task).start();;
    }

    
    public void taskstart(){
        while (true) {
            try {
                System.out.println("init");
                System.out.println("==============================");
                Thread.sleep(5*1000);
                Pager pager = new Pager(1, 200);
              pager.setSortname("dateAdded");
              pager.setSortorder("desc");
              List<M3u8Content> list = m3u8ContentDAO.findList(pager);
              System.out.println("=============================="+list.size());
                logger.info("tomcat 测试方法");
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            
        }
    }
}

        web.xml配置文件增加一个serlvet。

<servlet>
	<servlet-name>event-collector</servlet-name>
	<servlet-class>com.bijian.web.video.CleanVideoJsonRedis</servlet-class>
	<load-on-startup>60</load-on-startup>
</servlet>

<servlet-mapping>
	<servlet-name>event-collector</servlet-name>
	<url-pattern>/event-collect</url-pattern>
</servlet-mapping>

方法二:

        配置一个Listener来启动。

web.xml

<!-- Spring 上下文监听器 由此启动Spring上下文容器 -->
<listener>
	<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<!--  增加监听启动类的listener -->
<listener>
	<listener-class>com.bijian.web.video.CleanVideoJsonRedis</listener-class>
</listener>

        注意:<listener/> 标签一定要在<filter/>之后,在<servlet/>之前配置。

CleanVideoJsonRedis.java

package com.baijian.web.video;
import javax.servlet.*;
import java.util.*;
public class CleanVideoJsonRedis implements ServletContextListener{

public CleanVideoJsonRedis(){
   System.out.println("调用了构造方法");
}
public void contextInitialized(ServletContextEvent event) {
   System.out.println(" ----------创建了Context created on " +
   new Date() + ".");
   }
   public void contextDestroyed(ServletContextEvent event) {
   System.out.println("--------销毁了Context destroyed on " +
   new Date() + ".");
   }
}

        说明:listener 配置在web.xml中,当web服务启动时,会实例化<listener-class/>中指定的类,所以里面一定要写完整类路径。

方法三:

        使用Spring的Timer或者是著名的Quartz在Tomcat启动后再执行该方法,Spring中的Timer非常简单,这个地方不想讲解了,Quartz相对更复杂些,下面主要介绍下在Spring中怎么样使用Quartz来实现上面的需求。

例子:

package com.bijian.web.video;

import static com.cc.ovp.util.RedisUtils.getPool;

import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.FutureTask;

import javax.annotation.Resource;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;

import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Controller;

import redis.clients.jedis.Jedis;
import ayou.util.DOC;
import ayou.util.StringUtil;

import com.cc.ovp.dao.M3u8ContentDAO;
import com.cc.ovp.dao.M3u8_db;
import com.cc.ovp.domain.M3u8Content;
import com.cc.ovp.service.MongoSearchService;
import com.cc.ovp.util.JsonUtil;
import com.cc.ovp.util.Pager;


/**
 * 定时清理一下videojson 的redis缓存
 * 需求:
 * 查询m3u8_content的status字段不为8,
 * 就清除redis 缓存,重新set值到redis,
 * cleanRedisVideoJSon(String vid),
 * 更新m3u8_content的status 字段为8
 */
@Controller
public class CleanVideoJsonRedis implements Job{
    
    private static final Logger logger = LoggerFactory.getLogger(CleanVideoJsonRedis.class);
    
    private final int sleepTime=5*60*1000;
    
    @Resource(name = "m3u8ContentDAO")
    private M3u8ContentDAO m3u8ContentDAO;
    
    @Resource(name="videoJson")
    private VideoJson videoJson;
    
    
    @Override  
    public void execute(JobExecutionContext arg0) throws JobExecutionException {  
        // TODO Auto-generated method stub  
    }  
      
    public void executeA() throws JobExecutionException {  
        // TODO Auto-generated method stub  
        logger.info("==========clean redis videojson");
        //cleanRedis();
    }      
    
    /**
     * 每隔5分钟,执行一次 时间,
     * 在spring配置文件设置 dispatcher-servlet.xml
     */
    public void cleanRedis(){
           Pager pager = new Pager(1, 2000);
           pager.setSortname("dateAdded");
           pager.setSortorder("desc");
           List<M3u8Content> list = m3u8ContentDAO.findList(pager);
           if(list.size()>0){
               for(int i=0;i<list.size();i++){
                   M3u8Content m3u8content = new M3u8Content();
                   cleanRedisVideoJSon(m3u8content.getVideoPoolId());//覆盖redis.videojson缓存
                   logger.info("定时程序, 覆盖redis.videojson缓存");
                   //更改状态
                   m3u8content.setStatus(8);
                   m3u8ContentDAO.updateStatus(m3u8content);
                   logger.info("定时程序, 修改m3u8_content的status的值为8");
               }
           }
    }
    
    /**
     * 定时清除videoJson 的redis缓存
     */
    public void cleanRedisVideoJSon(String vid){
            Jedis jedis = null;
            //String userid = vid.substring(0,10);
            //String jsonbody = JsonUtil.docToJson(vdoc);
            jedis = getPool().getResource();
            String videokey = "videojson_"+vid;
            //String userkey = "userjson_"+userid;
            String videojson = jedis.get(videokey);
            if(!StringUtil.isFine(videojson)){
                DOC vdoc = videoJson.videoJson(vid);
                if (vdoc!=null && vdoc.size()!=0){
                    videojson = JsonUtil.docToJson(vdoc);
                    //预防缓存了空json
                    if(videojson.length()>10){
                        jedis.set(videokey, videojson);
                    }
                }
            }
    }
    
    /**
     * 测试方法
     */
    public void taskstart(){
        System.out.println("init");
        Pager pager = new Pager(1, 1000);
//      pager.setSortname("dateAdded");
//      pager.setSortorder("desc");
          logger.info("================s==============");
          List<M3u8Content> list = m3u8ContentDAO.findList(pager);
          logger.info("==================s============"+list.size());
          logger.info("==================u============");
          
          cleanRedisVideoJSon("fffdd2a8ecfdddfa9aee376070e1c759");//覆盖redis.videojson缓存
          M3u8Content m3u8content = new M3u8Content();
          m3u8content.setBitRateIndex(1);
          m3u8content.setVideoPoolId("fffdd2a8ecfdddfa9aee376070e1c759");
          m3u8content.setStatus(8);
          m3u8ContentDAO.updateStatus(m3u8content);
          logger.info("==================u============");
           logger.info("tomcat 测试方法");
    }
}

        spring 定时任务调度设置:(在spring配置文件增加如下配置)

<!-- 配置 定时任务 videoJson.redisbegin Begin  -->
<bean id="initJob" class="com.bijian.web.video.CleanVideoJsonRedis" />  
<!--定时器任务配置(开始)-->       
<!--配置JOB-->  
<bean id="initJobDetail"  
	class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean">  
	<property name="targetObject" ref="initJob" />  
	<property name="targetMethod" value="executeA" />  
	<!--   <property name="arguments" /> -->  
</bean>  
<!--配置Trigger-->  
<bean id="initTrigger"    
	class="org.springframework.scheduling.quartz.SimpleTriggerBean">    
	<property name="jobDetail" ref="initJobDetail" />    
	<property name="startDelay" value="10000" />  
	<property name="repeatInterval" value="300000" />  
   <!-- <property name="repeatCount" value="0" />   --> 
</bean>  
<!--配置Scheduler-->  
<bean id="schedulerFactory"    
	class="org.springframework.scheduling.quartz.SchedulerFactoryBean" autowire="no">    
	<property name="triggers">    
		<list>    
			<ref bean="initTrigger" />    
		</list>    
	</property>    
	<property name="autoStartup" value="true"/>  
</bean>  
<!--定时器任务配置(结束)--> 
<!--  配置 定时任务 videoJson.redisbegin end -->

并发执行设置

        是设置执行模式的,大概意思是:比如您设置了5分钟,可以在该任务执行之后的5分钟后继续执行下一次,就是上一次任务必须执行完毕之后执行下一次。还有就是无论上次任务有没有执行完毕,那么过五分钟之后继续执行下次任务。<property name="concurrent" value="false" />

<!-- 配置 定时任务 videoJson.redisbegin Begin  -->
<bean id="initJob" class="com.bijian.web.video.CleanVideoJsonRedis" />  
<!--定时器任务配置(开始)-->       
<!--配置JOB-->  
<bean id="initJobDetail"  
	class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean">  
	<property name="targetObject" ref="initJob" />  
	<property name="targetMethod" value="executeA" />
	<property name="concurrent" value="false" />  <!-- 不允许并发  -->
	<!--   <property name="arguments" /> -->  
</bean>  
<!--配置Trigger-->  
<bean id="initTrigger"    
	class="org.springframework.scheduling.quartz.SimpleTriggerBean">    
	<property name="jobDetail" ref="initJobDetail" />    
	<property name="startDelay" value="10000" />  
	<property name="repeatInterval" value="1000" /> 
</bean>  
<!--配置Scheduler-->  
<bean id="schedulerFactory"    
	class="org.springframework.scheduling.quartz.SchedulerFactoryBean" autowire="no">    
	<property name="triggers">    
		<list>    
			<ref bean="initTrigger" />    
		</list>    
	</property>    
	<property name="autoStartup" value="true"/>  
</bean>  
<!--定时器任务配置(结束)--> 
<!--  配置 定时任务 videoJson.redisbegin end -->

        或者使用Java的线程池实现:

public void execute() throws InterruptedException {  
    System.out.println("Start job");  
    ExecutorService exec = Executors.newFixedThreadPool(1);  
      
    Thread thread = new Thread(new Runnable() {  
        @Override  
        public void run() {  
            System.out.println("thread start");  
            try {  
                Thread.sleep(3000);  
            } catch (InterruptedException e) {  
                // TODO Auto-generated catch block  
                e.printStackTrace();  
            }  
            System.out.println("thread end");  
        }  
    });  
    exec.execute(thread);  
    exec.shutdown();  
       while (!exec.isTerminated()) {  
           // 等待所有子线程结束,才退出主线程  
       }          
    System.out.println("end job");  
}

        至此spring quartz多线程并发问题解决。回顾下,我们要使用isTerminated()方法等多线程结束后再结束job;多线程任务派发结束后,要使用shutdown()方法顺序关闭线程(等待正在执行任务,不接受新任务)。

文章来源:http://blog.csdn.net/kenhins/article/details/50514712

猜你喜欢

转载自bijian1013.iteye.com/blog/2307376