[整理]在 Web App 中使用 Quartz 替换 Java 定时器

之前的项目里有使用定时器,每隔一段时间去 MQ 服务器拉取新的消息。

使用的是Java 自身的 TimerTask 以及监听器实现的,因为项目需求变更,回头查看代码时,发现原来的实现不是很合理(一个Task里面做了多项操作),就找到Quarts这个开源框架了。

首先,看看原来使用Java提供的TimerTask 来实现定时触发的方法:

1.新建一个继承自java.util.TimerTask的类,重写其run()方法,这就是我们要定时执行的任务了:

import java.util.TimerTask;

public class GetMessageTask extends TimerTask {

	@Override
	public void run() {
		System.out.println("Ho ho, GetMessageTask is running @ "
				+ new java.util.Date());
	}

}

2.新建一个定时器类,在这个类中定义一个java.util.Timer类型的属性用来定义我们的执行任务的开始时间以及,时间间隔:

public class GetMessageTimer {
	java.util.Timer timer;

	public void startTimer() {
		timer = new java.util.Timer();
		/**
		 * 系统启动1分钟后开始执行任务
		 */
		java.util.GregorianCalendar gc = new java.util.GregorianCalendar();
		gc.setTime(new java.util.Date());
		gc.add(java.util.Calendar.MINUTE, 1);
		System.out.println("定时器 @ " + new java.util.Date() + "开启了!");
		/**
		 * 每30000毫秒执行一次取数任务
		 */
		timer.schedule(new GetMessageTask(), gc.getTime(), 30000);
	}

	public void stopTimer() {

		if (timer != null)
			timer.cancel();
		System.out.println("定时器 @ " + new java.util.Date() + "关闭了!");
	}

}

3.新建一个监听器,用于应用部署时,启动我们的定时器:

public class GetMessageListener implements ServletContextListener {

	GetMessageTimer timer = new GetMessageTimer();

	@Override
	public void contextDestroyed(ServletContextEvent sce) {
		timer.stopTimer();
	}

	@Override
	public void contextInitialized(ServletContextEvent sce) {
		timer.startTimer();
	}

}

4.在web.xml中配置监听器:



	
		定时取数
		
			com.abc.web.timertask.GetMessageListener
		
	

	
		index.jsp
	

这样我们就完成了,部署至Web 容器,查看控制台输出如下图:

[整理]在 Web App 中使用 Quartz 替换 Java 定时器_第1张图片

由图所示,定时器开启1分钟以后,我们的任务每隔30秒执行一次。

接下来,我们看一下,怎样使用 Quartz 来实现上面的功能。

1.首先下载 Quartz 不必多说了。

Quartz官网:http://quartz-scheduler.org/

我下的版本为2.1.3

2.新建 java web app,将所需jar包导入项目:

[整理]在 Web App 中使用 Quartz 替换 Java 定时器_第2张图片

3.我们是要在web app中使用quartz,查看官网文档http://quartz-scheduler.org/documentation/quartz-2.1.x/cookbook/ServletInitScheduler,我们可以看出,可以使用2种方法来启动quartz,这里我们使用servlert的方式来启动,修改web.xml如下:



	
		QuartzInitializer
		
			org.quartz.ee.servlet.QuartzInitializerServlet
		

		
			shutdown-on-unload
			true
		

		
		
			start-scheduler-on-load
			true
		

		
		
			start-delay-seconds
			60
		

		1
	
	
		index.jsp
	
4.查看相关文档,我们要先创建我们要执行的Job:
public class GetMessageJob implements Job {

	@Override
	public void execute(JobExecutionContext context)
			throws JobExecutionException {
		System.out.println("Hello, GetMessageJob is Executing @ "
				+ new java.util.Date());
	}

}
5.接着我们要新建Trigger,来告诉Schedule神马时候执Job,因为是测试没有集成其他框架,所以我就想到了quartz自带的QuartzInitializarServlet,这里我们改写这个Servlet。这个Servlet的源码,可以在下载的文档里找到,下面是我改写的Servlet:
public class InitializerQuartzServlet extends HttpServlet {

	private static final long serialVersionUID = 1L;

	public static final String QUARTZ_FACTORY_KEY = "org.quartz.impl.StdSchedulerFactory.KEY";

	private boolean performShutdown = true;
	private boolean waitOnShutdown = false;

	private transient Scheduler scheduler = null;

