从传统开发服务走向持续集成

前言

从15年毕业离校,至17年初,一直在传统开发领域做开发,一个应用从前端后台还有app等集合成一块,然后发布到运维,整个一条线。17年元旦开始,开始负责深圳千万级别的项目的技术构架,从传统领域走向微服务开发,从整个业务流程的转换到整个开发流程的确认,从开发的流水线到自动集成发布,整个过程完全走产品化路线,集成化路线。

为了更好的做好讲解,本文将对校园问答平台的开发做一次技术性解析,从技术选型到发布的整个过程。

技术选型:

开发工具:JDK8、STS(Eclipse),Maven+Nexus;

主要技术栈:

服务注册与发现:Dubbo、Zookeeper;

缓存 : Redis

Http容器:Jetty

版本控制:SVN(淘宝svn)

异步消息:Kafka

前端:bootstrap3

持续集成平台:Jenkins

选型的技术应该常见,这是在某一角度上来说,并不是越新的技术越好,或者听起来越高大上的技术即是完美,具体按业务场景。

数据库设计

数据库使用mysql,单表设计,数据库脱离第三范式,将业务逻辑移至应用层,多冗余设计。

基本字段:

e_id   全局主键

e_env 全局环境

c_id 容器的id

t_id 商户的id

app_id 应用的id

全局字段的意思即全局数据库引用此数据,便于后期其它应用接入做扩展处理。

服务模块划分:

为了更接近实际设计场景,这里做了大概规划,

基础服务,

 

微服务开发的软件过程

在介绍这个过程之前,先强调一个观点:

  • 人管代码,代码管机器
  • 人管代码,代码管机器
  • 人管代码,代码管机器

 

一、软件过程

Jira原是设计来进行Bug跟踪的系统,后来系统功能逐步完善后,被广泛适用于软件过程管理。Jira优势在于简单,好用。 这里就不介绍Jira的具体使用。 使用Jira进行软件项目管理,首先需要定义任务的处理流程。 以下是一个参考流程:

01.png

在这个流程中,需要区分两个概念:任务和子任务。 每个任务对应一个完整的业务需求,比如对账、对接工行借记卡、获取个人优惠券列表接口。这些业务需求每个都是可以独立测试的。子任务设置相对比较简单,每个子任务对应这在本次任务执行中需要修改的开发项目。 比如对接工行借记卡,会涉及到:

  1. 支付网关项目调整;
  2. 支付路由项目中增加路由选项;
  3. 工行借记卡通道对接。

三个项目的修改,那会对应在这个任务下建立三个子任务。

  • 任务是用来追踪项目过程的,这是项目经理和产品经理关注的层次。
  • 子任务是用来支持开发自动化的,这是开发人员关注的层次。

这样,针对任务和子任务,会设置不同的属性:

02.png

 

1.1 需求管理

Jira也是一个不错的需求管理工具。产品经理可以通过Jira来执行需求管理,相对开发来说,需求管理流程会比较简单,一般是开发需求、审核需求、关闭需求三个环节即可。 需要注意的地方是:

  1. 需求管理流程需要和开发流程分离,毕竟这是不同的团队做的事情。
  2. 开发任务可以和需求任务相关联。Jira通过复制任务来提供这个支持。

一个需求任务可以对应多个开发任务,这在实际操作中是很常见的:

  1. 为了满足上线要求,一个需求任务会被拆分成多个开发任务,先完成核心功能开发并上线,再完成外围功能开发。这两次独立上线的工作,会被拆分为2个或者更多的开发任务;
  2. 如果对不同平台,比如Android、IOS、PCweb有不同的上线时间要求和技术需求,也需要将当前需求按照目标平台来拆分成开发任务。

 

1.2 创建任务

如上所述,开发任务的来源有两个:

  • 需求任务,即对应产品经理提的需求;
  • 优化任务,这一般是开发团队内部进行重构或者性能优化来提的开发任务。

那任务的粒度如何把握? 每个开发任务是一个完整的需求,是可以独立执行测试和验证的。 每个任务开发周期控制在1个月以内。

1.3 创建子任务

在接收到开发任务后,开发人员需要对系统实现进行设计和分解,确定需要新开发的内容以及需要改进的工作。 在微服务架构中,一次任务开发会涉及到多个系统的变更。这样就需要为每个系统建立一个独立的子任务,以后,我们将按照这个子任务的设置来驱动开发流程。 每个子任务开发周期尽量限制2天以内,不能超过一周。

