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.cc.ovp.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.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.cc.ovp.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.cc.ovp.web.video.CleanVideoJsonRedis</listener-class>
- </listener>
注意:<listener/> 标签一定要在<filter/>之后,在<servlet/>之前配置
- 2。CleanVideoJsonRedis.java
- package myservlet;
- 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.cc.ovp.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.cc.ovp.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.cc.ovp.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");
- }
OK,至此spring quartz多线程并发问题解决。回顾下,我们要使用isTerminated()方法等多线程结束后在结束job;多线程任务派发结束后,要使用shutdown()方法顺序关闭线程(等待正在执行任务,不接受新任务)