定時任務備份為創建定時任務,定時的去備份數據庫,使用quartz做定時任務(存在spring無法管理quartz類的問題),並將定時任務的信息保存到數據庫實現定時任務的增刪改查和數據還原,然后將備份代碼集成到quartz中。
代碼中只實現table級別的備份,全局備份只是導出語句不同
1. Quartz集成到springboot
pom文件添加依賴
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-quartz</artifactId> </dependency>
quartz的配置類
/** * * 任務調度管理器 */ @Configuration public class QuartzConfig { private MyJobFactory jobFactory; public QuartzConfig(MyJobFactory jobFactory) { this.jobFactory = jobFactory; } /** * 配置SchedulerFactoryBean * 將一個方法產生為Bean並交給Spring容器管理 */ @Bean public SchedulerFactoryBean schedulerFactoryBean() { // Spring提供SchedulerFactoryBean為Scheduler提供配置信息,並被Spring容器管理其生命周期 SchedulerFactoryBean factory = new SchedulerFactoryBean(); // 啟動時更新己存在的Job,這樣就不用每次修改targetObject后刪除qrtz_job_details表對應記錄了 factory.setOverwriteExistingJobs(true); // 設置自定義Job Factory,用於Spring管理Job bean factory.setJobFactory(jobFactory); return factory; } @Bean(name = "scheduler") public Scheduler scheduler() { return schedulerFactoryBean().getScheduler(); } }
自定義job類
@Component public class MyJobFactory extends AdaptableJobFactory { /** * AutowireCapableBeanFactory接口是BeanFactory的子類 * * 可以連接和填充那些生命周期不被Spring管理的已存在的bean實例 */ private AutowireCapableBeanFactory factory; public MyJobFactory(AutowireCapableBeanFactory factory) { this.factory = factory; } /** * 創建Job實例 */ @Override protected Object createJobInstance(TriggerFiredBundle bundle) throws Exception { // 實例化對象 Object job = super.createJobInstance(bundle); // 進行注入(Spring管理該Bean) factory.autowireBean(job); // 返回對象 return job; } }
定時任務的工具類
@Service public class SchedulerUtil { @Autowired private Scheduler scheduler; private final static Logger LOGGER = LoggerFactory.getLogger(SchedulerUtil.class); /** * @Description: 任務執行類 */ private static SchedulerFactory schedulerFactory = new StdSchedulerFactory(); private static String JOB_GROUP_NAME = "EXTJWEB_JOBGROUP_NAME"; private static String TRIGGER_GROUP_NAME = "EXTJWEB_TRIGGERGROUP_NAME"; /** * 添加一個定時任務,使用默認的任務組名,觸發器名,觸發器組名 * * @param jobName 任務名 * @param cls 任務 * @param time 時間設置,參考quartz說明文檔 */ public void addJob(String jobName, Class cls, String time, Map<String, Object> map) { try { // Scheduler scheduler = schedulerFactory.getScheduler();//創建一個觸發器表 JobDetail jobDetail = JobBuilder.newJob()// 創建一個jobbuilder用來定義一個任務明細。 .ofType(cls)// 設置類,將被實例化 .withIdentity(new JobKey(jobName, JOB_GROUP_NAME))// 使用給定的名稱和默認組jobkey識別任務明細 .build();// 產品定義的JobDetail實例這jobbuilder。 Trigger trigger = TriggerBuilder.newTrigger().withSchedule(CronScheduleBuilder.cronSchedule(time))// 設置schedulebuilder將用於定義觸發器的表。 .withIdentity(new TriggerKey(jobName, TRIGGER_GROUP_NAME)).build();// 創建Trigger TriggerKey triggerKey = trigger.getKey(); JobKey jobKey = trigger.getJobKey(); // 把保存triggerKey和jobKey map.put("triggerKey", triggerKey); map.put("jobKey", jobKey); map.put("jobname", jobName); jobDetail.getJobDataMap().put("canshu", map); scheduler.scheduleJob(jobDetail, trigger);// 綁定 scheduler.start(); } catch (Exception e) { LOGGER.error("添加定時任務失敗", e.getMessage()); throw new RuntimeException(e); } } /** * 修改一個任務的觸發時間(使用默認的任務組名,觸發器名,觸發器組名) * * @param jobName * @param time
* @param map 業務參數,根據實際情況傳參
*/ public void modifyJobTime(String jobName, String time, Map<String, Object> map, String jobkey, String triggerkey) { try { Trigger trigger = TriggerBuilder.newTrigger().withSchedule(CronScheduleBuilder.cronSchedule(time))// 設置schedulebuilder將用於定義觸發器的表。 .withIdentity(new TriggerKey(jobName, TRIGGER_GROUP_NAME)).build();// 創建Trigger if (trigger == null) { return; } removeJob(new TriggerKey(triggerkey), new JobKey(jobkey)); addJob(jobName, QuartzJob.class, time, map); } catch (Exception e) { LOGGER.error("添加定時任務失敗", e.getMessage()); throw new RuntimeException(e); } } /** * 暫停一個任務(使用默認的任務組名,觸發器名,觸發器組名) * * @param triggerKey * @param jobKey */ public void pauseJob(TriggerKey triggerKey, JobKey jobKey) { try { scheduler.pauseTrigger(triggerKey);// 停止觸發器 scheduler.pauseJob(jobKey); } catch (Exception e) { throw new RuntimeException(e); } } /** * 恢復一個任務(使用默認的任務組名,觸發器名,觸發器組名) * * @param triggerKey * @param jobKey */ public void resumeJob(TriggerKey triggerKey, JobKey jobKey) { try { scheduler.resumeTrigger(triggerKey); scheduler.resumeJob(jobKey); } catch (Exception e) { throw new RuntimeException(e); } } /** * 移除一個任務(使用默認的任務組名,觸發器名,觸發器組名) * * @param triggerKey * @param jobKey */ public void removeJob(TriggerKey triggerKey, JobKey jobKey) { try { scheduler.unscheduleJob(triggerKey);// 移除觸發器 scheduler.deleteJob(jobKey); } catch (Exception e) { throw new RuntimeException(e); } } /** * 啟動所有定時任務 */ public void startJobs() { try { Scheduler sched = schedulerFactory.getScheduler(); sched.start(); } catch (Exception e) { throw new RuntimeException(e); } } /** * 關閉所有定時任務 */ public void shutdownJobs() { try { Scheduler sched = schedulerFactory.getScheduler(); if (!sched.isShutdown()) { sched.shutdown(); } } catch (Exception e) { throw new RuntimeException(e); } } }
2. 備份集成到Quartz
創建QuartzJob類實現備份功能
/** * 任務類 */ @Service public class QuartzJob implements Job { @Autowired private BfbService bfbService; @Autowired private SjglSjbflbService sjglSjbflbService; @Autowired private SjglDsrwxxService sjglDsrwxxService; @Value("${dmdump.dev.password}") private String password; @Value("${dameng.dev.ip}") private String ip; @Value("${dameng.dev.userName}") private String userName; @Value("${dameng.dev.userPwd}") private String userPwd; @Override public void execute(JobExecutionContext context) throws JobExecutionException { JobDataMap map = context.getJobDetail().getJobDataMap(); Map<String, Object> data = (Map<String, Object>) map.get("canshu"); String copyType = data.get("copyType").toString(); System.out.println("備份地點:" + data.get("copyType")); // 拼接表名 String tnames = data.get("tnames"); String result = null; // 保存備份列表,備份文件名稱=數據庫id+T+時間 SjglSjbflbParam param = new SjglSjbflbParam(); param.setCjlx(copyType); // 獲取服務器連接 if (data.get("ip") != null && data.get("port") != null && data.get("user") != null && data.get("pwd") != null && data.get("dir") != null) { param.setIp(data.get("ip").toString()); param.setPort(data.get("port").toString()); param.setUsername(data.get("user").toString()); param.setUsername(data.get("user").toString()); param.setRepassword(data.get("pwd").toString()); param.setDir(data.get("dir").toString()); } // 添加數據庫備份列表數據 String fname = data.get("jobname").toString(); param.setDsrwxxid(Long.parseLong(fname.split("T")[0])); // param.setWjmc(fname); param.setCjz((Long) data.get("cjz")); StringBuffer tids = new StringBuffer(); for (String s1 : (String[]) data.get("ids")) { tids.append(s1).append(","); } param.setTids(tids.toString().substring(0, tids.toString().length() - 1)); param.setBflx("2"); String filename = sjglSjbflbService.add(param); // 修改任務表中的jobkey和triggerKey SjglDsrwxxParam dsparam = new SjglDsrwxxParam(); // 在定時任務表中添加keys字段 sjglDsrwxxService.updateKeyByid(Long.parseLong(fname.split("T")[0]), data.get("triggerKey").toString(), context.getJobDetail().getKey().toString()); // 獲取服務器連接 Connection conn = DMruntimeUtil.login(ip, userName, userPwd); // 備份 if (copyType.equals("local")) { String dexpStr = DMruntimeUtil.dumpByTables(data.get("hostip").toString(), data.get("dbname").toString(), data.get("username").toString(), data.get("password").toString(), tnames, filename); String cmd = "cd /opt/dameng/dmdbms/bin;" + dexpStr; result = DMruntimeUtil.execute(conn, cmd); } else if (copyType.equals("foreignLands")) { // 調用shell腳本 sh dexplocal.sh test2 DEV.SYS_USER root@ip:/opt/data // pwd 22 String cmd = "sh /opt/dameng/dmdbms/shelldata/dexphand.sh " + filename + " " + tnames + " " + data.get("user") + "@" + data.get("ip") + ":" + data.get("dir") + " " + data.get("pwd") + " " + data.get("port"); result = DMruntimeUtil.execute(conn, cmd); } // return result; } }
通過linux腳本實現異地備份,也可通過拼接命令實現
#!/bin/bash //腳本聲明 # 手動異城:方式備份EVAL.DEV數據 //注釋信息 cd /opt/dameng/dmdbms/bin filename=$1 tables=$2 reurl=$3 password=$4 port=$5 ./dexp USERID=SYSDBA/SYSDBA@ip:port FILE=${filename}.dmp LOG=${filename}.log TABLES=${tables} DIRECTORY=/opt/dameng/dmdbms/dm7data/EVAL/dexp sshpass -p ${password} scp -P ${port} /opt/dameng/dmdbms/dm7data/EVAL/dexp/${filename}.dmp ${reurl}/${filename}.dmp echo "success"