1.4 启动主任务开发

主任务启动开发流程比较简单,主要是邮件通知到各相关人员,可以启动该任务。

1.5 启动子任务开发

子任务的启动和执行,是整个流程的核心工作。

03.png

这里如果是使用git/gitlab来做版本控制,整个流程的要点在于:

  1. 如果需要新建项目来开发,则由开发人员填写新项目的名称、类型(Web、RPC、工具类等),在Git上创建一个项目框架,包含必要的基础文件;
  2. 邮件通知开发人员需要下载的项目代码库地址;
  3. 开发人员签出代码到本地,执行开发工作;
  4. 开发人员随时可以签入代码到服务器上,发出Merge Request;
  5. GitLab在接受签入前,执行静态代码检查。静态代码检查的工具有FindBugs、PMD、Sonar等。 开发人员在开发时也必须自我进行静态检查,这里执行检查是避免开发人员漏查;
  6. 执行单元测试;
  7. 通知相关人员进行代码审核;
  8. 执行代码审核;
  9. 符合审核条件(如至少有2个人同意),审核通过, 代码被自动合并到主干版本。
  10. 通知子任务可以提测。 当然,是否提测,是由开发人员来决定。

 

1.6 子任务和任务提测

子任务开发完成后,即可提测。子任务提测时,将触发Jenkins进行测试环境部署。

测试有两种方式:自动测试和人工测试。尽量采用自动测试,使得开发人员能够及时发现问题。

所有子任务完成后,主任务可以提测。主任务提测后,如果是人工测试,则测试人员介入开始执行测试任务;如果是自动测试,则开始运行集成测试脚本。

04.png

测试通过后, 既可以准备上线。

1.7 预部署和全部署

一般上线会分为两步,预部署和全部署。预部署的目的是先验证系统在线上环境运行是否正常,减少回滚成本。特别是在部署服务器特别多的情况下,先部署1-2台机器,可以在线上验证本次上线是否可以。 验证通过后,既可以执行全部署。

注意,预部署和全部署都是针对子任务而言。

05.png

少数公司会要求上线前进行审批,但这样做是不利于流程自动化的。 一天几十次上线,谁能知道这是不是可以上。 但有一点很重要,系统上线前,必须通知到相关的使用方。如果出现问题,使用方可以尽快知悉。

二、项目文件结构

开发参考目录结构:

06.jpg

从这个目录里面我们可以看到,和项目相关的部署用脚本,需要由项目开发人员自己来维护,用以保证部署工作能够自动执行。包括验证项目部署成功的脚本。

验证项目是否部署成功,一种方式是在日志中打桩,grep到这个日志,即意味着系统成功启动;一种方式是调用接口来验证是否成功。

部署目录参考:

07.png

总之,微服务项目的管理核心理念在于“自动化”,消除人为因素。人管代码,代码管机器,最终目标是要实现自动上线。 消除人工测试,取代以自动化测试;消除人工验证,取代以自动验证;消除人工部署,取代以自动化部署。 这样,再多的项目,也能够很好的进行管理。

答案不规范的话术

答案不规范的话术:

1、答案不符合平台规范,请按规范提问,回答人员会按自己的经验分享给你,给你最好的经验。

如:民大校花是谁?

2、问题范围太广,背景略少,回答人员无法明确经验,建立百度得到更详细的答案。

如:如何独立?

3、重复提问,请在公众号搜索问题。

如:广西民族大学男女平台多少。

4、未明确指明学校,回答人员无法定位问题,无法给出个人经验,感谢对平台的支持。

如:为什么宿舍那么贵,却没有空调。

消息最终一致性处理分布式事务的理解

前言

消息最终一致性是用于处理分布式事务解决方案之一,其中主要保证的目的是为了消息的可靠性,最终一致性,而可靠消息并不可靠,由于其它未知原因(如网络、宕机等),依然可能会造成消息的中断,发不出去的情况,就需要人工介入处理或者有一定的处理机制,所以在一定角度上来说,可靠消息在某一程度上,只是最大保障了消息的最终一致性,以为称“消息最终一致性”会更为合理。

以下为分析消息最终一致性,其中按的思路为ACID,即事务的主要特性。

传统事务处理

举例一个场景,以CURD事务举例,一个事务中有添加、删除、修改三个操作,这里我们且称添加为A,删除为B,修改为C,设置A,B,C为同步操作,则一个事务表示为(序号表示执行顺序):

