Quartz+JAVA+Servlet實現任務排程系統(簡潔)
阿新 • • 發佈:2018-12-26
1.開發環境 tomcat8.5,Jdk1.8,maven ;技術:java ,quartz,servlet(為了簡便)
2.該系統使用場景:
`在12306上買了一張火車票,30分鐘內需要支付(需要新增一個倒計時),30分鐘還沒有支付就請求取消訂單的介面(自動根據url請求),如果支付了收到了支付的回撥通知後,就刪除計時器上的該任務
`
3.測試環境:需要兩個專案,一個為生產環境,一個為排程伺服器(後邊測試我只用一個排程環境,生產環境通過http請求工具模擬測試)
4.建立maven專案:JobServer ,下圖是專案結構圖
5.引入所需要依賴的jar包
<!-- https://mvnrepository.com/artifact/org.quartz-scheduler/quartz -->
<dependency>
<groupId>org.quartz-scheduler</groupId>
<artifactId>quartz</artifactId>
<version>2.2.2</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId >
<artifactId>jackson-core</artifactId>
<version>2.5.3</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.5.3</version>
</dependency>
<!-- https://mvnrepository.com/artifact/commons-beanutils/commons-beanutils -->
<dependency>
<groupId>commons-beanutils</groupId>
<artifactId>commons-beanutils</artifactId>
<version>1.9.2</version>
</dependency>
<!-- https://mvnrepository.com/artifact/commons-collections/commons-collections -->
<dependency>
<groupId>commons-collections</groupId>
<artifactId>commons-collections</artifactId>
<version>3.2.2</version>
</dependency>
<!-- https://mvnrepository.com/artifact/commons-lang/commons-lang -->
<dependency>
<groupId>commons-lang</groupId>
<artifactId>commons-lang</artifactId>
<version>2.6</version>
</dependency>
<!-- https://mvnrepository.com/artifact/commons-logging/commons-logging -->
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>1.2</version>
</dependency>
<!-- https://mvnrepository.com/artifact/net.sf.ezmorph/ezmorph -->
<dependency>
<groupId>net.sf.ezmorph</groupId>
<artifactId>ezmorph</artifactId>
<version>1.0.6</version>
</dependency>
<!-- https://mvnrepository.com/artifact/net.sf.json-lib/json-lib -->
<dependency>
<groupId>net.sf.json-lib</groupId>
<artifactId>json-lib</artifactId>
<version>2.4</version>
</dependency>
編寫以下類:
Config.java
package com.job.config;
public class Config {
public static String token="SB is zhangke";
private Config() {
super();
}
}
JobGroupInfo.java
package com.job.model;
public class JobGroupInfo {
private String jobName;//任務名字
private String jobGroupName;//組名字
private Long nextFireTime;//下次執行時間
public String getJobName() {
return jobName;
}
public void setJobName(String jobName) {
this.jobName = jobName;
}
public String getJobGroupName() {
return jobGroupName;
}
public void setJobGroupName(String jobGroupName) {
this.jobGroupName = jobGroupName;
}
public Long getNextFireTime() {
return nextFireTime;
}
public void setNextFireTime(Long nextFireTime) {
this.nextFireTime = nextFireTime;
}
public JobGroupInfo() {
super();
// TODO Auto-generated constructor stub
}
public JobGroupInfo(String jobName, String jobGroupName, Long nextFireTime) {
super();
this.jobName = jobName;
this.jobGroupName = jobGroupName;
this.nextFireTime = nextFireTime;
}
}
package com.job.model;
TaskInfo.java
public class TaskInfo {
private String backUrl;//任務回撥地址
private String jobName;//任務名字
private Integer seconds;//計時時間
private Object context;//其他內容
private Integer errormaxcount;//失敗請求的次數
private String jobGorupName;//任務組名字
public String getJobGorupName() {
return jobGorupName;
}
public void setJobGorupName(String jobGorupName) {
this.jobGorupName = jobGorupName;
}
public Integer getErrormaxcount() {
return errormaxcount;
}
public void setErrormaxcount(Integer errormaxcount) {
this.errormaxcount = errormaxcount;
}
public String getBackUrl() {
return backUrl;
}
public void setBackUrl(String backUrl) {
this.backUrl = backUrl;
}
public String getJobName() {
return jobName;
}
public void setJobName(String jobName) {
this.jobName = jobName;
}
public Integer getSeconds() {
return seconds;
}
public void setSeconds(Integer seconds) {
this.seconds = seconds;
}
public Object getContext() {
return context;
}
public void setContext(Object context) {
this.context = context;
}
public TaskInfo() {
super();
// TODO Auto-generated constructor stub
}
public TaskInfo(String backUrl, String jobName, Integer seconds,
Object context, Integer errormaxcount, String jobGorupName) {
super();
this.backUrl = backUrl;
this.jobName = jobName;
this.seconds = seconds;
this.context = context;
this.errormaxcount = errormaxcount;
this.jobGorupName = jobGorupName;
}
@Override
public String toString() {
return "TaskInfo [backUrl=" + backUrl + ", jobName=" + jobName
+ ", seconds=" + seconds + ", context=" + context
+ ", errormaxcount=" + errormaxcount + ", jobGorupName="
+ jobGorupName + "]";
}
}
JobBack.java
package com.job.server;
import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import com.job.config.Config;
import com.job.model.TaskInfo;
import com.job.utli.HttpUtil;
import com.job.utli.MD5Util;
/**
* 執行回撥任務
*
* @author niewei
*
*/
public class JobBack implements Job {
//回撥執行方法
public void execute(JobExecutionContext context) throws JobExecutionException {
//得到新增任務中的引數
TaskInfo task = (TaskInfo) context.getMergedJobDataMap().get("task");
sendBack(task);
}
private void sendBack(TaskInfo task) {
task.setErrormaxcount(task.getErrormaxcount() - 1);
// 得到引數請求回撥
try {
//拼裝請求地址以及引數
String uri = "jobName=" + task.getJobName() + "&context=" + task.getContext() + "&jobGorupName="
+ task.getJobGorupName() + "¶mkey=" + MD5Util.GetMD5Code(Config.token);
//請求並得到返回值
String res = HttpUtil.request_post(task.getBackUrl(), uri);
//如果返回值不是“SUCCESS” 就等待10S進行重複請求(此處避免請求失敗就結束請求,引數中傳遞了一個失敗請求次數)
if (!res.trim().equals("SUCCESS")) {
//如果請求錯誤次數還大於0
if (task.getErrormaxcount() >= 1) {
Thread.sleep(10000);
sendBack(task);
}
}
} catch (Exception e) {
System.out.println(e.getMessage());
if (task.getErrormaxcount() >= 1) {
try {
Thread.sleep(10000);
} catch (InterruptedException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
sendBack(task);
}
}
}
}
JobManager.java
package com.job.server;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import org.quartz.Job;
import org.quartz.JobDetail;
import org.quartz.JobKey;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.quartz.SchedulerFactory;
import org.quartz.core.jmx.JobDetailSupport;
import org.quartz.impl.StdSchedulerFactory;
import org.quartz.impl.triggers.SimpleTriggerImpl;
/**
* 任務排程
* @author niewei
*
*/
public class JobManager {
private final static String TRIGGER_GROUP_NAME = "QUARTZ_TRIGGERGROUP";//觸發器組
private final static SchedulerFactory sf = new StdSchedulerFactory();
/**
* 新增任務
* @param jobName 任務名稱
* @param job 任務處理類 需要繼承Job
* @param context 處理任務可以獲取的上下文 通過context.getMergedJobDataMap().getString("context"); 獲取
* @param seconds 間隔秒
* @return
*/
public static int addJob(String jobName,Class<? extends Job> job,Object task,int seconds,String jobGorupName){
try {
//判斷任務是否存在
Scheduler sche = sf.getScheduler();
JobKey jobKey = JobKey.jobKey(jobName,jobGorupName);
if(sche.checkExists(jobKey)){
return 1;//任務已經存在
}
//建立一個JobDetail例項,指定SimpleJob
Map<String, Object> JobDetailmap =new HashMap<String, Object>();
JobDetailmap.put("name", jobName);//設定任務名字
JobDetailmap.put("group", jobGorupName);//設定任務組
JobDetailmap.put("jobClass",job.getCanonicalName());//指定執行類 Task.class.getCanonicalName()
JobDetail jobDetail=JobDetailSupport.newJobDetail(JobDetailmap);
//新增資料內容
jobDetail.getJobDataMap().put("task",task);//傳輸的上下文
//通過SimpleTrigger定義排程規則:馬上啟動,每2秒執行一次,共執行100次 等。。。。
SimpleTriggerImpl simpleTrigger = new SimpleTriggerImpl();
simpleTrigger.setName(jobName);
simpleTrigger.setGroup(TRIGGER_GROUP_NAME);
//什麼時候開始執行
simpleTrigger.setStartTime(new Date(new Date().getTime()+1000*seconds));
//間隔時間
simpleTrigger.setRepeatInterval(1000*seconds);
//最多訪問次數 預設執行一次
simpleTrigger.setRepeatCount(0);
//通過SchedulerFactory獲取一個排程器例項
SchedulerFactory schedulerFactory = new StdSchedulerFactory();
Scheduler scheduler = schedulerFactory.getScheduler();
scheduler.scheduleJob(jobDetail, simpleTrigger);//④ 註冊並進行排程
scheduler.start();//⑤排程啟動
return 0;//新增成功
} catch (Exception e) {
//e.printStackTrace();
return 2;//操作異常
}
}
/**
* 關閉任務排程
* @param jobName 任務名稱
* @return 0 關閉成功 1: 關閉失敗 2:操作異常
*/
public static int closeJob(String jobName,String jobGorupName){
//關閉任務排程
try {
Scheduler sche = sf.getScheduler();
JobKey jobKey = JobKey.jobKey(jobName,jobGorupName);
return sche.deleteJob(jobKey)==true?0:1;
} catch (SchedulerException e) {
//e.printStackTrace();
return 2;
}
}
private JobManager() {}
}
AddJob.java
package com.job.servlet;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.job.model.TaskInfo;
import com.job.server.JobBack;
import com.job.server.JobManager;
/***
* 新增任務排程
*
* @author niewei
*
*/
@WebServlet("/addJob.do")
public class AddJob extends HttpServlet {
private static final long serialVersionUID = 1L;
/**
* @see HttpServlet#HttpServlet()
*/
public AddJob() {
super();
// TODO Auto-generated constructor stub
}
/**
* @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse
* response)
*/
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
//避免get 請求
response.sendError(403);
// doPost(request, response);
}
/**
* 處理新增任務
*/
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// 設定編碼
request.setCharacterEncoding("utf-8");
response.setCharacterEncoding("utf-8");
response.setContentType("utf-8");
// 得到請求
String jobName = request.getParameter("jobName");// 得到任務名稱
// 得到token
//String token = request.getParameter("token");
// 驗證簽名
/*if (!MD5Util.GetMD5Code(jobName + Config.token + "zhangke is shabi!").equals(token)) {
return;
}*/
// 得到回撥
String backUrl = request.getParameter("backUrl");
// 得到請求定時時間
Integer seconds = Integer.valueOf(request.getParameter("seconds"));
String errMaxCount = request.getParameter("errormaxcount");
Integer errormaxcount = errMaxCount == null ? 1 : Integer.valueOf(errMaxCount);// 回撥請求失敗的次數
// 預設為一次
// 得到其他引數
String context = request.getParameter("context");
// 得到任務組
String jobGorupName = request.getParameter("jobGorupName");
TaskInfo t = new TaskInfo(backUrl, jobName, seconds, context, errormaxcount, jobGorupName);
// 新增任務
Integer res = JobManager.addJob(jobName, JobBack.class, t, seconds, jobGorupName);
response.getWriter().write(res.toString());
}
}
MonitoringJob.java
package com.job.servlet;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.quartz.JobKey;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.quartz.Trigger;
import org.quartz.impl.StdSchedulerFactory;
import org.quartz.impl.matchers.GroupMatcher;
import com.job.model.JobGroupInfo;
import net.sf.json.JSONArray;
/***
* 檢視任務情況
*
* @author niewei
*
*/
@WebServlet("/monitoringJob.do")
public class MonitoringJob extends HttpServlet {
private static final long serialVersionUID = 1L;
/**
* @see HttpServlet#HttpServlet()
*/
public MonitoringJob() {
super();
}
protected void doGet(HttpServletRequest request,
HttpServletResponse response) throws ServletException, IOException {
doPost(request, response);
}
protected void doPost(HttpServletRequest request,
HttpServletResponse response) throws ServletException, IOException {
request.setCharacterEncoding("utf-8");
response.setCharacterEncoding("utf-8");
response.setContentType("text/json;char=utf-8");
// 得到任務組名字
String jobGroupName = request.getParameter("jobGroupName");
// 驗證是否為空
//monitoringJob.do
List<JobGroupInfo> list=new ArrayList<JobGroupInfo>();
try {
Scheduler scheduler = new StdSchedulerFactory().getScheduler();
for (JobKey jobKey : scheduler.getJobKeys(GroupMatcher.jobGroupEquals(jobGroupName==null?"":jobGroupName))) {
String jobName = jobKey.getName();
String jobGroup = jobKey.getGroup();
// get job's trigger
List<Trigger> triggers = (List<Trigger>) scheduler.getTriggersOfJob(jobKey);
Date nextFireTime = triggers.get(0).getNextFireTime(); // 下一次執行時間、
JobGroupInfo gri=new JobGroupInfo(jobName, jobGroup, nextFireTime.getTime());
list.add(gri);
}
response.getWriter().write(JSONArray.fromObject(list).toString());
} catch (SchedulerException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
RemoveJob.java
package com.job.servlet;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.job.server.JobManager;
/***
* 移除任務
* @author niewei
*
*/
@WebServlet("/removeJob.do")
public class RemoveJob extends HttpServlet {
private static final long serialVersionUID = 1L;
/**
* @see HttpServlet#HttpServlet()
*/
public RemoveJob() {
super();
// TODO Auto-generated constructor stub
}
/**
* @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
*/
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.sendError(403);
}
/**
* @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response)
*/
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//得到任務名
String jobName=request.getParameter("jobName");
//得到簽名
String token =request.getParameter("token");
//得到任務組
String jobGorupName=request.getParameter("jobGorupName");
//驗證簽名
/*if(!MD5Util.GetMD5Code(jobName+Config.token+"zhangke is sb!").equals(token)){
response.getWriter().write("2");
return;
}*/
//執行移除操作
int res=JobManager.closeJob(jobName,jobGorupName);
if(res==0){//成功
response.getWriter().write("0");
}else if(res==1){//不存在
response.getWriter().write("1");
}else{
//報錯啦!
response.getWriter().write("2");
}
}
}
TestBack.java(這個servlet是用來測試回撥用,我回調url就寫這個。。)
package com.job.servlet;
import java.io.IOException;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import net.sf.json.JSONObject;
import org.quartz.JobKey;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.quartz.Trigger;
import org.quartz.impl.StdSchedulerFactory;
import org.quartz.impl.matchers.GroupMatcher;
import com.fasterxml.jackson.databind.util.JSONPObject;
/**
* 測試回撥地址
*/
@WebServlet("/testback")
public class TestBack extends HttpServlet {
private static final long serialVersionUID = 1L;
/**
* @see HttpServlet#HttpServlet()
*/
public TestBack() {
super();
// TODO Auto-generated constructor stub
}
/**
* @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
*/
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("get請求");
// TODO Auto-generated method stub
}
/**
* @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response)
*/
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
request.setCharacterEncoding("utf-8");
Map<String, String[]> map=request.getParameterMap();
Iterator<String> iter = map.keySet().iterator();
while (iter.hasNext()) {
String key=iter.next();
System.out.println("key:"+key+" value:"+map.get(key)[0]);
}
response.getWriter().write("SUCCESS");
}
}
HttpUtil.java
package com.job.utli;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
public class HttpUtil {
public static String request_get(String httpUrl) {
BufferedReader reader = null;
String result = null;
StringBuffer sbf = new StringBuffer();
try {
URL url = new URL(httpUrl);
HttpURLConnection connection = (HttpURLConnection) url
.openConnection();
connection.setRequestMethod("GET");
connection.setRequestProperty("Content-Type","application/x-www-form-urlencoded");
connection.setDoOutput(true);
connection.setDoInput(true);
connection.setConnectTimeout(5000);
connection.setReadTimeout(20000);
connection.connect();
InputStream is = connection.getInputStream();
reader = new BufferedReader(new InputStreamReader(is,"UTF-8"));
String strRead = null;
while ((strRead = reader.readLine()) != null) {
sbf.append(strRead);
sbf.append("\r\n");
}
reader.close();
result = sbf.toString();
} catch (Exception e) {
e.printStackTrace();
}
return result;
}
public static String request_post(String httpUrl, String httpArg) {
BufferedReader reader = null;
String result = null;
StringBuffer sbf = new StringBuffer();
try {
URL url = new URL(httpUrl);
HttpURLConnection connection = (HttpURLConnection) url
.openConnection();
connection.setRequestMethod("POST");
connection.setRequestProperty("Content-Type","application/x-www-form-urlencoded");
connection.setDoOutput(true);
connection.setDoInput(true);
connection.setConnectTimeout(5000);
connection.setReadTimeout(20000);
connection.getOutputStream().write(httpArg.getBytes("UTF-8"));
connection.connect();
InputStream is = connection.getInputStream();
reader = new BufferedReader(new InputStreamReader(is,"UTF-8"));
String strRead = null;
while ((strRead = reader.readLine()) != null) {
sbf.append(strRead);
sbf.append("\r\n");
}
reader.close();
result = sbf.toString();
} catch (Exception e) {
e.printStackTrace();
}
return result;
}
}
MD5Util.java
package com.job.utli;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
/***
*md5
* @author niewei
*
*/
public class MD5Util {
private final static String[] strDigits = { "0", "1", "2", "3", "4", "5",
"6", "7", "8", "9", "a", "b", "c", "d", "e", "f" };
public MD5Util() {
}
private static String byteToArrayString(byte bByte) {
int iRet = bByte;
if (iRet < 0) {
iRet += 256;
}
int iD1 = iRet / 16;
int iD2 = iRet % 16;
return strDigits[iD1] + strDigits[iD2];
}
private static String byteToNum(byte bByte) {
int iRet = bByte;
System.out.println("iRet1=" + iRet);
if (iRet < 0) {
iRet += 256;
}
return String.valueOf(iRet);
}
private static String byteToString(byte[] bByte) {
StringBuffer sBuffer = new StringBuffer();
for (int i = 0; i < bByte.length; i++) {
sBuffer.append(byteToArrayString(bByte[i]));
}
return sBuffer.toString();
}
public static String GetMD5Code(String strObj) {
String resultString = null;
try {
resultString = new String(strObj);
MessageDigest md = MessageDigest.getInstance("MD5");
resultString = byteToString(md.digest(strObj.getBytes()));
} catch (NoSuchAlgorithmException ex) {
ex.printStackTrace();
}
return resultString;
}
}
以上程式碼編寫完了之後就跑起來
測試
1.我使用火狐瀏覽器—》附加元件—》搜尋HttpRequester安裝重啟火狐即可
2.
在測試工具中填寫如下型別引數即可,注意回撥地址我寫的排程中心的測試方法
3.等5s中就能在在控制檯中打印出來
以上大致就完成了,檢視任務詳情,和刪除任務就不一一測試了。
以上只是簡單得一個場景測試,重啟伺服器任務就丟失,所以這樣不是很推薦這樣在生產環境中使用,後邊文章中會吧任務放置資料庫中。