Springboot2.x+Quartz分布式集群的实现

生产环境一般都是多节点高可用,Springboot本身自带有定时任务功能,但我们项目需求要求能对定时任务进行增,删,改,查。所以考虑引进Quartz,引入Quartz就需要考虑分布式集群,所以就有了这篇文章。

数据库脚本

Quartz数据库有11张表,既支持Mysql,也支持Oracle

Mysql

/*
Navicat MySQL Data Transfer

Source Server     : 10.19.34.3_ehr_admin
Source Server Version : 50639
Source Host      : 10.19.34.3:3306
Source Database    : attend_base_dev

Target Server Type  : MYSQL
Target Server Version : 50639
File Encoding     : 65001

Date: 2020-08-28 16:29:36
*/

-- SET FOREIGN_KEY_CHECKS=0;

-- ----------------------------
-- Table structure for `qrtz_CALENDARS`
-- ----------------------------
-- DROP TABLE IF EXISTS `qrtz_CALENDARS`;
CREATE TABLE `qrtz_CALENDARS` (
 `SCHED_NAME` varchar(120) NOT NULL COMMENT '计划名称',
 `CALENDAR_NAME` varchar(200) NOT NULL,
 `CALENDAR` blob NOT NULL,
 PRIMARY KEY (`SCHED_NAME`,`CALENDAR_NAME`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='日历信息';

-- ----------------------------
-- Records of qrtz_CALENDARS
-- ----------------------------

-- ----------------------------
-- Table structure for `qrtz_FIRED_TRIGGERS`
-- ----------------------------
-- DROP TABLE IF EXISTS `qrtz_FIRED_TRIGGERS`;
CREATE TABLE `qrtz_FIRED_TRIGGERS` (
 `SCHED_NAME` varchar(120) NOT NULL COMMENT '计划名称',
 `ENTRY_ID` varchar(95) NOT NULL COMMENT '组标识',
 `TRIGGER_NAME` varchar(200) NOT NULL COMMENT '触发器名称',
 `TRIGGER_GROUP` varchar(200) NOT NULL COMMENT '触发器组',
 `INSTANCE_NAME` varchar(200) NOT NULL COMMENT '当前实例的名称',
 `FIRED_TIME` bigint(13) NOT NULL COMMENT '当前执行时间',
 `SCHED_TIME` bigint(13) NOT NULL COMMENT '计划时间',
 `PRIORITY` int(11) NOT NULL COMMENT '权重',
 `STATE` varchar(16) NOT NULL COMMENT '状态:WAITING:等待 \r\nPAUSED:暂停 \r\nACQUIRED:正常执行 \r\nBLOCKED:阻塞 \r\nERROR:错误',
 `JOB_NAME` varchar(200) DEFAULT NULL COMMENT '作业名称',
 `JOB_GROUP` varchar(200) DEFAULT NULL COMMENT '作业组',
 `IS_NONCONCURRENT` varchar(1) DEFAULT NULL COMMENT '是否并行',
 `REQUESTS_RECOVERY` varchar(1) DEFAULT NULL COMMENT '是否要求唤醒',
 PRIMARY KEY (`SCHED_NAME`,`ENTRY_ID`),
 KEY `IDX_qrtz_FT_TRIG_INST_NAME` (`SCHED_NAME`,`INSTANCE_NAME`),
 KEY `IDX_qrtz_FT_INST_JOB_REQ_RCVRY` (`SCHED_NAME`,`INSTANCE_NAME`,`REQUESTS_RECOVERY`),
 KEY `IDX_qrtz_FT_J_G` (`SCHED_NAME`,`JOB_NAME`,`JOB_GROUP`),
 KEY `IDX_qrtz_FT_JG` (`SCHED_NAME`,`JOB_GROUP`),
 KEY `IDX_qrtz_FT_T_G` (`SCHED_NAME`,`TRIGGER_NAME`,`TRIGGER_GROUP`),
 KEY `IDX_qrtz_FT_TG` (`SCHED_NAME`,`TRIGGER_GROUP`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='保存已经触发的触发器状态信息';

-- ----------------------------
-- Records of qrtz_FIRED_TRIGGERS
-- ----------------------------

-- ----------------------------
-- Table structure for `qrtz_JOB_DETAILS`
-- ----------------------------
-- DROP TABLE IF EXISTS `qrtz_JOB_DETAILS`;
CREATE TABLE `qrtz_JOB_DETAILS` (
 `SCHED_NAME` varchar(120) NOT NULL COMMENT '计划名称',
 `JOB_NAME` varchar(200) NOT NULL COMMENT '集群中job的名字',
 `JOB_GROUP` varchar(200) NOT NULL COMMENT '集群中job的所属组的名字',
 `DESCRIPTION` varchar(250) DEFAULT NULL COMMENT '描述',
 `JOB_CLASS_NAME` varchar(250) NOT NULL COMMENT '作业程序类名',
 `IS_DURABLE` varchar(1) NOT NULL COMMENT '是否持久',
 `IS_NONCONCURRENT` varchar(1) NOT NULL COMMENT '是否并行',
 `IS_UPDATE_DATA` varchar(1) NOT NULL COMMENT '是否更新',
 `REQUESTS_RECOVERY` varchar(1) NOT NULL COMMENT '是否要求唤醒',
 `JOB_DATA` blob,
 PRIMARY KEY (`SCHED_NAME`,`JOB_NAME`,`JOB_GROUP`),
 KEY `IDX_qrtz_J_REQ_RECOVERY` (`SCHED_NAME`,`REQUESTS_RECOVERY`),
 KEY `IDX_qrtz_J_GRP` (`SCHED_NAME`,`JOB_GROUP`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='job 详细信息';

-- ----------------------------
-- Records of qrtz_JOB_DETAILS
-- ----------------------------
-- ----------------------------
-- Table structure for `qrtz_LOCKS`
-- ----------------------------
-- DROP TABLE IF EXISTS `qrtz_LOCKS`;
CREATE TABLE `qrtz_LOCKS` (
 `SCHED_NAME` varchar(120) NOT NULL COMMENT '计划名称',
 `LOCK_NAME` varchar(40) NOT NULL COMMENT '锁名称',
 PRIMARY KEY (`SCHED_NAME`,`LOCK_NAME`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='存储程序的悲观锁的信息(假如使用了悲观锁) ';

-- ----------------------------
-- Records of qrtz_LOCKS

-- ----------------------------
-- Table structure for `qrtz_PAUSED_TRIGGER_GRPS`
-- ----------------------------
-- DROP TABLE IF EXISTS `qrtz_PAUSED_TRIGGER_GRPS`;
CREATE TABLE `qrtz_PAUSED_TRIGGER_GRPS` (
 `SCHED_NAME` varchar(120) NOT NULL COMMENT '计划名称',
 `TRIGGER_GROUP` varchar(200) NOT NULL COMMENT '触发器组',
 PRIMARY KEY (`SCHED_NAME`,`TRIGGER_GROUP`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='存放暂停掉的触发器';

-- ----------------------------
-- Records of qrtz_PAUSED_TRIGGER_GRPS
-- ----------------------------

-- ----------------------------
-- Table structure for `qrtz_SCHEDULER_STATE`
-- ----------------------------
-- DROP TABLE IF EXISTS `qrtz_SCHEDULER_STATE`;
CREATE TABLE `qrtz_SCHEDULER_STATE` (
 `SCHED_NAME` varchar(120) NOT NULL COMMENT '计划名称',
 `INSTANCE_NAME` varchar(200) NOT NULL COMMENT '实例名称',
 `LAST_CHECKIN_TIME` bigint(13) NOT NULL COMMENT '最后的检查时间',
 `CHECKIN_INTERVAL` bigint(13) NOT NULL COMMENT '检查间隔',
 PRIMARY KEY (`SCHED_NAME`,`INSTANCE_NAME`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='调度器状态';

-- ----------------------------
-- Records of qrtz_SCHEDULER_STATE

-- ----------------------------
-- Table structure for `qrtz_TRIGGERS`
-- ----------------------------
-- DROP TABLE IF EXISTS `qrtz_TRIGGERS`;
CREATE TABLE `qrtz_TRIGGERS` (
 `SCHED_NAME` varchar(120) NOT NULL COMMENT '计划名称',
 `TRIGGER_NAME` varchar(200) NOT NULL COMMENT '触发器名称',
 `TRIGGER_GROUP` varchar(200) NOT NULL COMMENT '触发器组',
 `JOB_NAME` varchar(200) NOT NULL COMMENT '作业名称',
 `JOB_GROUP` varchar(200) NOT NULL COMMENT '作业组',
 `DESCRIPTION` varchar(250) DEFAULT NULL COMMENT '描述',
 `NEXT_FIRE_TIME` bigint(13) DEFAULT NULL COMMENT '下次执行时间',
 `PREV_FIRE_TIME` bigint(13) DEFAULT NULL COMMENT '前一次',
 `PRIORITY` int(11) DEFAULT NULL COMMENT '优先权',
 `TRIGGER_STATE` varchar(16) NOT NULL COMMENT '触发器状态',
 `TRIGGER_TYPE` varchar(8) NOT NULL COMMENT '触发器类型',
 `START_TIME` bigint(13) NOT NULL COMMENT '开始时间',
 `END_TIME` bigint(13) DEFAULT NULL COMMENT '结束时间',
 `CALENDAR_NAME` varchar(200) DEFAULT NULL COMMENT '日历名称',
 `MISFIRE_INSTR` smallint(2) DEFAULT NULL COMMENT '失败次数',
 `JOB_DATA` blob,
 PRIMARY KEY (`SCHED_NAME`,`TRIGGER_NAME`,`TRIGGER_GROUP`),
 KEY `IDX_qrtz_T_J` (`SCHED_NAME`,`JOB_NAME`,`JOB_GROUP`),
 KEY `IDX_qrtz_T_JG` (`SCHED_NAME`,`JOB_GROUP`),
 KEY `IDX_qrtz_T_C` (`SCHED_NAME`,`CALENDAR_NAME`),
 KEY `IDX_qrtz_T_G` (`SCHED_NAME`,`TRIGGER_GROUP`),
 KEY `IDX_qrtz_T_STATE` (`SCHED_NAME`,`TRIGGER_STATE`),
 KEY `IDX_qrtz_T_N_STATE` (`SCHED_NAME`,`TRIGGER_NAME`,`TRIGGER_GROUP`,`TRIGGER_STATE`),
 KEY `IDX_qrtz_T_N_G_STATE` (`SCHED_NAME`,`TRIGGER_GROUP`,`TRIGGER_STATE`),
 KEY `IDX_qrtz_T_NEXT_FIRE_TIME` (`SCHED_NAME`,`NEXT_FIRE_TIME`),
 KEY `IDX_qrtz_T_NFT_ST` (`SCHED_NAME`,`TRIGGER_STATE`,`NEXT_FIRE_TIME`),
 KEY `IDX_qrtz_T_NFT_MISFIRE` (`SCHED_NAME`,`MISFIRE_INSTR`,`NEXT_FIRE_TIME`),
 KEY `IDX_qrtz_T_NFT_ST_MISFIRE` (`SCHED_NAME`,`MISFIRE_INSTR`,`NEXT_FIRE_TIME`,`TRIGGER_STATE`),
 KEY `IDX_qrtz_T_NFT_ST_MISFIRE_GRP` (`SCHED_NAME`,`MISFIRE_INSTR`,`NEXT_FIRE_TIME`,`TRIGGER_GROUP`,`TRIGGER_STATE`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='触发器';

-- ----------------------------
-- Records of qrtz_TRIGGERS

-- ----------------------------
-- Table structure for `qrtz_SIMPLE_TRIGGERS`
-- ----------------------------
-- DROP TABLE IF EXISTS `qrtz_SIMPLE_TRIGGERS`;
CREATE TABLE `qrtz_SIMPLE_TRIGGERS` (
 `SCHED_NAME` varchar(120) NOT NULL COMMENT '计划名称',
 `TRIGGER_NAME` varchar(200) NOT NULL COMMENT '触发器名称',
 `TRIGGER_GROUP` varchar(200) NOT NULL COMMENT '触发器组',
 `REPEAT_COUNT` bigint(7) NOT NULL COMMENT '重复次数',
 `REPEAT_INTERVAL` bigint(12) NOT NULL COMMENT '重复间隔',
 `TIMES_TRIGGERED` bigint(10) NOT NULL COMMENT '触发次数',
 PRIMARY KEY (`SCHED_NAME`,`TRIGGER_NAME`,`TRIGGER_GROUP`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='简单的触发器';

-- ----------------------------
-- Records of qrtz_SIMPLE_TRIGGERS
-- ----------------------------

-- ----------------------------
-- Table structure for `qrtz_SIMPROP_TRIGGERS`
-- ----------------------------
-- DROP TABLE IF EXISTS `qrtz_SIMPROP_TRIGGERS`;
CREATE TABLE `qrtz_SIMPROP_TRIGGERS` (
 `SCHED_NAME` varchar(120) NOT NULL COMMENT '计划名称',
 `TRIGGER_NAME` varchar(200) NOT NULL COMMENT '触发器名称',
 `TRIGGER_GROUP` varchar(200) NOT NULL COMMENT '触发器组',
 `STR_PROP_1` varchar(512) DEFAULT NULL,
 `STR_PROP_2` varchar(512) DEFAULT NULL,
 `STR_PROP_3` varchar(512) DEFAULT NULL,
 `INT_PROP_1` int(11) DEFAULT NULL,
 `INT_PROP_2` int(11) DEFAULT NULL,
 `LONG_PROP_1` bigint(20) DEFAULT NULL,
 `LONG_PROP_2` bigint(20) DEFAULT NULL,
 `DEC_PROP_1` decimal(13,4) DEFAULT NULL,
 `DEC_PROP_2` decimal(13,4) DEFAULT NULL,
 `BOOL_PROP_1` varchar(1) DEFAULT NULL,
 `BOOL_PROP_2` varchar(1) DEFAULT NULL,
 PRIMARY KEY (`SCHED_NAME`,`TRIGGER_NAME`,`TRIGGER_GROUP`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='存储CalendarIntervalTrigger和DailyTimeIntervalTrigger两种类型的触发器';

-- ----------------------------
-- Records of qrtz_SIMPROP_TRIGGERS
-- ----------------------------

-- ----------------------------
-- Table structure for `qrtz_BLOB_TRIGGERS`
-- ----------------------------
-- DROP TABLE IF EXISTS `qrtz_BLOB_TRIGGERS`;
CREATE TABLE `qrtz_BLOB_TRIGGERS` (
 `SCHED_NAME` varchar(120) NOT NULL COMMENT '计划名',
 `TRIGGER_NAME` varchar(200) NOT NULL COMMENT '触发器名称',
 `TRIGGER_GROUP` varchar(200) NOT NULL COMMENT '触发器组',
 `BLOB_DATA` blob,
 PRIMARY KEY (`SCHED_NAME`,`TRIGGER_NAME`,`TRIGGER_GROUP`),
 KEY `SCHED_NAME` (`SCHED_NAME`,`TRIGGER_NAME`,`TRIGGER_GROUP`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='以Blob 类型存储的触发器';

-- ----------------------------
-- Records of qrtz_BLOB_TRIGGERS
-- ----------------------------

-- ----------------------------
-- Table structure for `qrtz_CRON_TRIGGERS`
-- ----------------------------
-- DROP TABLE IF EXISTS `qrtz_CRON_TRIGGERS`;
CREATE TABLE `qrtz_CRON_TRIGGERS` (
 `SCHED_NAME` varchar(120) NOT NULL COMMENT '计划名称',
 `TRIGGER_NAME` varchar(200) NOT NULL COMMENT '触发器名称',
 `TRIGGER_GROUP` varchar(200) NOT NULL COMMENT '触发器组',
 `CRON_EXPRESSION` varchar(120) NOT NULL COMMENT '时间表达式',
 `TIME_ZONE_ID` varchar(80) DEFAULT NULL COMMENT '时区ID   nvarchar   80',
 PRIMARY KEY (`SCHED_NAME`,`TRIGGER_NAME`,`TRIGGER_GROUP`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='定时触发器';

-- ----------------------------
-- Records of qrtz_CRON_TRIGGERS
-- ----------------------------
Oracle
create table QRTZ_CALENDARS
(
 sched_name  VARCHAR2(120) not null,
 calendar_name VARCHAR2(200) not null,
 calendar   BLOB not null
);
alter table QRTZ_CALENDARS
 add constraint PK_QRTZ_CALENDARS primary key (SCHED_NAME, CALENDAR_NAME);

create table QRTZ_FIRED_TRIGGERS
(
 sched_name    VARCHAR2(120) not null,
 entry_id     VARCHAR2(95) not null,
 trigger_name   VARCHAR2(200) not null,
 trigger_group   VARCHAR2(200) not null,
 instance_name   VARCHAR2(200) not null,
 fired_time    NUMBER(13) not null,
 sched_time    NUMBER(13) not null,
 priority     INTEGER not null,
 state       VARCHAR2(16) not null,
 job_name     VARCHAR2(200),
 job_group     VARCHAR2(200),
 is_nonconcurrent VARCHAR2(1),
 requests_recovery VARCHAR2(1)
);
alter table QRTZ_FIRED_TRIGGERS
 add constraint PK_QRTZ_FIRED_TRIGGERS primary key (SCHED_NAME, ENTRY_ID);

create table QRTZ_JOB_DETAILS
(
 sched_name    VARCHAR2(120) not null,
 job_name     VARCHAR2(200) not null,
 job_group     VARCHAR2(200) not null,
 description    VARCHAR2(250),
 job_class_name  VARCHAR2(250) not null,
 is_durable    VARCHAR2(1) not null,
 is_nonconcurrent VARCHAR2(1) not null,
 is_update_data  VARCHAR2(1) not null,
 requests_recovery VARCHAR2(1) not null,
 job_data     BLOB
);
alter table QRTZ_JOB_DETAILS
 add constraint PK_QRTZ_JOB_DETAILS primary key (SCHED_NAME, JOB_NAME, JOB_GROUP);

create table QRTZ_LOCKS
(
 sched_name VARCHAR2(120) not null,
 lock_name VARCHAR2(40) not null
);
alter table QRTZ_LOCKS
 add constraint PK_QRTZ_LOCKS primary key (SCHED_NAME, LOCK_NAME);

create table QRTZ_PAUSED_TRIGGER_GRPS
(
 sched_name  VARCHAR2(120) not null,
 trigger_group VARCHAR2(200) not null
);
alter table QRTZ_PAUSED_TRIGGER_GRPS
 add constraint PK__TRIGGER_GRPS primary key (SCHED_NAME, TRIGGER_GROUP);

create table QRTZ_SCHEDULER_STATE
(
 sched_name    VARCHAR2(120) not null,
 instance_name   VARCHAR2(200) not null,
 last_checkin_time NUMBER(13) not null,
 checkin_interval NUMBER(13) not null
);
alter table QRTZ_SCHEDULER_STATE
 add constraint PK_QRTZ_SCHEDULER_STATE primary key (SCHED_NAME, INSTANCE_NAME);

create table QRTZ_TRIGGERS
(
 sched_name   VARCHAR2(120) not null,
 trigger_name  VARCHAR2(200) not null,
 trigger_group VARCHAR2(200) not null,
 job_name    VARCHAR2(200) not null,
 job_group   VARCHAR2(200) not null,
 description  VARCHAR2(250),
 next_fire_time NUMBER(13),
 prev_fire_time NUMBER(13),
 priority    INTEGER,
 trigger_state VARCHAR2(16) not null,
 trigger_type  VARCHAR2(8) not null,
 start_time   NUMBER(13) not null,
 end_time    NUMBER(13),
 calendar_name VARCHAR2(200),
 misfire_instr NUMBER(2),
 job_data    BLOB
);

alter table QRTZ_TRIGGERS
 add constraint PK_QRTZ_TRIGGERS primary key (SCHED_NAME, TRIGGER_NAME, TRIGGER_GROUP);

create table QRTZ_SIMPLE_TRIGGERS
(
 sched_name   VARCHAR2(120) not null,
 trigger_name  VARCHAR2(200) not null,
 trigger_group  VARCHAR2(200) not null,
 repeat_count  NUMBER(7) not null,
 repeat_interval NUMBER(12) not null,
 times_triggered NUMBER(10) not null
);
alter table QRTZ_SIMPLE_TRIGGERS
 add constraint PK_QRTZ_SIMPLE_TRIGGERS primary key (SCHED_NAME, TRIGGER_NAME, TRIGGER_GROUP);

create table QRTZ_SIMPROP_TRIGGERS
(
 sched_name  VARCHAR2(120) not null,
 trigger_name VARCHAR2(200) not null,
 trigger_group VARCHAR2(200) not null,
 str_prop_1  VARCHAR2(512),
 str_prop_2  VARCHAR2(512),
 str_prop_3  VARCHAR2(512),
 int_prop_1  INTEGER,
 int_prop_2  INTEGER,
 long_prop_1  NUMBER,
 long_prop_2  NUMBER,
 dec_prop_1  NUMBER(13,4),
 dec_prop_2  NUMBER(13,4),
 bool_prop_1  VARCHAR2(1),
 bool_prop_2  VARCHAR2(1)
);
alter table QRTZ_SIMPROP_TRIGGERS
 add constraint PK_QRTZ_SIMPROP_TRIGGERS primary key (SCHED_NAME, TRIGGER_NAME, TRIGGER_GROUP);

create table QRTZ_BLOB_TRIGGERS
(
 sched_name  VARCHAR2(120) not null,
 trigger_name VARCHAR2(200) not null,
 trigger_group VARCHAR2(200) not null,
 blob_data   BLOB
);
alter table QRTZ_BLOB_TRIGGERS
 add constraint PK_QRTZ_BLOB_TRIGGERS primary key (SCHED_NAME, TRIGGER_NAME, TRIGGER_GROUP);

create table QRTZ_CRON_TRIGGERS
(
 sched_name   VARCHAR2(120) not null,
 trigger_name  VARCHAR2(200) not null,
 trigger_group  VARCHAR2(200) not null,
 cron_expression VARCHAR2(200) not null,
 time_zone_id  VARCHAR2(80)
);
alter table QRTZ_CRON_TRIGGERS
 add constraint PK_QRTZ_CRON_TRIGGERS primary key (SCHED_NAME, TRIGGER_NAME, TRIGGER_GROUP);

delete from QRTZ_JOB_DETAILS;
delete from QRTZ_CRON_TRIGGERS;
delete from QRTZ_BLOB_TRIGGERS;
delete from QRTZ_CALENDARS;
delete from QRTZ_FIRED_TRIGGERS;
delete from QRTZ_LOCKS;
delete from QRTZ_PAUSED_TRIGGER_GRPS;
delete from QRTZ_SCHEDULER_STATE;
delete from QRTZ_SIMPLE_TRIGGERS;
delete from QRTZ_SIMPROP_TRIGGERS;
delete from QRTZ_TRIGGERS;

Maven

我这里后台使用的是Springboot2.1

   <dependency>
     <groupId>org.springframework.boot</groupId>
     <artifactId>spring-boot-starter-quartz</artifactId>
   </dependency>

application.yml

quartz:
  #quartz相关属性配置
  properties:
   org:
    quartz:
     scheduler:
      instanceName: clusteredScheduler #调度器的实例名
      instanceId: AUTO #调度器编号自动生成
     jobStore:
      class: org.quartz.impl.jdbcjobstore.JobStoreTX
      driverDelegateClass: org.quartz.impl.jdbcjobstore.StdJDBCDelegate
      tablePrefix: qrtz_ #数据库表名前缀
      isClustered: true #开启分布式部署
      clusterCheckinInterval: 10000 #分布式节点有效性检查时间间隔,单位:秒
      useProperties: false
     threadPool:
      class: org.quartz.simpl.SimpleThreadPool #自带的线程池实现类
      threadCount: 10 #开启10个线程
      threadPriority: 5 #工作者线程的优先级
      threadsInheritContextClassLoaderOfInitializingThread: true
  #数据库方式
  job-store-type: jdbc

Bean

import org.quartz.JobDataMap;
import java.util.Date;

/**
 * @program: QuartzBean
 * @description:
 * @author: Yuwl
 * @create: 2020-06-02 18:36
 **/
public class QuartzBean {

  /** 任务id */
  private String id;

  /** 任务名称 */
  private String jobName;

  /** 任务组 */
  private String jobGroup;

  /** 任务执行类 */
  private String jobClass;

  /** 任务状态 启动还是暂停*/
  private Integer status;

  /**
   * 任务开始时间
   */
  private Date startTime;

  /**
   * 任务循环间隔-单位:分钟
   */
  private Integer interval;

  /**
   * 任务结束时间
   */
  private Date endTime;

  /** 任务运行时间表达式 */
  private String cronExpression;

  private JobDataMap jobDataMap;

  public String getId() {
    return id;
  }

  public void setId(String id) {
    this.id = id;
  }

  public String getJobName() {
    return jobName;
  }

  public void setJobName(String jobName) {
    this.jobName = jobName;
  }

  public String getJobClass() {
    return jobClass;
  }

  public void setJobClass(String jobClass) {
    this.jobClass = jobClass;
  }

  public Integer getStatus() {
    return status;
  }

  public void setStatus(Integer status) {
    this.status = status;
  }

  public String getCronExpression() {
    return cronExpression;
  }

  public void setCronExpression(String cronExpression) {
    this.cronExpression = cronExpression;
  }

  public Date getStartTime() {
    return startTime;
  }

  public void setStartTime(Date startTime) {
    this.startTime = startTime;
  }

  public Integer getInterval() {
    return interval;
  }

  public void setInterval(Integer interval) {
    this.interval = interval;
  }

  public Date getEndTime() {
    return endTime;
  }

  public void setEndTime(Date endTime) {
    this.endTime = endTime;
  }

  public JobDataMap getJobDataMap() {
    return jobDataMap;
  }

  public void setJobDataMap(JobDataMap jobDataMap) {
    this.jobDataMap = jobDataMap;
  }

  public String getJobGroup() {
    return jobGroup;
  }

  public void setJobGroup(String jobGroup) {
    this.jobGroup = jobGroup;
  }
}

Service

import com.baomidou.mybatisplus.core.toolkit.ObjectUtils;
import com.ruoyi.framework.quartz.QuartzBean;
import org.quartz.*;
import org.quartz.impl.matchers.GroupMatcher;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;

/**
 * @program: QuartzJobService
 * @description:
 * @author: Yuwl
 * @create: 2020-07-21 17:00
 **/
@Service
public class QuartzJobService {

  @Autowired
  private Scheduler scheduler;

  /**
   * 创建定时任务Simple
   * quartzBean.getInterval()==null表示单次提醒,
   * 否则循环提醒(quartzBean.getEndTime()!=null)
   * @param quartzBean
   */
  public void createScheduleJobSimple(QuartzBean quartzBean) throws Exception{
    //获取到定时任务的执行类 必须是类的绝对路径名称
    //定时任务类需要是job类的具体实现 QuartzJobBean是job的抽象类。
    Class<? extends Job> jobClass = (Class<? extends Job>) Class.forName(quartzBean.getJobClass());
    // 构建定时任务信息
    JobDetail jobDetail = JobBuilder.newJob(jobClass)
        .withIdentity(quartzBean.getJobName(), ObjectUtils.isNotEmpty(quartzBean.getJobGroup()) ?quartzBean.getJobGroup():null)
        .setJobData(quartzBean.getJobDataMap())
        .build();
    // 设置定时任务执行方式
    SimpleScheduleBuilder simpleScheduleBuilder = null;
    if (quartzBean.getInterval() == null) { //单次
      simpleScheduleBuilder = SimpleScheduleBuilder.simpleSchedule();
    } else { //循环
      simpleScheduleBuilder = SimpleScheduleBuilder.repeatMinutelyForever(quartzBean.getInterval());
    }
    // 构建触发器trigger
    Trigger trigger = null;
    if (quartzBean.getInterval() == null) { //单次
      trigger = TriggerBuilder.newTrigger()
          .withIdentity(quartzBean.getJobName(),ObjectUtils.isNotEmpty(quartzBean.getJobGroup()) ?quartzBean.getJobGroup():null)
          .withSchedule(simpleScheduleBuilder)
          .startAt(quartzBean.getStartTime())
          .build();
    } else { //循环
      trigger = TriggerBuilder.newTrigger()
          .withIdentity(quartzBean.getJobName(),ObjectUtils.isNotEmpty(quartzBean.getJobGroup()) ?quartzBean.getJobGroup():null)
          .withSchedule(simpleScheduleBuilder)
          .startAt(quartzBean.getStartTime())
          .endAt(quartzBean.getEndTime())
          .build();
    }
    scheduler.scheduleJob(jobDetail, trigger);
  }

  /**
   * 创建定时任务Cron
   * 定时任务创建之后默认启动状态
   * @param quartzBean 定时任务信息类
   * @throws Exception
   */
  public void createScheduleJobCron(QuartzBean quartzBean) throws Exception{
    //获取到定时任务的执行类 必须是类的绝对路径名称
    //定时任务类需要是job类的具体实现 QuartzJobBean是job的抽象类。
    Class<? extends Job> jobClass = (Class<? extends Job>) Class.forName(quartzBean.getJobClass());
    // 构建定时任务信息
    JobDetail jobDetail = JobBuilder.newJob(jobClass).withIdentity(quartzBean.getJobName()).setJobData(quartzBean.getJobDataMap()).build();
    // 设置定时任务执行方式
    CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(quartzBean.getCronExpression());
    // 构建触发器trigger
    CronTrigger trigger = TriggerBuilder.newTrigger().withIdentity(quartzBean.getJobName()).withSchedule(scheduleBuilder).build();
    scheduler.scheduleJob(jobDetail, trigger);
  }

  /**
   * 根据任务名称暂停定时任务
   * @param jobName 定时任务名称
   * @param jobGroup 任务组(没有分组传值null)
   * @throws Exception
   */
  public void pauseScheduleJob(String jobName,String jobGroup) throws Exception{
    JobKey jobKey = JobKey.jobKey(jobName,ObjectUtils.isNotEmpty(jobGroup) ?jobGroup:null);
    scheduler.pauseJob(jobKey);
  }

  /**
   * 根据任务名称恢复定时任务
   * @param jobName  定时任务名
   * @param jobGroup 任务组(没有分组传值null)
   * @throws SchedulerException
   */
  public void resumeScheduleJob(String jobName,String jobGroup) throws Exception {
    JobKey jobKey = JobKey.jobKey(jobName,ObjectUtils.isNotEmpty(jobGroup) ?jobGroup:null);
    scheduler.resumeJob(jobKey);
  }

  /**
   * 根据任务名称立即运行一次定时任务
   * @param jobName    定时任务名称
   * @param jobGroup 任务组(没有分组传值null)
   * @throws SchedulerException
   */
  public void runOnce(String jobName,String jobGroup) throws Exception{
    JobKey jobKey = JobKey.jobKey(jobName,ObjectUtils.isNotEmpty(jobGroup) ?jobGroup:null);
    scheduler.triggerJob(jobKey);
  }

  /**
   * 更新定时任务Simple
   * @param quartzBean 定时任务信息类
   * @throws SchedulerException
   */
  public void updateScheduleJobSimple(QuartzBean quartzBean) throws Exception {
    //获取到对应任务的触发器
    TriggerKey triggerKey = TriggerKey.triggerKey(quartzBean.getJobName(), ObjectUtils.isNotEmpty(quartzBean.getJobGroup()) ?quartzBean.getJobGroup():null);
    // 设置定时任务执行方式
    SimpleScheduleBuilder simpleScheduleBuilder = null;
    if (quartzBean.getInterval() == null) { //单次
      simpleScheduleBuilder = SimpleScheduleBuilder.simpleSchedule();
    } else { //循环
      simpleScheduleBuilder = SimpleScheduleBuilder.repeatMinutelyForever(quartzBean.getInterval());
    }
    // 构建触发器trigger
    Trigger trigger = null;
    if (quartzBean.getInterval() == null) { //单次
      trigger = TriggerBuilder.newTrigger()
          .withIdentity(quartzBean.getJobName(), ObjectUtils.isNotEmpty(quartzBean.getJobGroup()) ?quartzBean.getJobGroup():null)
          .withSchedule(simpleScheduleBuilder)
          .startAt(quartzBean.getStartTime())
          .build();
    } else { //循环
      TriggerBuilder.newTrigger()
          .withIdentity(quartzBean.getJobName(), ObjectUtils.isNotEmpty(quartzBean.getJobGroup()) ?quartzBean.getJobGroup():null)
          .withSchedule(simpleScheduleBuilder)
          .startAt(quartzBean.getStartTime())
          .endAt(quartzBean.getEndTime())
          .build();
    }
    //重置对应的job
    scheduler.rescheduleJob(triggerKey, trigger);
  }

  /**
   * 更新定时任务Cron
   * @param quartzBean 定时任务信息类
   * @throws SchedulerException
   */
  public void updateScheduleJobCron(QuartzBean quartzBean) throws Exception {
    //获取到对应任务的触发器
    TriggerKey triggerKey = TriggerKey.triggerKey(quartzBean.getJobName());
    //设置定时任务执行方式
    CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(quartzBean.getCronExpression());
    //重新构建任务的触发器trigger
    CronTrigger trigger = (CronTrigger) scheduler.getTrigger(triggerKey);
    trigger = trigger.getTriggerBuilder().withIdentity(triggerKey).withSchedule(scheduleBuilder).build();
    //重置对应的job
    scheduler.rescheduleJob(triggerKey, trigger);
  }

  /**
   * 根据定时任务名称从调度器当中删除定时任务
   * @param jobName  定时任务名称
   * @param jobGroup 任务组(没有分组传值null)
   * @throws SchedulerException
   */
  public void deleteScheduleJob(String jobName,String jobGroup) throws Exception {
    JobKey jobKey = JobKey.jobKey(jobName,ObjectUtils.isNotEmpty(jobGroup) ?jobGroup:null);
    scheduler.deleteJob(jobKey);
  }

  /**
   * 获取任务状态
   * @param jobName
   * @param jobGroup 任务组(没有分组传值null)
   * @return
   * (" BLOCKED ", " 阻塞 ");
   * ("COMPLETE", "完成");
   * ("ERROR", "出错");
   * ("NONE", "不存在");
   * ("NORMAL", "正常");
   * ("PAUSED", "暂停");
   */
  public String getScheduleJobStatus(String jobName,String jobGroup) throws Exception {
    TriggerKey triggerKey = TriggerKey.triggerKey(jobName,ObjectUtils.isNotEmpty(jobGroup) ?jobGroup:null);
    Trigger.TriggerState state = scheduler.getTriggerState(triggerKey);
    return state.name();
  }

  /**
   * 根据定时任务名称来判断任务是否存在
   * @param jobName  定时任务名称
   * @param jobGroup 任务组(没有分组传值null)
   * @throws SchedulerException
   */
  public Boolean checkExistsScheduleJob(String jobName,String jobGroup) throws Exception {
    JobKey jobKey = JobKey.jobKey(jobName,ObjectUtils.isNotEmpty(jobGroup) ?jobGroup:null);
    return scheduler.checkExists(jobKey);
  }

  /**
   * 根据任务組刪除定時任务
   * @param jobGroup 任务组
   * @throws SchedulerException
   */
  public Boolean deleteGroupJob(String jobGroup) throws Exception {
    GroupMatcher<JobKey> matcher = GroupMatcher.groupEquals(jobGroup);
    Set<JobKey> jobkeySet = scheduler.getJobKeys(matcher);
    List<JobKey> jobkeyList = new ArrayList<JobKey>();
    jobkeyList.addAll(jobkeySet);
    return scheduler.deleteJobs(jobkeyList);
  }

  /**
   * 根据任务組批量刪除定時任务
   * @param jobkeyList
   * @throws SchedulerException
   */
  public Boolean batchDeleteGroupJob(List<JobKey> jobkeyList) throws Exception {
    return scheduler.deleteJobs(jobkeyList);
  }

  /**
   * 根据任务組批量查询出jobkey
   * @param jobGroup 任务组
   * @throws SchedulerException
   */
  public void batchQueryGroupJob(List<JobKey> jobkeyList,String jobGroup) throws Exception {
    GroupMatcher<JobKey> matcher = GroupMatcher.groupEquals(jobGroup);
    Set<JobKey> jobkeySet = scheduler.getJobKeys(matcher);
    jobkeyList.addAll(jobkeySet);
  }
}

Job

package com.quartz.demo.job

import org.quartz.JobDataMap;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.quartz.JobKey;
import org.springframework.scheduling.quartz.QuartzJobBean;
import org.springframework.stereotype.Component;

import java.util.Date;

/**
 * @program: job
 * @description:
 * @author: Yuwl
 * @create: 2020-06-02 18:07
 **/
@Component
public class MyTask extends QuartzJobBean {
  @Override
  protected void executeInternal(JobExecutionContext context) throws JobExecutionException {
    JobKey jobKey = context.getJobDetail().getKey();
    JobDataMap map = context.getJobDetail().getJobDataMap();
    String userId = map.getString("userId");
    System.out.println("SimpleJob says: " + jobKey + ", userId: " + userId + " executing at " + new Date());
  }
}

Controller

import com.baomidou.mybatisplus.core.toolkit.ObjectUtils;
import com.ruoyi.common.utils.DateUtils;
import com.ruoyi.framework.quartz.QuartzBean;
import com.ruoyi.framework.quartz.service.QuartzJobService;
import org.quartz.JobDataMap;
import org.quartz.SchedulerException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.text.ParseException;
import java.util.Date;

/**
 * @program: JobController
 * @description:
 * @author: Yuwl
 * @create: 2020-07-21 17:08
 **/
@RestController
@RequestMapping("/api/quartz/")
public class JobController {

  @Autowired
  private QuartzJobService quartzJobService;

  //创建&启动
  @GetMapping("startSimpleJob")
  public String startSimpleJob() throws SchedulerException, ClassNotFoundException, ParseException {
    QuartzBean quartzBean = new QuartzBean();
    quartzBean.setJobClass("com.quartz.demo.job.MyTask");
    quartzBean.setJobName("job1");
    JobDataMap map = new JobDataMap();
    map.put("userId", "123456");
    quartzBean.setJobDataMap(map);
    Date now = new Date();
    quartzBean.setStartTime(DateUtils.addSeconds(now, 10));
    quartzBean.setInterval(10);
    quartzBean.setEndTime(DateUtils.addMinutes(now, 1));
    try {
      quartzJobService.createScheduleJobSimple(quartzBean);
    } catch (Exception e) {
      e.printStackTrace();
    }
    return "startJob Success!";
  }

  /**
   * 创建cron Job
   * @param quartzBean
   * @return
   */
  @RequestMapping("/createCronJob")
  @ResponseBody
  public String createJob(QuartzBean quartzBean) {
    try {
      //进行测试所以写死
      quartzBean.setJobClass("com.quartz.demo.job.MyTask");
      quartzBean.setJobName("job1");
      quartzBean.setCronExpression("*/5 * * * * ?");
      quartzJobService.createScheduleJobCron(quartzBean);
    } catch (Exception e) {
      return "创建失败";
    }
    return "创建成功";
  }

  /**
   * 暂停job
   * @return
   */
  @RequestMapping(value = {"/pauseJob/{jobName}","/pauseJob/{jobName}/{jobGroup}"})
  @ResponseBody
  public String pauseJob(@PathVariable("jobName") String jobName,@PathVariable(required = false) String jobGroup) {
    try {
    quartzJobService.pauseScheduleJob(jobName,ObjectUtils.isNotEmpty(jobGroup) ?jobGroup:null);
    } catch (Exception e) {
      return "暂停失败";
    }
    return "暂停成功";
  }

  @RequestMapping(value = {"/resume/{jobName}","/resume/{jobName}/{jobGroup}"})
  @ResponseBody
  public String resume(@PathVariable("jobName") String jobName,@PathVariable(required = false) String jobGroup) {
    try {
  quartzJobService.resumeScheduleJob(jobName,ObjectUtils.isNotEmpty(jobGroup) ?jobGroup:null);
    } catch (Exception e) {
      return "启动失败";
    }
    return "启动成功";
  }

  @RequestMapping(value = {"/delete/{jobName}","/delete/{jobName}/{jobGroup}"})
  public String delete(@PathVariable("jobName") String jobName,@PathVariable(required = false) String jobGroup) {
    try {
      quartzJobService.deleteScheduleJob(jobName,ObjectUtils.isNotEmpty(jobGroup) ?jobGroup:null);
    } catch (Exception e) {
      return "删除失败";
    }
    return "删除成功";
  }

  @RequestMapping(value = {"/check/{jobName}","/check/{jobName}/{jobGroup}"})
  public String check(@PathVariable("jobName") String jobName,@PathVariable(required = false) String jobGroup) {
    try {
      if(quartzJobService.checkExistsScheduleJob(jobName,ObjectUtils.isNotEmpty(jobGroup) ?jobGroup:null)){
        return "存在定时任务:"+jobName;
      }else{
        return "不存在定时任务:"+jobName;
      }
    } catch (Exception e) {
      return "查询任务失败";
    }
  }

  @RequestMapping(value = {"/status/{jobName}","/status/{jobName}/{jobGroup}"})
  @ResponseBody
  public String status(@PathVariable("jobName") String jobName,@PathVariable(required = false) String jobGroup) {
    try {
      return quartzJobService.getScheduleJobStatus(jobName,ObjectUtils.isNotEmpty(jobGroup) ?jobGroup:null);
    } catch (Exception e) {
      return "获取状态失败";
    }
    //return "获取状态成功";
  }

}

测试
http://localhost:8080/api/quartz/startSimpleJob

到此这篇关于Springboot2.x+Quartz分布式集群的实现的文章就介绍到这了,更多相关Springboot2.x Quartz分布式集群内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • SpringBoot与Quartz集成实现分布式定时任务集群的代码实例

    Spring Boot与Quartz集成实现分布式定时任务集群 直接贴代码 POM <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xs

  • SpringBoot开发案例 分布式集群共享Session详解

    前言 在分布式系统中,为了提升系统性能,通常会对单体项目进行拆分,分解成多个基于功能的微服务,如果有条件,可能还会对单个微服务进行水平扩展,保证服务高可用. 那么问题来了,如果使用传统管理 Session 的方式,我们会遇到什么样的问题? 案例 这里拿下单举例,用户小明在天猫上相中了一个的娃娃,觉得不错,果断购买,选尺寸,挑身高,然后确认选择,赶紧提交订单,然后就跳转到了登录页面!小明表示很郁闷,大写的问号??? 小明进入娃娃页面,此时请求通过代理服务发送到业务系统一. 小明选尺寸,挑身高,此操

  • Springboot2.x+Quartz分布式集群的实现

    生产环境一般都是多节点高可用,Springboot本身自带有定时任务功能,但我们项目需求要求能对定时任务进行增,删,改,查.所以考虑引进Quartz,引入Quartz就需要考虑分布式集群,所以就有了这篇文章. 数据库脚本 Quartz数据库有11张表,既支持Mysql,也支持Oracle Mysql /* Navicat MySQL Data Transfer Source Server : 10.19.34.3_ehr_admin Source Server Version : 50639 S

  • 详解使用docker搭建hadoop分布式集群

    使用Docker搭建部署Hadoop分布式集群 在网上找了很长时间都没有找到使用docker搭建hadoop分布式集群的文档,没办法,只能自己写一个了. 一:环境准备: 1:首先要有一个Centos7操作系统,可以在虚拟机中安装. 2:在centos7中安装docker,docker的版本为1.8.2 安装步骤如下: <1>安装制定版本的docker yum install -y docker-1.8.2-10.el7.centos <2>安装的时候可能会报错,需要删除这个依赖 r

  • 详解CentOS 6.5搭建Redis3.2.8单机分布式集群

    前言 最近在服务器上搭建了一套Redis3.0伪分布式集群,发现一个问题,就是Shell脚本编写能力和运维工具的重要性亟待提高. 集群环境安装 1.安装Redis $ cd /usr/local #安装目录 $ wget http://download.redis.io/releases/redis-3.2.8.tar.gz $ tar xzf redis-3.2.8.tar.gz $ mv redis-3.2.8/ redis $ cd redis $ make $ make install

  • Linux下Kafka分布式集群安装教程

    Kafka(http://kafka.apache.org/) 是由 LinkedIn 使用 Scala 编写的一个分布式消息系统,用作 LinkedIn 的活动流(Activity Stream)和运营数据处理管道(Pipeline)的基础,具有高水平扩展和高吞吐量.Spack.Elasticsearch 都支持与 Kafka 集成.下面看一下几种分布式开源消息队列系统的对比: Kafka 集群架构: 一般不建议直接使用 Kafka 自带的 Zookeeper 建立 zk 集群,这里我们使用独

  • Python搭建Spark分布式集群环境

    前言 Apache Spark 是一个新兴的大数据处理通用引擎,提供了分布式的内存抽象.Spark 最大的特点就是快,可比 Hadoop MapReduce 的处理速度快 100 倍.本文没有使用一台电脑上构建多个虚拟机的方法来模拟集群,而是使用三台电脑来搭建一个小型分布式集群环境安装. 本教程采用Spark2.0以上版本(比如Spark2.0.2.Spark2.1.0等)搭建集群,同样适用于搭建Spark1.6.2集群. 安装Hadoop并搭建好Hadoop集群环境 Spark分布式集群的安装

  • Linux下ZooKeeper分布式集群安装教程

    ZooKeeper 就是动物园管理员的意思,它是用来管理 Hadoop(大象).Hive(蜜蜂).pig(小猪)的管理员,Apache Hbase.Apache Solr.Dubbo 都用到了 ZooKeeper,其实就是一个集群管理工具,是集群的入口.ZooKeeper 是一个分布式的.开源的程序协调服务,是 Hadoop 项目下的一个子项目.ZooKeeper 主要应用场景包括集群管理(主从管理.负载均衡.高可用的管理).配置文件的集中管理.分布式锁.注册中心等.实际项目中,为了保证高可用,

  • Centos7.3 RabbitMQ分布式集群搭建示例

    本文介绍了Centos7.3 RabbitMQ分布式集群搭建示例,分享给大家,具体如下: 注意事项 centos 7.x 关闭firewall 三台机器: 172.17.250.97 rabbiMQ01 172.17.250.98 rabbiMQ03 172.17.250.99 rabbiMQ02 配置 hosts 172.17.250.97 fz-rabbitMQ01 172.17.250.99 fz-rabbitMQ02 172.17.250.98 fz-rabbitMQ03 $ syste

  • 详解SpringBoot2 使用Spring Session集群

    有几种办法: 1.扩展指定server 利用Servlet容器提供的插件功能,自定义HttpSession的创建和管理策略,并通过配置的方式替换掉默认的策略.缺点:耦合Tomcat/Jetty等Servlet容器,不能随意更换容器. 2.利用Filter 利用HttpServletRequestWrapper,实现自己的 getSession()方法,接管创建和管理Session数据的工作.spring-session就是通过这样的思路实现的. 3 利用spring session Spring

  • ol7.7安装部署4节点hadoop 3.2.1分布式集群学习环境的详细教程

    准备4台虚拟机,安装好ol7.7,分配固定ip192.168.168.11 12 13 14,其中192.168.168.11作为master,其他3个作为slave,主节点也同时作为namenode的同时也是datanode,192.168.168.14作为datanode的同时也作为secondary namenodes 首先修改/etc/hostname将主机名改为master.slave1.slave2.slave3 然后修改/etc/hosts文件添加 192.168.168.11 m

  • ol7.7安装部署4节点spark3.0.0分布式集群的详细教程

    为学习spark,虚拟机中开4台虚拟机安装spark3.0.0 底层hadoop集群已经安装好,见ol7.7安装部署4节点hadoop 3.2.1分布式集群学习环境 首先,去http://spark.apache.org/downloads.html下载对应安装包 解压 [hadoop@master ~]$ sudo tar -zxf spark-3.0.0-bin-without-hadoop.tgz -C /usr/local [hadoop@master ~]$ cd /usr/local

随机推荐