在这一事务中,可以用传统的处理方案处理,保证事务的一致性。

分布式事务处理

由于业务的发展,业务独立在一定程度上更好有效的解决了耦合,达到可扩展,组件化,高性能等,在服务化之后,我们继续使用上面的A、B、C来说明,假设已经分为A服务,B服务,C服务,实际的业务场景则为:

为了保证ACID,在A完成之后,一定要操作B服务,在B完成之后,一定要操作C,但实际中,网络、应用稳定性、异步调用等的不确定性,容易造成事务不统一,如A完成,在调用B时,由于网络或者其它原因,B并没有完成业务操作,同样,C 也没有完成,如下图:

以上的情况,这个事务并不统一,服务是独立的,要实现A回滚的操作,需要做更多的额外工作来处理。所以,为了确定B一定执行,在已完成的事务(如A)需要重复去调用或者有一种验证机制去确定B一定执行。

以上只是一种情况,可能出现多种情况。总之,打破了事务的ACID原则,而我们要做的就是尽最大努力确保这个ACID原则,即事务性。消息最终一致性的方案,为保持这个原则,做了一个保障方案。

消息最终一致性方案

注:可靠消息是一种基于消息中间件的包装,确保消息一致性的服务或者组件。

此方案中引入了可靠消息做为媒介,如图(序号表示执行顺序):

在A完成之后,会一定去调用B服务,B服务一定会去调用C服务,确保一定会完成ACID的操作。

如果说事务失败,即在A失败,这部分可以在A服务内自己控制事务,包括异常处理等,就不会去调用B,此时,整个事务就不会进行。

回到之前的事务讨论

前面提过,添加为A,删除为B,修改为C,其中A,B成功,C操作失败,要不要做A、B回滚的处理?这个之前很多同事都有提问到。事务具有ACID特性,即整个事务来说,对消费方来说是透明的,要么成功,要么失败,A、B属于事务中的一部分,不会有回滚的操作,因为它会确保C一定去执行。

如果需要添加回滚,在程序上处理可实现,考虑到开发成本上,不太建议。如有更好的办法,也希望学习。

可靠消息对业务有一定的入侵性,并不能完全解耦,如在做幂等处理时,需要跟具体业务结合判断。

总结

针对以上可靠消息的特性及使用场景,在划分服务颗粒度上需按实际业务场景划分,以最简单的成本,解决最实际的问题。

 

任务调度开源框架Quartz动态添加、修改和删除定时任务

Quartz 是个开源的作业调度框架,为在 Java 应用程序中进行作业调度提供了简单却强大的机制。Quartz框架包含了调度器监听、作业和触发器监听。你可以配置作业和触发器监听为全局监听或者是特定于作业和触发器的监听。Quartz 允许开发人员根据时间间隔(或天)来调度作业。它实现了作业和触发器的多对多关系,还能把多个作业与不同的触发器关联。整合了 Quartz 的应用程序可以重用来自不同事件的作业,还可以为一个事件组合多个作业。并且还能和spring配置整合使用。Quartz在功能上远远超越了JDK自带的Timer,很好很强大!好啦,直接上代码:

package com.zoki.module.timer;
import ch.qos.logback.classic.Logger;
import java.util.Date;
import java.util.Map;
import java.util.Map.Entry;
import java.util.concurrent.ConcurrentHashMap;
import org.quartz.CronScheduleBuilder;
import org.quartz.CronTrigger;
import org.quartz.JobBuilder;
import org.quartz.JobDetail;
import org.quartz.JobKey;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.quartz.SchedulerFactory;
import org.quartz.SimpleScheduleBuilder;
import org.quartz.Trigger;
import org.quartz.TriggerBuilder;
import org.quartz.TriggerKey;
import org.quartz.impl.StdSchedulerFactory;
import org.slf4j.LoggerFactory;
/**
 * 定时器任务调度管理器
 *
 * @author zhoukai
 */