	@Override
	public void init(ServletConfig cfg) throws javax.servlet.ServletException {
		super.init(cfg);

		System.out
				.println("Quartz Initializer Servlet 加载成功, initializing Scheduler...");

		StdSchedulerFactory factory;
		try {

			String configFile = cfg.getInitParameter("config-file");
			String shutdownPref = cfg.getInitParameter("shutdown-on-unload");

			if (shutdownPref != null) {
				performShutdown = Boolean.valueOf(shutdownPref).booleanValue();
			}
			String shutdownWaitPref = cfg.getInitParameter("wait-on-shutdown");
			if (shutdownPref != null) {
				waitOnShutdown = Boolean.valueOf(shutdownWaitPref)
						.booleanValue();
			}

			factory = getSchedulerFactory(configFile);

			scheduler = factory.getScheduler();

			String startOnLoad = cfg
					.getInitParameter("start-scheduler-on-load");

			int startDelay = 0;
			String startDelayS = cfg.getInitParameter("start-delay-seconds");
			try {
				if (startDelayS != null && startDelayS.trim().length() > 0)
					startDelay = Integer.parseInt(startDelayS);
			} catch (Exception e) {
				log(
						"Cannot parse value of 'start-delay-seconds' to an integer: "
								+ startDelayS + ", defaulting to 5 seconds.", e);
				startDelay = 5;
			}

			/*
			 * 告诉scheduler要执行的任务,以及执行间隔时间,这里是30秒
			 */
			JobDetail getMessageJob = newJob(GetMessageJob.class).withIdentity(
					"getDetailsJob", "group1").build();

			Trigger getMessageTrigger = newTrigger().withIdentity(
					"getDetailsTrigger", "group1").startNow().withSchedule(
					simpleSchedule().withIntervalInSeconds(30).repeatForever())
					.build();			

			scheduler.scheduleJob(getMessageJob, getMessageTrigger);

			if (startOnLoad == null
					|| (Boolean.valueOf(startOnLoad).booleanValue())) {
				if (startDelay <= 0) {
					// Start now
					scheduler.start();
					System.out.println("Scheduler has been started...");
				} else {
					// Start delayed
					scheduler.startDelayed(startDelay);
					System.out.println("Scheduler will start in " + startDelay
							+ " seconds.");
				}
			} else {
				System.out
						.println("Scheduler has not been started. Use scheduler.start()");
			}

			String factoryKey = cfg
					.getInitParameter("servlet-context-factory-key");
			if (factoryKey == null) {
				factoryKey = QUARTZ_FACTORY_KEY;
			}

			System.out
					.println("Storing the Quartz Scheduler Factory in the servlet context at key: "
							+ factoryKey);
			cfg.getServletContext().setAttribute(factoryKey, factory);

			String servletCtxtKey = cfg
					.getInitParameter("scheduler-context-servlet-context-key");
			if (servletCtxtKey != null) {
				System.out
						.println("Storing the ServletContext in the scheduler context at key: "
								+ servletCtxtKey);
				scheduler.getContext().put(servletCtxtKey,
						cfg.getServletContext());
			}

		} catch (Exception e) {
			System.out.println("Quartz Scheduler 初始化失败: "
					+ e.toString());
			throw new ServletException(e);
		}
	}

	protected StdSchedulerFactory getSchedulerFactory(String configFile)
			throws SchedulerException {
		StdSchedulerFactory factory;
		// get Properties
		if (configFile != null) {
			factory = new StdSchedulerFactory(configFile);
		} else {
			factory = new StdSchedulerFactory();
		}
		return factory;
	}

	@Override
	public void destroy() {

		if (!performShutdown) {
			return;
		}

		try {
			if (scheduler != null) {
				scheduler.shutdown(waitOnShutdown);
			}
		} catch (Exception e) {
			System.out.println("Quartz Scheduler 停止失败: "
					+ e.toString());
			e.printStackTrace();
		}

		System.out.println("Quartz Scheduler 停止了.");
	}

	@Override
	public void doPost(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
		response.sendError(HttpServletResponse.SC_FORBIDDEN);
	}

	@Override
	public void doGet(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
		response.sendError(HttpServletResponse.SC_FORBIDDEN);
	}

}

6.这里我们看到初始化时需要一个quartz.properties的配置文件,我们从demo里面copy一个过来,稍作修改:

org.quartz.scheduler.instanceName: ExampleScheduler
org.quartz.threadPool.threadCount: 3
org.quartz.threadPool.class: org.quartz.simpl.SimpleThreadPool
org.quartz.jobStore.class:org.quartz.simpl.RAMJobStore

7.不要忘了把web.xml中的servlet-class改成我们改写的servlet,最后配一个log4j.properties。部署到应用服务器查看控制台输出如下图:

[整理]在 Web App 中使用 Quartz 替换 Java 定时器_第3张图片

[整理]在 Web App 中使用 Quartz 替换 Java 定时器_第4张图片

上图所示,定时器在应用部署1分钟后开始启动,任务每隔30秒执行一次。

更高级的使用Quartz的方法,请大家参考cookbook。

你可能感兴趣的:(后端)