public class QuartzManager {
    private final String jobGroupName;
    private final String triggerGroupName;
    private Scheduler sched;
    private final static SchedulerFactory sf = new StdSchedulerFactory();
    private final static Map<String, QuartzManager> instanceMap = new ConcurrentHashMap<>();
    private final static Logger logger = (Logger) LoggerFactory.getLogger(QuartzManager.class);
    /**
     * 构造方法,外部不能实例化
     *
     * @param jobGroupName 工作组名
     * @param triggerGroupName 触发器组名
     */
    private QuartzManager(String jobGroupName, String triggerGroupName) {
        this.jobGroupName = jobGroupName;
        this.triggerGroupName = triggerGroupName;
    }
    /**
     * 获取调度器
     *
     * @return 定时调度器
     * @throws SchedulerException 调度器内部执行异常
     */
    private Scheduler getScheduler() throws SchedulerException {
        if (sched == null) {
            sched = sf.getScheduler();
            //sched.getListenerManager().addTriggerListener(new QuartzTriggerListener());
            //sched.getListenerManager().addSchedulerListener(new QuartzSchedulerListener(sched));
        }
        return sched;
    }
    /**
     * 获取一个定时调度管理器
     *
     * @param jobGroupName 工作组名
     * @param triggerGroupName 触发器组名
     * @return 定时调度管理器
     */
    public static QuartzManager getInstance(String jobGroupName, String triggerGroupName) {
        String instanceKey = jobGroupName + "_" + triggerGroupName;
        if (instanceMap.containsKey(instanceKey)) {
            return instanceMap.get(instanceKey);
        }
        QuartzManager manager = new QuartzManager(jobGroupName, triggerGroupName);
        instanceMap.put(instanceKey, manager);
        return manager;
    }
    /**
     * 获取一个定时调度管理器
     *
     * @param groupName 工作组名和触发器组名相同
     * @return 定时调度管理器
     */
    public static QuartzManager getInstance(String groupName) {
        return getInstance(groupName, groupName);
    }
    /**
     * 关闭调度器
     *
     * @param waitForJobsToComplete 是否等候所有工作被调度完成后再关闭
     */
    public void shutdown(boolean waitForJobsToComplete) {
        String instanceKey = jobGroupName + "_" + triggerGroupName;
        if (instanceMap.containsKey(instanceKey)) {
            QuartzManager manager = instanceMap.get(instanceKey);
            if (manager != null && manager.sched != null) {
                try {
                    manager.sched.shutdown(waitForJobsToComplete);
                } catch (SchedulerException ex) {
                    logger.debug(ex.getLocalizedMessage(), ex);
                }
            }
        }
    }
    /**
     * 关闭调度器
     */
    public void shutdown() {
        shutdown(false);
    }
    
    /**
     * 添加一个调度任务
     *
     * @param jobName 任务名
     * @param cls 任务类型
     * @param start 任务执行的开始时间,null表示立刻执行
     * @param end 任务执行的结束时间,null表示任务在执行repeat次数后自动结束
     * @param jobBindData 绑定到job中的数据,可为null
     * @param intervalType 任务执行时间间隔类型,1、毫秒,2、秒,3、分,4、小时,默认为2
     * @param intervalValue 任务执行时间间隔
     * @param repeat 任务执行次数,0表示执行一次,实际执行次数为此值+1
     * @throws SchedulerException
     */
    public void addJob(String jobName, Class cls, Date start, Date end, Map<String, Object> jobBindData, int intervalType, long intervalValue, int repeat) throws SchedulerException {
        JobDetail jobDetail = JobBuilder.newJob(cls).withIdentity(jobName, jobGroupName).build();
        if (jobBindData != null) {
            for (Entry<String, Object> entry : jobBindData.entrySet()) {
                jobDetail.getJobDataMap().put(entry.getKey(), entry.getValue());
            }
        }
        SimpleScheduleBuilder simpleScheduleBuilder = SimpleScheduleBuilder.simpleSchedule();
        switch (intervalType) {
            case 1:
                //毫秒
                simpleScheduleBuilder.withIntervalInMilliseconds(intervalValue);
                break;
            case 3:
                //分
                simpleScheduleBuilder.withIntervalInMinutes((int) intervalValue);
                break;
            case 4:
                //时
                simpleScheduleBuilder.withIntervalInHours((int) intervalValue);
                break;
            default:
                //秒
                simpleScheduleBuilder.withIntervalInSeconds((int) intervalValue);
                break;
        }
        if(repeat >= 0) {
            simpleScheduleBuilder.withRepeatCount(repeat);
        }
        TriggerBuilder<Trigger> builder = TriggerBuilder.newTrigger()
                .withIdentity(jobName, triggerGroupName);
        if (start == null) {
            builder.startNow();
        } else {
            builder.startAt(start);
        }
        if (end != null) {
            builder.endAt(end);
        }
        Trigger trigger = builder.withSchedule(simpleScheduleBuilder).build();
        Scheduler scheduler = getScheduler();
        scheduler.scheduleJob(jobDetail, trigger);
        if (!scheduler.isShutdown()) {
            scheduler.start();
        }
    }
    
    /**
     * 添加一个调度任务,在start时间执行一次
     * @param jobName 任务名
     * @param cls 任务类型
     * @param start 任务执行的开始时间,null表示立刻执行
     * @param end 任务执行的结束时间,null表示任务在执行repeat次数后自动结束
     * @param jobBindData 绑定到job中的数据,可为null
     * @throws SchedulerException
     */
    public void addJob(String jobName, Class cls, Date start , Date end, Map<String, Object> jobBindData) throws SchedulerException {
        this.addJob(jobName, cls, start, end, jobBindData, 2, 1, 0);
    }
    
    /**
     * 添加一个调度任务,在start时间执行一次
     * @param jobName 任务名
     * @param cls 任务类型
     * @param start 任务执行的开始时间,null表示立刻执行
     * @param jobBindData 绑定到job中的数据,可为null
     * @throws SchedulerException
     */
    public void addJob(String jobName, Class cls, Date start, Map<String, Object> jobBindData) throws SchedulerException {
        this.addJob(jobName, cls, start, null, jobBindData);
    }
    /**
     * 添加一个定时任务,使用默认的任务组名,触发器名,触发器组名
     *
     * @param jobName 任务名
     * @param cls
     * @param time 时间设置,参考quartz说明文档
     * @throws SchedulerException 调度器内部执行异常
     */
    public void addJob(String jobName, Class cls, String time) throws SchedulerException {
        JobDetail jobDetail = JobBuilder.newJob(cls).withIdentity(jobName, jobGroupName).build();
        CronTrigger trigger = TriggerBuilder.newTrigger()
                .withIdentity(jobName, triggerGroupName)//触发器名,触发器组
                .withSchedule(CronScheduleBuilder.cronSchedule(time))
                .build();
        Scheduler scheduler = getScheduler();
        scheduler.scheduleJob(jobDetail, trigger);
        if (!scheduler.isShutdown()) {
            scheduler.start();
        }
    }
    /**
     * 移除一个任务
     *
     * @param triggerKey 触发器键
     * @param jobKey 工作任务键
     * @throws SchedulerException 调度器内部执行异常
     */
    public void removeJob(TriggerKey triggerKey, JobKey jobKey) throws SchedulerException {
        Scheduler scheduler = getScheduler();
        scheduler.pauseTrigger(triggerKey);
        scheduler.unscheduleJob(triggerKey);
        scheduler.deleteJob(jobKey);
    }
    /**
     * 移除一个任务(使用默认的任务组名,触发器名,触发器组名)
     *
     * @param jobName 工作任务名
     * @throws SchedulerException 调度器内部执行异常
     */
    public void removeJob(String jobName) throws SchedulerException {
        TriggerKey triggerKey = new TriggerKey(jobName, triggerGroupName);
        JobKey jobKey = new JobKey(jobName, jobGroupName);
        removeJob(triggerKey, jobKey);
    }
}

[java] view plain copy

在CODE上查看代码片
  1. /**
  2.  * @Description: 
  3.  *
  4.  * @Title: QuartzManager.java
  5.  * @Package com.joyce.quartz
  6.  * @Copyright: Copyright (c) 2014
  7.  *
  8.  * @author Comsys-LZP
  9.  * @date 2014-6-26 下午03:15:52
  10.  * @version V2.0
  11.  */
  12. package com.joyce.quartz;
  13. import org.quartz.CronTrigger;
  14. import org.quartz.JobDetail;
  15. import org.quartz.Scheduler;
  16. import org.quartz.SchedulerFactory;
  17. import org.quartz.impl.StdSchedulerFactory;
  18. /**
  19.  * @Description: 定时任务管理类
  20.  * 
  21.  * @ClassName: QuartzManager
  22.  * @Copyright: Copyright (c) 2014
  23.  * 
  24.  * @author Comsys-LZP
  25.  * @date 2014-6-26 下午03:15:52
  26.  * @version V2.0
  27.  */
  28. public class QuartzManager {
  29.     private static SchedulerFactory gSchedulerFactory = new StdSchedulerFactory();
  30.     private static String JOB_GROUP_NAME = “EXTJWEB_JOBGROUP_NAME”;
  31.     private static String TRIGGER_GROUP_NAME = “EXTJWEB_TRIGGERGROUP_NAME”;
  32.     /**
  33.      * @Description: 添加一个定时任务,使用默认的任务组名,触发器名,触发器组名
  34.      * 
  35.      * @param jobName
  36.      *            任务名
  37.      * @param cls
  38.      *            任务
  39.      * @param time
  40.      *            时间设置,参考quartz说明文档
  41.      * 
  42.      * @Title: QuartzManager.java
  43.      * @Copyright: Copyright (c) 2014
  44.      * 
  45.      * @author Comsys-LZP
  46.      * @date 2014-6-26 下午03:47:44
  47.      * @version V2.0
  48.      */
  49.     @SuppressWarnings(“unchecked”)
  50.     public static void addJob(String jobName, Class cls, String time) {
  51.         try {
  52.             Scheduler sched = gSchedulerFactory.getScheduler();
  53.             JobDetail jobDetail = new JobDetail(jobName, JOB_GROUP_NAME, cls);// 任务名,任务组,任务执行类
  54.             // 触发器
  55.             CronTrigger trigger = new CronTrigger(jobName, TRIGGER_GROUP_NAME);// 触发器名,触发器组
  56.             trigger.setCronExpression(time);// 触发器时间设定
  57.             sched.scheduleJob(jobDetail, trigger);
  58.             // 启动
  59.             if (!sched.isShutdown()) {
  60.                 sched.start();
  61.             }
  62.         } catch (Exception e) {
  63.             throw new RuntimeException(e);
  64.         }
  65.     }
  66.     /**
  67.      * @Description: 添加一个定时任务
  68.      * 
  69.      * @param jobName
  70.      *            任务名
  71.      * @param jobGroupName
  72.      *            任务组名
  73.      * @param triggerName
  74.      *            触发器名
  75.      * @param triggerGroupName
  76.      *            触发器组名
  77.      * @param jobClass
  78.      *            任务
  79.      * @param time
  80.      *            时间设置,参考quartz说明文档
  81.      * 
  82.      * @Title: QuartzManager.java
  83.      * @Copyright: Copyright (c) 2014
  84.      * 
  85.      * @author Comsys-LZP
  86.      * @date 2014-6-26 下午03:48:15
  87.      * @version V2.0
  88.      */
  89.     @SuppressWarnings(“unchecked”)
  90.     public static void addJob(String jobName, String jobGroupName,
  91.             String triggerName, String triggerGroupName, Class jobClass,
  92.             String time) {
  93.         try {
  94.             Scheduler sched = gSchedulerFactory.getScheduler();
  95.             JobDetail jobDetail = new JobDetail(jobName, jobGroupName, jobClass);// 任务名,任务组,任务执行类
  96.             // 触发器
  97.             CronTrigger trigger = new CronTrigger(triggerName, triggerGroupName);// 触发器名,触发器组
  98.             trigger.setCronExpression(time);// 触发器时间设定
  99.             sched.scheduleJob(jobDetail, trigger);
  100.         } catch (Exception e) {
  101.             throw new RuntimeException(e);
  102.         }
  103.     }
  104.     /**
  105.      * @Description: 修改一个任务的触发时间(使用默认的任务组名,触发器名,触发器组名)
  106.      * 
  107.      * @param jobName
  108.      * @param time
  109.      * 
  110.      * @Title: QuartzManager.java
  111.      * @Copyright: Copyright (c) 2014
  112.      * 
  113.      * @author Comsys-LZP
  114.      * @date 2014-6-26 下午03:49:21
  115.      * @version V2.0
  116.      */
  117.     @SuppressWarnings(“unchecked”)
  118.     public static void modifyJobTime(String jobName, String time) {
  119.         try {
  120.             Scheduler sched = gSchedulerFactory.getScheduler();
  121.             CronTrigger trigger = (CronTrigger) sched.getTrigger(jobName,TRIGGER_GROUP_NAME);
  122.             if (trigger == null) {
  123.                 return;
  124.             }
  125.             String oldTime = trigger.getCronExpression();
  126.             if (!oldTime.equalsIgnoreCase(time)) {
  127.                 JobDetail jobDetail = sched.getJobDetail(jobName,JOB_GROUP_NAME);
  128.                 Class objJobClass = jobDetail.getJobClass();
  129.                 removeJob(jobName);
  130.                 addJob(jobName, objJobClass, time);
  131.             }
  132.         } catch (Exception e) {
  133.             throw new RuntimeException(e);
  134.         }
  135.     }
  136.     /**
  137.      * @Description: 修改一个任务的触发时间
  138.      * 
  139.      * @param triggerName
  140.      * @param triggerGroupName
  141.      * @param time
  142.      * 
  143.      * @Title: QuartzManager.java
  144.      * @Copyright: Copyright (c) 2014
  145.      * 
  146.      * @author Comsys-LZP
  147.      * @date 2014-6-26 下午03:49:37
  148.      * @version V2.0
  149.      */
  150.     public static void modifyJobTime(String triggerName,
  151.             String triggerGroupName, String time) {
  152.         try {
  153.             Scheduler sched = gSchedulerFactory.getScheduler();
  154.             CronTrigger trigger = (CronTrigger) sched.getTrigger(triggerName,triggerGroupName);
  155.             if (trigger == null) {
  156.                 return;
  157.             }
  158.             String oldTime = trigger.getCronExpression();
  159.             if (!oldTime.equalsIgnoreCase(time)) {
  160.                 CronTrigger ct = (CronTrigger) trigger;
  161.                 // 修改时间
  162.                 ct.setCronExpression(time);
  163.                 // 重启触发器
  164.                 sched.resumeTrigger(triggerName, triggerGroupName);
  165.             }
  166.         } catch (Exception e) {
  167.             throw new RuntimeException(e);
  168.         }
  169.     }
  170.     /**
  171.      * @Description: 移除一个任务(使用默认的任务组名,触发器名,触发器组名)
  172.      * 
  173.      * @param jobName
  174.      * 
  175.      * @Title: QuartzManager.java
  176.      * @Copyright: Copyright (c) 2014
  177.      * 
  178.      * @author Comsys-LZP
  179.      * @date 2014-6-26 下午03:49:51
  180.      * @version V2.0
  181.      */
  182.     public static void removeJob(String jobName) {
  183.         try {
  184.             Scheduler sched = gSchedulerFactory.getScheduler();
  185.             sched.pauseTrigger(jobName, TRIGGER_GROUP_NAME);// 停止触发器
  186.             sched.unscheduleJob(jobName, TRIGGER_GROUP_NAME);// 移除触发器
  187.             sched.deleteJob(jobName, JOB_GROUP_NAME);// 删除任务
  188.         } catch (Exception e) {
  189.             throw new RuntimeException(e);
  190.         }
  191.     }
  192.     /**
  193.      * @Description: 移除一个任务
  194.      * 
  195.      * @param jobName
  196.      * @param jobGroupName
  197.      * @param triggerName
  198.      * @param triggerGroupName
  199.      * 
  200.      * @Title: QuartzManager.java
  201.      * @Copyright: Copyright (c) 2014
  202.      * 
  203.      * @author Comsys-LZP
  204.      * @date 2014-6-26 下午03:50:01
  205.      * @version V2.0
  206.      */
  207.     public static void removeJob(String jobName, String jobGroupName,
  208.             String triggerName, String triggerGroupName) {
  209.         try {
  210.             Scheduler sched = gSchedulerFactory.getScheduler();
  211.             sched.pauseTrigger(triggerName, triggerGroupName);// 停止触发器
  212.             sched.unscheduleJob(triggerName, triggerGroupName);// 移除触发器
  213.             sched.deleteJob(jobName, jobGroupName);// 删除任务
  214.         } catch (Exception e) {
  215.             throw new RuntimeException(e);
  216.         }
  217.     }
  218.     /**
  219.      * @Description:启动所有定时任务
  220.      * 
  221.      * 
  222.      * @Title: QuartzManager.java
  223.      * @Copyright: Copyright (c) 2014
  224.      * 
  225.      * @author Comsys-LZP
  226.      * @date 2014-6-26 下午03:50:18
  227.      * @version V2.0
  228.      */
  229.     public static void startJobs() {
  230.         try {
  231.             Scheduler sched = gSchedulerFactory.getScheduler();
  232.             sched.start();
  233.         } catch (Exception e) {
  234.             throw new RuntimeException(e);
  235.         }
  236.     }
  237.     /**
  238.      * @Description:关闭所有定时任务
  239.      * 
  240.      * 
  241.      * @Title: QuartzManager.java
  242.      * @Copyright: Copyright (c) 2014
  243.      * 
  244.      * @author Comsys-LZP
  245.      * @date 2014-6-26 下午03:50:26
  246.      * @version V2.0
  247.      */
  248.     public static void shutdownJobs() {
  249.         try {
  250.             Scheduler sched = gSchedulerFactory.getScheduler();
  251.             if (!sched.isShutdown()) {
  252.                 sched.shutdown();
  253.             }
  254.         } catch (Exception e) {
  255.             throw new RuntimeException(e);
  256.         }
  257.     }
  258. }

以上就是quartz任务调度对于任务的常用操作,封装起来以便在外部调用!这样我们就需要任务的执行了:

[java] view plain copy

在CODE上查看代码片
  1. /**
  2.  * @Description: 
  3.  *
  4.  * @Title: QuartzJob.java
  5.  * @Package com.joyce.quartz
  6.  * @Copyright: Copyright (c) 2014
  7.  *
  8.  * @author Comsys-LZP
  9.  * @date 2014-6-26 下午03:37:11
  10.  * @version V2.0
  11.  */
  12. package com.joyce.quartz;
  13. import java.text.SimpleDateFormat;
  14. import java.util.Date;
  15. import org.quartz.Job;
  16. import org.quartz.JobExecutionContext;
  17. import org.quartz.JobExecutionException;
  18. /**
  19.  * @Description: 任务执行类
  20.  *
  21.  * @ClassName: QuartzJob
  22.  * @Copyright: Copyright (c) 2014
  23.  *
  24.  * @author Comsys-LZP
  25.  * @date 2014-6-26 下午03:37:11
  26.  * @version V2.0
  27.  */
  28. public class QuartzJob implements Job {
  29.     @Override
  30.     public void execute(JobExecutionContext arg0) throws JobExecutionException {
  31.         System.out.println(new SimpleDateFormat(“yyyy-MM-dd HH:mm:ss”).format(new Date())+ “★★★★★★★★★★★”);
  32.     }
  33. }

Ok,我们来测试一下:

[java] view plain copy

在CODE上查看代码片派生到我的代码片

  1. /**
  2.  * @Description: 
  3.  *
  4.  * @Title: QuartzTest.java
  5.  * @Package com.joyce.quartz.main
  6.  * @Copyright: Copyright (c) 2014
  7.  *
  8.  * @author Comsys-LZP
  9.  * @date 2014-6-26 下午03:35:05
  10.  * @version V2.0
  11.  */
  12. package com.joyce.quartz.main;
  13. import com.joyce.quartz.QuartzJob;
  14. import com.joyce.quartz.QuartzManager;
  15. /**
  16.  * @Description: 测试类
  17.  *
  18.  * @ClassName: QuartzTest
  19.  * @Copyright: Copyright (c) 2014
  20.  *
  21.  * @author Comsys-LZP
  22.  * @date 2014-6-26 下午03:35:05
  23.  * @version V2.0
  24.  */
  25. public class QuartzTest {
  26.     public static void main(String[] args) {
  27.         try {
  28.             String job_name = “动态任务调度”;
  29.             System.out.println(“【系统启动】开始(每1秒输出一次)…”);
  30.             QuartzManager.addJob(job_name, QuartzJob.class“0/1 * * * * ?”);
  31.             Thread.sleep(5000);
  32.             System.out.println(“【修改时间】开始(每2秒输出一次)…”);
  33.             QuartzManager.modifyJobTime(job_name, “10/2 * * * * ?”);
  34.             Thread.sleep(6000);
  35.             System.out.println(“【移除定时】开始…”);
  36.             QuartzManager.removeJob(job_name);
  37.             System.out.println(“【移除定时】成功”);
  38.             System.out.println(“【再次添加定时任务】开始(每10秒输出一次)…”);
  39.             QuartzManager.addJob(job_name, QuartzJob.class“*/10 * * * * ?”);
  40.             Thread.sleep(60000);
  41.             System.out.println(“【移除定时】开始…”);
  42.             QuartzManager.removeJob(job_name);
  43.             System.out.println(“【移除定时】成功”);
  44.         } catch (Exception e) {
  45.             e.printStackTrace();
  46.         }
  47.     }
  48. }

运行一下,看看效果图: