精华内容
下载资源
问答
  • 环信无需对方同意直接添加好友
    千次阅读
    2019-10-09 18:03:44

    一、预期功能

    环信web版(或h5版)实现添加好友功能,需直接添加好友而不用确认通过,能够实现直接添加好友然后开始聊天。

    二、主要思路

    查询了环信官方给出的开发文档发现不支持这种操作,但在环信控制台界面,管理员可以直接让两个用户建立好友关系,因此考虑使用环信已有接口实现。

    研究发现添加好友的接口要通过Bearer验证,也就是说要在登陆成功时获取到通过验证的token信息,然后就可以拿着token发请求将两个用户建立好友关系。

    graph LR
    模拟登陆-->获取到token
    获取到token-->建立好友关系
    建立好友关系-->开始聊天
    

    经过测试 已经添加为好友的两个用户重复添加不会报错

    三、代码实现

    以下代码中需要用到的参数包含:

    • 环信控制台登陆用户名
    • 环信控制台登陆密码
    • Orgname
    • appname
    • 当前环信用户用户名
    • 待添加环信用户用户名

    Orgnameappname可以在环信控制台->IM->我的应用->具体任务详情(点击某一任务可查看)中找到。

    具体实现代码如下:

    let currentUser = 'admin1' // 当前用户
    let anotherUser = 'admin2' // 被添加用户
    
    const getIMOperateToken = e => {
      let xhr = new XMLHttpRequest()
      let obj = {
        grant_type: "password",
        password: "", // 环信控制台登陆密码
        username: "" // 环信控制台登陆用户名
      }
    
      xhr.open("post", `https://a1.easemob.com/management/token?t=${+new Date}`, true)
    
      xhr.onload = () => {
        addUser(JSON.parse(xhr.responseText).access_token)
      }
    
      xhr.send(JSON.stringify(obj));
    }
    
    const addUser = token => {
    
      let xhr = new XMLHttpRequest()
      xhr.open("post", `https://a1.easemob.com/1123190718010879/stevendemoapp/users/${currentUser}/contacts/users/${anotherUser}?t=${+new Date}`, true)
      xhr.setRequestHeader("Authorization", `Bearer ${token}`)
    
      xhr.onload = () => {
        // 判断好友添加成功后可直接开始聊天
      }
    
      xhr.send();
    }
    
    

    END

    更多相关内容
  • 通常,您应该允许系统定义任务和 Activity 在概览屏幕中的显示方法,并且无需修改此行为。不过,应用可以确定 Activity 在概览屏幕中的显示方式和时间。 您可以 使用 ActivityManager.AppTask 类来管理任务,使用 ...

    Overview Screen(概览屏幕)简介

    概览屏幕(也称为最新动态屏幕、最近任务列表或最近使用的应用)是一个系统级别 UI,其中列出了最近访问过的Activity和任务。用户可以浏览该列表并选择要恢复的任务,也可以通过滑动清除任务将其从列表中删除。 对于 Android 5.0 版本(API 级别 21),包含多个文档的同一 Activity 的多个实例可能会以任务的形式显示在概览屏幕中。例如,Google Drive 可能对多个 Google 文档中的每个文档均执行一个任务。每个文档均以任务的形式显示在概览屏幕中。



    图 1. 显示了三个 Google Drive 文档的概览屏幕,每个文档分别以一个单独的任务表示。

    通常,您应该允许系统定义任务和 Activity 在概览屏幕中的显示方法,并且无需修改此行为。不过,应用可以确定 Activity 在概览屏幕中的显示方式和时间。 您可以使用 ActivityManager.AppTask 类来管理任务,使用 Intent 类的 Activity 标志来指定某 Activity 添加到概览屏幕或从中删除的时间。此外,您也可以使用 <activity> 属性在清单文件中设置该行为。

    Add tasks to the overview screen(将任务添加到概览屏幕)

    通过使用 Intent 类的标志添加任务,您可以更好地控制某文档在概览屏幕中打开或重新打开的时间和方式。使用 <activity> 属性时,您可以选择始终在新任务中打开文档,或选择对文档重复使用现有任务。

    1.使用 Intent 标志添加任务

    为 Activity 创建新文档时,可调用 ActivityManager.AppTask 类的 startActivity() 方法,以向其传递启动 Activity 的 Intent。要插入逻辑换行符以便系统将 Activity 视为新任务显示在概览屏幕中,可在启动 Activity 的 Intent 的 addFlags() 方法中传递FLAG_ACTIVITY_NEW_DOCUMENT标志。

    注:FLAG_ACTIVITY_NEW_DOCUMENT 标志取代了 FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET 标志,后者自 Android 5.0(API 级别 21)起已弃用。

    如果在创建新文档时设置 FLAG_ACTIVITY_MULTIPLE_TASK 标志,则系统始终会以目标 Activity 作为根创建新任务。此设置允许同一文档在多个任务中打开。以下代码演示了主 Activity 如何执行此操作:

    DocumentCentricActivity.java
    public void createNewDocument(View view) {
          final Intent newDocumentIntent = newDocumentIntent();
          if (useMultipleTasks) {
              newDocumentIntent.addFlags(Intent.FLAG_ACTIVITY_MULTIPLE_TASK);
          }
          startActivity(newDocumentIntent);
      }
    
      private Intent newDocumentIntent() {
          boolean useMultipleTasks = mCheckbox.isChecked();
          final Intent newDocumentIntent = new Intent(this, NewDocumentActivity.class);
          newDocumentIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_DOCUMENT);
          newDocumentIntent.putExtra(KEY_EXTRA_NEW_DOCUMENT_COUNTER, incrementAndGet());
          return newDocumentIntent;
      }
    
      private static int incrementAndGet() {
          Log.d(TAG, "incrementAndGet(): " + mDocumentCounter);
          return mDocumentCounter++;
      }
    }

    注:使用 FLAG_ACTIVITY_NEW_DOCUMENT 标志启动的 Activity 必须具有在清单文件中设置的android:launchMode="standard" 属性值(默认)。

    当主 Activity 启动新 Activity 时,系统会搜遍现有任务,看看是否有任务的 Intent 与 Activity 的 Intent 组件名称和 Intent 数据相匹配。 如果未找到任务或者 Intent 包含 FLAG_ACTIVITY_MULTIPLE_TASK 标志,则会以该 Activity 作为其根创建新任务。如果找到的话,则会将该任务转到前台并将新 Intent 传递给 onNewIntent()。新 Activity 将获得 Intent 并在概览屏幕中创建新文档,如下例所示:

    NewDocumentActivity.java:
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_new_document);
        mDocumentCount = getIntent()
                .getIntExtra(DocumentCentricActivity.KEY_EXTRA_NEW_DOCUMENT_COUNTER, 0);
        mDocumentCounterTextView = (TextView) findViewById(
                R.id.hello_new_document_text_view);
        setDocumentCounterText(R.string.hello_new_document_counter);
    }
    
    @Override
    protected void onNewIntent(Intent intent) {
        super.onNewIntent(intent);
        /* If FLAG_ACTIVITY_MULTIPLE_TASK has not been used, this activity
        is reused to create a new document.
         */
        setDocumentCounterText(R.string.reusing_document_counter);
    }
    

    2.使用 Activity 属性添加任务

    此外,Activity 还可以在其清单文件中指定始终通过使用 <activity> 属性 android:documentLaunchMode 进入新任务。 此属性有四个值,会在用户使用该应用打开文档时产生以下效果:

    (1)“intoExisting”
    该 Activity 会对文档重复使用现有任务。这与不设置 FLAG_ACTIVITY_MULTIPLE_TASK 标志、但设置FLAG_ACTIVITY_NEW_DOCUMENT 标志所产生的效果相同,如上文的使用 Intent 标志添加任务中所述。

    (2)“always”
    该 Activity 为文档创建新任务,即便文档已打开也是如此。使用此值与同时设置 FLAG_ACTIVITY_NEW_DOCUMENT 和 FLAG_ACTIVITY_MULTIPLE_TASK 标志所产生的效果相同。

    (3)“none”
    该 Activity 不会为文档创建新任务。概览屏幕将按其默认方式对待此 Activity:为应用显示单个任务,该任务将从用户上次调用的任意 Activity 开始继续执行。

    (4)“never”
    该 Activity 不会为文档创建新任务。设置此值会替代 FLAG_ACTIVITY_NEW_DOCUMENT 和 FLAG_ACTIVITY_MULTIPLE_TASK 标志的行为(如果在 Intent 中设置了其中一个标志),并且概览屏幕将为应用显示单个任务,该任务将从用户上次调用的任意 Activity 开始继续执行。

    注:对于除 none 和 never 以外的值,必须使用 launchMode="standard" 定义 Activity。如果未指定此属性,则使用 documentLaunchMode="none"。

    删除任务

    默认情况下,在 Activity 结束后,文档任务会从概览屏幕中自动删除。 您可以使用 ActivityManager.AppTask 类、Intent 标志或 <activity> 属性替代此行为。

    通过将 <activity> 属性 android:excludeFromRecents 设置为 true,您可以始终将任务从概览屏幕中完全排除。

    您可以通过将 <activity> 属性 android:maxRecents 设置为整型值,设置应用能够包括在概览屏幕中的最大任务数默认值为 16。达到最大任务数后,最近最少使用的任务将从概览屏幕中删除。 android:maxRecents 的最大值为 50(内存不足的设备上为 25);小于 1 的值无效。

    1.使用 AppTask 类删除任务

    在于概览屏幕创建新任务的 Activity 中,您可以通过调用 finishAndRemoveTask() 方法指定何时删除该任务以及结束所有与之相关的 Activity。

    NewDocumentActivity.java:
    public void onRemoveFromRecents(View view) {
        // The document is no longer needed; remove its task.
        finishAndRemoveTask();
    }

    注:如下所述,使用 finishAndRemoveTask() 方法代替使用 FLAG_ACTIVITY_RETAIN_IN_RECENTS 标记。

    2.保留已完成的任务

    若要将任务保留在概览屏幕中(即使其 Activity 已完成),可在启动 Activity 的 Intent 的 addFlags() 方法中传递 FLAG_ACTIVITY_RETAIN_IN_RECENTS 标志。

    DocumentCentricActivity.java:
    private Intent newDocumentIntent() {
        final Intent newDocumentIntent = new Intent(this, NewDocumentActivity.class);
        newDocumentIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_DOCUMENT |
          android.content.Intent.FLAG_ACTIVITY_RETAIN_IN_RECENTS);
        newDocumentIntent.putExtra(KEY_EXTRA_NEW_DOCUMENT_COUNTER, incrementAndGet());
        return newDocumentIntent;
    }

    要达到同样的效果, 请将 <activity> 属性 android:autoRemoveFromRecents 设置为 false文档 Activity 的默认值为 true,常规 Activity 的默认值为 false。如前所述,使用此属性替代FLAG_ACTIVITY_RETAIN_IN_RECENTS 标志。




    本文是来自于Android Developer






    展开全文
  • 它是指在特定的时机分配合理的资源去处理预先确定的任务,用于在适当的时机触发一个包含业务逻辑的应用。调度无论在单机还是分布式环境中都是很重要的课题。在单机环境,调度与底层操作系统脱离不了干系;而在分布式...

    目录

    背景

    ElasticJob是什么?

    ElasticJob调度模型

    进程内调度

    进程级调度

    ElasticJob功能列表

    弹性调度

    资源分配

    作业治理

    可视化管控端

    ElasticJob典型应用场景

    复杂任务

    资源导向任务

    业务应用

    ElasticJob 新版本设计理念

    作业生态圈

    多元化调度器

    微内核 & 生态分离

    未来规划

    作业依赖

    调度执行分离

    更加易用的云管产品

    可插拔生态

    关于 ElasticJob 社区

    作者简介


    导读:调度(Scheduling)在计算机领域是个庞大概念,CPU调度、内存调度、进程调度等都可称之为调度。它是指在特定的时机分配合理的资源去处理预先确定的任务,用于在适当的时机触发一个包含业务逻辑的应用。调度无论在单机还是分布式环境中都是很重要的课题。在单机环境,调度与底层操作系统脱离不了干系;而在分布式环境中,调度直接决定运行集群的投入和产出。调度的两个核心要素是资源治理和触发时机。

    背景

    ElasticJob 诞生于 2015年,当时业界虽然有 QuartZ 等出类拔萃的定时任务框架,但缺乏分布式方面的探索。分布式调度云平台产品的缺失,使得 ElasticJob 从出现伊始便备受关注。它有效的弥补了作业在分布式领域的短板,并且提供了一站式的自动化运维管控端。

    ElasticJob 在技术选型时,选择站在了巨人的肩膀上而不是重复制造轮子的理念,将定时任务事实标准的 QuartZ 与 分布式协调的利器 ZooKeeper 完美结合,快速而稳定的搭建了全新概念的分布式调度框架。

    ElasticJob是什么?

    ElasticJobhttp://shardingsphere.apache.org/elasticjob/ 是一个分布式调度解决方案,由两个相互独立的子项目 ElasticJob Lite 和 ElasticJob Cloud 组成。ElasticJob Lite 定位为轻量级无中心化解决方案,使用 jar 的形式提供分布式任务的协调服务;ElasticJob Cloud 采用自研 Mesos Framework 的解决方案,额外提供资源治理、应用分发以及进程隔离等功能。它通过弹性调度、资源管控、以及作业治理的功能,打造一个适用于互联网场景的分布式调度解决方案,并通过开放的架构设计,提供多元化的作业生态。

    使用 ElasticJob 能够让开发工程师不再担心任务的线性吞吐量提升等非功能需求,使开发工程师能够更加专注于面向业务编码设计;同时,它能够解放运维工程师,使他们不必再担心任务的可用性和相关管理需求,只通过轻松的增加服务节点即可达到自动化运维的目的。

    ElasticJob调度模型

    与大部分的作业平台不同,ElasticJob 的调度模型划分为支持线程级别调度的进程内调度 ElasticJob Lite,和进程级别调度的ElasticJob Cloud。

    进程内调度

    ElasticJob Lite 是面向进程内的线程级调度框架。通过 ElasticJob ,作业能够透明化的与业务应用系统相结合。它能够方便的与 Spring 、Dubbo等 Java 框架配合使用,在作业中可自由使用 Spring 注入的 Bean,如数据源连接池、Dubbo 远程服务等,更加方便的贴合业务开发。

    ElasticJob Lite与业务应用部署在一起,其生命周期与业务应用保持一致,是典型的嵌入式轻量级架构。ElasticJob Lite 非常适合于资源使用稳定、部署架构简单的普通 Java 应用,可以理解为 Java 开发框架。

    ElasticJob Lite 本身是无中心化架构,无需独立的中心化调度节点,分布式下的每个任务节点均是以自调度的方式适时的调度作业。任务之间只需要一个注册中心来对分布式场景下的任务状态进行协调即可,目前支持 ZooKeeper 和 ETCD 作为注册中心。

    架构图如下:

    通过图中可看出,ElasticJob Lite 的分布式作业节点通过选举获取主节点,并通过主节点进行分片。分片完毕后,主节点与从节点并无二致,均以自我调度的方式执行任务。

    进程级调度

    ElasticJob Cloud 拥有进程内调度和进程级别调度两种方式。由于 ElasticJob Cloud 能够对作业服务器的资源进行控制,因此其作业类型可划分为常驻任务和瞬时任务。常驻任务类似于ElasticJob Lite,是进程内调度;瞬时任务则完全不同,它充分的利用了资源分配的削峰填谷能力,是进程级的调度,每次任务的会启动全新的进程处理。

    ElasticJob Cloud 需要通过 Mesos 对资源进行控制,并且通过部署在 Mesos Master的调度器进行任务和资源的分配。Cloud采用中心化架构,将调度中心的高可用交由 Mesos管理。

    它的架构图如下:

    通过图中可看出,ElasticJob Cloud 除了拥有 Lite 的全部能力之外,还拥有资源分配和任务分发的能力。它将作业的开发、打包、分发、调度、治理、分片等一些列的生命周期完全托管,是真正的作业云调度系统。

    相比于 ElasticJob Lite 的简单易用,ElasticJob Cloud 对 Mesos 的强依赖增加了系统部署的复杂度,因此更加适合大规模的作业系统。

    ElasticJob功能列表

    ElasticJob 功能主要有弹性调度、资源分配、作业治理和可视化管控。

    弹性调度

    弹性调度是 ElasticJob 最重要的功能,也是这款产品名称的由来。它是一款能够让任务通过分片进行水平扩展的任务处理系统。

    ElasticJob 中任务分片项的概念,使得任务可以在分布式的环境下运行,每台任务服务器只运行分配给该服务器的分片。随着服务器的增加或宕机,ElasticJob 会近乎实时的感知服务器数量的变更,从而重新为分布式的任务服务器分配更加合理的任务分片项,使得任务可以随着资源的增加而提升效率。

    举例说明,如果作业分为 4 片,用两台服务器执行,则每个服务器分到 2 片,如下图所示。

    当新增加作业服务器时,ElasticJob 会通过注册中心的临时节点的变化感知到新服务器的存在,并在下次任务调度的时候重新分片,新的服务器会承载一部分作业分片,分片如下图所示。

    当作业服务器在运行中宕机时,注册中心同样会通过临时节点感知,并将在下次运行时将分片转移至仍存活的服务器,以达到作业高可用的效果。本次由于服务器宕机而未执行完的作业,则可以通过失效转移的方式继续执行。作业高可用如下图所示。

    资源分配

    在导读中提到过,调度是指在适合的时间将适合的资源分配给任务,并使其生效。ElasticJob 具备资源分配的能力,它能够像分布式的操作系统一样调度任务。资源分配是借由 Mesos 实现的,由 Mesos 负责分配任务声明的所需资源(CPU 和内存),并将分配出去的资源进行隔离。ElasticJob 在获取到资源之后才会执行任务。

    考虑到 Mesos 系统部署相对复杂,因此 ElasticJob 将这部分拆分至 ElasticJob cloud 部分,供高级用户使用。随着 Kubernetes 的强劲发展,ElasticJob 未来也会完成 cloud 部分与它的对接。

    作业治理

    作业在分布式场景下的高可用、失效转移、错过作业重新执行等行为的治理和协调。

    可视化管控端

    主要包括作业的增删改查管控端、执行历史记录查询、配置中心的管理等。

    ElasticJob典型应用场景

    ElasticJob着重解决与复杂任务、资源导向任务和业务应用任务这几个方面的问题。

    复杂任务

    数据迁移。如果将百亿的数据从一组数据库集群迁移至另一组数据库集群,单线程的作业可能需要几天到几周不等。通过 ElasticJob 的弹性分片能力,可以大幅减少海量数据迁移所需要的时间。

    资源导向任务

    占用大量计算资源的报表作业。如果每天凌晨需要花费数小时计算 T+1 的业务报表,没有资源的管控,则无论报表作业是否启动,都要为其分配足够的资源。ElasticJob 将作业分为常驻作业和瞬时作业,对于报表类作业,瞬时作业是非常适合的。它能否在作业启动时获取资源,在作业结束后归还资源,做到真正的削峰填谷,更加合理的利用资源。

    业务应用

    订单拉取作业。订单系统大多采用消息中间件或作业的方式实现订单拉取,用于将订单生成系统和后端履约系统解耦,以便于前后端流量分离。采用作业实现的订单系统,可以通过 ElasticJob 实现订单相关业务逻辑,可以方便的利用外围系统所提供的依赖注入服务,无缝的融入业务端研发。

    ElasticJob 新版本设计理念

    经过了一个多月的开发,ElasticJob 社区近期计划发布3.0.0-alpha,以作为它进入 Apache 软件基金会的第一个发布版本。它的主要功能包括:

    作业生态圈

    灵活定制化作业是3.x版本的最重要设计变革。新版本基于 Apache ShardingSphere 可插拔架构的设计理念,打造了全新作业 API。意在使开发者能够更加便捷且相互隔离的方式拓展作业类型,打造 ElasticJob 作业的生态圈。

    ElasticJob 提供灵活的作业API,它将作业解耦为作业接口和执行器接口。用户可以定制化全新的作业类型,诸如脚本执行、HTTP服务执行、大数据类作业、文件类作业等。目前 ElasticJob 内置了脚本执行作业,并且完全开放了扩展接口,开发者可以通过 SPI 的方式引入新的作业类型,并且可以便捷的回馈至社区。

    多元化调度器

    在保留原有的基于 cron 的时间触发调度器的基础上,增加了一次性的调度 API,为 ElasticJob 增加了时间维度之外的全新调度维度。

    微内核 & 生态分离

    抽象作业内核模块,将作业执行轨迹追踪等辅助功能以及作业生态等可扩展模块从内核模块完全抽离。作业执行轨迹追踪模块作为二级生态,修改了之前只支持MySQL 作为存储介质的限制,完全开放持久化的适配。

    未来规划

    3.0.0的版本作为一个快速给社区回馈的版本,并未进行颠覆性的革新,而是尝试将项目内核一点一滴的解耦。在未来的规划中,ElasticJob 将大刀阔斧的向前迈进,主要的规划如下。

    作业依赖

    支持基于有向无环图(DAG)的作业依赖。依赖包含基于作业整体维度的依赖,以及基于作业分片项的依赖,打造更加灵活的作业治理解决方案。

    调度执行分离

    将调度器和执行器完全分离。调度器可以与执行器一起部署,即为 ElasticJob lite 的无中心化轻量级版本;调度器可以与执行器分离部署,即为ElasticJob cloud 的资源管控的一站式分布式调度系统。

    更加易用的云管产品

    将目前仅支持 Mesos 的 ElasticJob cloud 打造为支持 Mesos 和 Kubernetes的作业云管平台,并提供无 Mesos 和 Kubernetes 也能够独立使用的不包含资源管控的纯作业管控平台。

    可插拔生态

    与 Apache ShardingSphere 一脉相承,ElasticJob 也将提供更加可插拔和模块化架构,为开发者提供基础设施。开发者可以方便的基于 ElasticJob 二次开发,添加各种定制化功能,包括但不限于作业类型(如:大数据作业、HTTP作业等)、注册中心类型(如:Eureka等)、执行轨迹存储介质(如其他数据库类型)等。ElasticJob的定位如下图所示。

     

    关于 ElasticJob 社区

    ElasticJob 社区在之前的几年处于停滞状况,主要原因是作者精力有限,分身乏术。在接收到了作为 Apache ShardingSphere 弹性迁移的调度基础设施的需求之后,本就一脉相承的 ElasticJob 社区决定重启,并且作为 Apache ShardingSphere 的子项目继续发光发热。目前的 ElasticJob 已正式将项目源码迁入 Apache 的 GitHub 仓库,并且在重启的一个多月以来十分活跃,已完成了上百个Pull Request的开发,在GitHub 周和月度趋势排名中榜上有名。

    ElasticJob 是Apache ShardingSpherehttps://github.com/apache/shardingsphere的子项目,目标是成为独立的 Apache 顶级项目,以及为 Apache ShardingSphere 的弹性迁移提供数据调度的基石。

    欢迎开源爱好者加入 ElasticJob 社区的建设。

    GitHub地址:https://github.com/apache/shardingsphere-elasticjob-lite

    官方网站:http://shardingsphere.apache.org/elasticjob/

    作者简介

    张亮,京东数科数据研发负责人,Apache ShardingSphere创始人 & 项目VP、ElasticJob创始人。

    热爱开源,主导开源项目 ShardingSphere (原名 Sharding-JDBC) 和 Elastic-Job。擅长以Java 为主分布式架构,推崇优雅代码,对如何写出具有展现力的代码有较多研究。

    目前主要精力投入在将 Apache ShardingSphere 打造为业界一流的金融级数据解决方案之上。Apache ShardingSphere 是 Apache软件基金会旗下的顶级项目,也是 Apache 软件基金会首个分布式数据库中间件。

     

    展开全文
  • 通过使用包含的RAMJobStore,所有的作业和触发器存储在RAM,因此不计划执行仍然存在 - 但这是无需使用外部数据库的优势。 第二节 Quartz使用 2.1 基本使用 Maven+Idea pom.xml < dependencies > <!--...

    官网链接

    第一节 Quartz简介

    1.1 简介
    Quartz 是一种功能丰富的,开放源码的作业调度库,可以在几乎任何Java应用程序集成 - 从最小的独立的应用程序到规模最大电子商务系统。Quartz可以用来创建简单或复杂的日程安排执行几十,几百,甚至是十万的作业数 -  作业被定义为标准的Java组件,可以执行几乎任何东西,可以编程让它们执行。 Quartz调度包括许多企业级功能,如JTA事务和集群支持。
    Quartz 是可自由使用,使用Apache 2.0 license授权方式。
    
    Quartz是一个任务调度框架。比如你遇到这样的问题
    想每月29号,信用卡自动还款
    想每年4月1日自己给当年暗恋女神发一封匿名贺卡
    想每隔1小时,备份一下自己的学习笔记到云盘
    
    这些问题总结起来就是:在某一个有规律的时间点干某件事。并且时间的触发的条件可以非常复杂(比如每月最后一个工作日的17:50),复杂到需要一个专门的框架来干这个事。 Quartz就是来干这样的事,你给它一个触发条件的定义,它负责到了时间点,触发相应的Job起来干活
    
    1.2 作用
    如果应用程序需要在给定时间执行任务,或者如果系统有连续维护作业,那么Quartz是理想的解决方案。
    使用Quartz作业调度应用的示例:
    驱动处理工作流程:作为一个新的订单被初始化放置,调度作业到在正好两个小时内,它将检查订单的状态,如果订单确认消息尚未收到命令触发警告通知,以及改变订单的状态为“等待的干预”。
    系统维护:调度工作给数据库的内容,每个工作日(节假日除外平日)在11:30 PM转储到一个XML文件中。
    在应用程序内提供提醒服务。
    
    1.3 特点
    1.3.1 环境
    Quartz 可以运行嵌入在另一个独立式应用程序
    Quartz 可以在应用程序服务器(或servlet容器)内被实例化,并且参与XA事务
    Quartz 可以作为一个独立的程序运行(其自己的Java虚拟机内),可以通过RMI使用
    Quartz 可以被实例化,作为独立的项目集群(负载平衡和故障转移功能),用于作业的执行
    
    1.3.2 作业调度
    作业被安排在一个给定的触发时运行。触发器可以使用以下指令的接近任何组合来创建:
    
    在一天中的某个时间(到毫秒)
    在一周的某几天
    在每月的某一天
    在一年中的某些日期
    不在注册的日历中列出的特定日期(如商业节假日除外)
    重复特定次数
    重复进行,直到一个特定的时间/日期
    无限重复
    重复的延迟时间间隔
    
    作业是由其创建者赋予的名字,也可以组织成命名组。触发器也可以给予名称和放置在组中,以方便地将它们调度内组织。作业可以被添加到所述调度器一次,而是具有多个触发器注册。在企业Java环境中,作业可以执行自己的工作作为分布式(XA)事务的一部分
    
    1.3.3 作业执行
    作业可以实现简单的作业接口,为作业执行工作的任何Java类。
    Job类的实例可以通过Quartz被实例化,或者通过应用程序框架。
    当触发时,调度通知实现JobListener和TriggerListener接口零个或多个Java对象(监听器可以是简单的Java对象,或EJB,JMS或发布者等)。这些监听器在作业已经执行之后通知。
    由于作业完成后返回JobCompletionCode,它通知的成功或失败的调度。JobCompletionCode还可以指示的基础上,成功的话就采取行动调度/失败的代码 - 如立即重新执行作业。
    
    1.3.4 作业持久性
    Quartz的设计包括可被实现以提供的作业存储各种机制一个作业存储接口
    通过使用包含的JDBCJobStore,所有的作业和触发器配置为“非挥发性”都存储在通过JDBC关系数据库。
    通过使用包含的RAMJobStore,所有的作业和触发器存储在RAM,因此不计划执行仍然存在 - 但这是无需使用外部数据库的优势。
    

    第二节 Quartz使用

    2.1 基本使用

    Maven+Idea

    pom.xml

    <dependencies>
    		<!--Quartz任务调度-->
    		<!-- https://mvnrepository.com/artifact/org.quartz-scheduler/quartz -->
    		<dependency>
    			<groupId>org.quartz-scheduler</groupId>
    			<artifactId>quartz</artifactId>
    			<version>2.2.3</version>
    		</dependency>
    	</dependencies>
    

    HelloQuartz 具体的工作类

    
    /**
     * 工作类的具体实现
     * */
    public class HelloQuartz implements Job {
        //执行
        public void execute(JobExecutionContext context) throws JobExecutionException {
            //创建工作详情
            JobDetail detail=context.getJobDetail();
            //获取工作的名称
            String name=detail.getJobDataMap().getString("name");
            String job=detail.getJobDataMap().getString("job1");
    
            System.out.println("任务调度:组:"+job+",工作名:"+name+"---->今日整点抢购,不容错过!");
        }
    }
    

    Quartz_1 运行任务调度类

    public static void main(String[] args) {
            try{
                //创建scheduler,执行计划
                Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();
                //定义一个Trigger,触发条件类
                Trigger trigger = TriggerBuilder.newTrigger().
                        withIdentity("trigger1", "group1") //定义name/group
                        .startNow()//一旦加入scheduler,立即生效
                        .withSchedule(SimpleScheduleBuilder.simpleSchedule() //使用SimpleTrigger
                                .withIntervalInSeconds(1) //每隔一秒执行一次
                                .repeatForever()) //一直执行,奔腾到老不停歇
                        .build();
                //定义一个JobDetail
                JobDetail job = JobBuilder.newJob(HelloQuartz.class) //定义Job类为HelloQuartz类,这是真正的执行逻辑所在
                        .withIdentity("job1", "group1") //定义name/group
                        .usingJobData("name", "quartz") //定义属性
                        .build();
                //加入这个调度
                scheduler.scheduleJob(job, trigger);
                //启动任务调度
                scheduler.start();
                
            }catch (Exception ex){
                ex.printStackTrace();
            }
    }
    

    运行结果:

    在这里插入图片描述

    2.2 核心类说明
    Scheduler:调度器。所有的调度都是由它控制
      Scheduler就是Quartz的大脑,所有任务都是由它来设施
      Schduelr包含一个两个重要组件: JobStore和ThreadPool
        JobStore是会来存储运行时信息的,包括Trigger,Schduler,JobDetail,业务锁等
        ThreadPool就是线程池,Quartz有自己的线程池实现。所有任务的都会由线程池执行
    
    SchdulerFactory,顾名思义就是来用创建Schduler了,有两个实现:DirectSchedulerFactory和 StdSchdulerFactory。前者可以用来在代码里定制你自己的Schduler参数。后者是直接读取classpath下的quartz.properties(不存在就都使用默认值)配置来实例化Schduler。通常来讲,我们使用StdSchdulerFactory也就足够了。
    SchdulerFactory本身是支持创建RMI stub的,可以用来管理远程的Scheduler,功能与本地一样
    

    quartz.properties 常用配置示例:

    org.quartz.scheduler.instanceName = DefaultQuartzScheduler
    org.quartz.threadPool.class = org.quartz.simpl.SimpleThreadPool
    org.quartz.threadPool.threadCount = 10 
    org.quartz.threadPool.threadPriority = 5
    org.quartz.threadPool.threadsInheritContextClassLoaderOfInitializingThread = true
    org.quartz.jobStore.class = org.quartz.simpl.RAMJobStore
    
    Trigger: 定义触发的条件。可以使用SimpleTrigger,每隔1秒中执行一次
    
    JobDetail & Job: JobDetail 定义的是任务数据,而真正的执行逻辑是在Job中。 为什么设计成JobDetail + Job,不直接使用Job?这是因为任务是有可能并发执行,如果Scheduler直接使用Job,就会存在对同一个Job实例并发访问的问题。而JobDetail & Job 方式,sheduler每次执行,都会根据JobDetail创建一个新的Job实例,这样就可以规避并发访问的问题
    
    JobDetail和Trigger都有name和group。
    name是它们在这个sheduler里面的唯一标识。如果我们要更新一个JobDetail定义,只需要设置一个name相同的JobDetail实例即可。
    group是一个组织单元,sheduler会提供一些对整组操作的API,比如 scheduler.resumeJobs()。
    
    2.3 Trigger
    2.3.1 trigger常用属性

    StartTime & EndTime

    StartTime & EndTime

    startTime和endTime指定的Trigger会被触发的时间区间。在这个区间之外,Trigger是不会被触发的。
    所有Trigger都会包含这两个属性
    

    Priority

    当scheduler比较繁忙的时候,可能在同一个时刻,有多个Trigger被触发了,但资源不足(比如线程池不足)。那么这个时候比剪刀石头布更好的方式,就是设置优先级。优先级高的先执行。
    需要注意的是,优先级只有在同一时刻执行的Trigger之间才会起作用,如果一个Trigger是9:00,另一个Trigger是9:30。那么无论后一个优先级多高,前一个都是先执行。
    优先级的值默认是5,当为负数时使用默认值。最大值似乎没有指定,但建议遵循Java的标准,使用1-10,不然鬼才知道看到【优先级为10】是时,上头还有没有更大的值。
    

    Misfire

    Misfire(错失触发)策略类似的Scheduler资源不足的时候,或者机器崩溃重启等,有可能某一些Trigger在应该触发的时间点没有被触发,也就是Miss Fire了。这个时候Trigger需要一个策略来处理这种情况。每种Trigger可选的策略各不相同。
    这里有两个点需要重点注意:
    MisFire的触发是有一个阀值,这个阀值是配置在JobStore的。比RAMJobStore是org.quartz.jobStore.misfireThreshold。只有超过这个阀值,才会算MisFire。小于这个阀值,Quartz是会全部重新触发。
    所有MisFire的策略实际上都是解答两个问题:
    1. 已经MisFire的任务还要重新触发吗?
    2. 如果发生MisFire,要调整现有的调度时间吗?
    

    SimpleTrigger的MisFire策略有

    MISFIRE_INSTRUCTION_IGNORE_MISFIRE_POLICY

    这个不是忽略已经错失的触发的意思,而是说忽略MisFire策略。它会在资源合适的时候,重新触发所有的MisFire任务,并且不会影响现有的调度时间。
    比如,SimpleTrigger每15秒执行一次,而中间有5分钟时间它都MisFire了,一共错失了20个,5分钟后,假设资源充足了,并且任务允许并发,它会被一次性触发。
    这个属性是所有Trigger都适用。
    

    MISFIRE_INSTRUCTION_FIRE_NOW

    忽略已经MisFire的任务,并且立即执行调度。这通常只适用于只执行一次的任务。
    

    MISFIRE_INSTRUCTION_RESCHEDULE_NOW_WITH_EXISTING_REPEAT_COUNT

    将startTime设置当前时间,立即重新调度任务,包括的MisFire的
    MISFIRE_INSTRUCTION_RESCHEDULE_NOW_WITH_REMAINING_REPEAT_COUNT
    类似MISFIRE_INSTRUCTION_RESCHEDULE_NOW_WITH_EXISTING_REPEAT_COUNT,区别在于会忽略已经MisFire的任务
    

    MISFIRE_INSTRUCTION_RESCHEDULE_NEXT_WITH_EXISTING_COUNT

    在下一次调度时间点,重新开始调度任务,包括的MisFire的
    MISFIRE_INSTRUCTION_RESCHEDULE_NEXT_WITH_REMAINING_COUNT
    类似于MISFIRE_INSTRUCTION_RESCHEDULE_NEXT_WITH_EXISTING_COUNT,区别在于会忽略已经MisFire的任务。
    

    MISFIRE_INSTRUCTION_SMART_POLICY

    所有的Trigger的MisFire默认值都是这个,大致意思是“把处理逻辑交给聪明的Quartz去决定”。基本策略是,
    如果是只执行一次的调度,使用MISFIRE_INSTRUCTION_FIRE_NOW
    如果是无限次的调度(repeatCount是无限的),使用MISFIRE_INSTRUCTION_RESCHEDULE_NEXT_WITH_REMAINING_COUNT
    否则,使用MISFIRE_INSTRUCTION_RESCHEDULE_NOW_WITH_EXISTING_REPEAT_COUNT
    

    Calendar

    Calendar不是jdk的java.util.Calendar,不是为了计算日期的。它的作用是在于补充Trigger的时间。可以排除或加入某一些特定的时间点。
    以”每月29日零点自动还信用卡“为例,我们想排除掉每年的2月29号零点这个时间点(因为平年和润年2月不一样)。这个时间,就可以用Calendar来实现
    
    Quartz提供以下几种Calendar,注意,所有的Calendar既可以是排除,也可以是包含,取决于:
    HolidayCalendar。指定特定的日期,比如20140613。精度到天。
    DailyCalendar。指定每天的时间段(rangeStartingTime, rangeEndingTime),格式是HH:MM[:SS[:mmm]]。也就是最大精度可以到毫秒。
    WeeklyCalendar。指定每星期的星期几,可选值比如为java.util.Calendar.SUNDAY。精度是天。
    MonthlyCalendar。指定每月的几号。可选值为1-31。精度是天
    AnnualCalendar。 指定每年的哪一天。使用方式如上例。精度是天。
    CronCalendar。指定Cron表达式。精度取决于Cron表达式,也就是最大精度可以到秒。
    
    2.3.2 Trigger实现类

    SimpleTrigger

    指定从某一个时间开始,以一定的时间间隔(单位是毫秒)执行的任务。
    它适合的任务类似于:9:00 开始,每隔1小时,执行一次。
    它的属性有:
      repeatInterval 重复间隔
      repeatCount 重复次数。实际执行次数是 repeatCount+1。因为在startTime的时候一定会执行一次。
    

    示例:

    SimpleScheduleBuilder.simpleSchedule().
                        withIntervalInSeconds(10).//每隔10秒执行一次
                        repeatForever().//永远执行
                        build();
    
    SimpleScheduleBuilder.simpleSchedule().
                        withIntervalInMinutes(3).//每隔3分钟执行一次
                        withRepeatCount(3).//执行3次
                        build();
    

    CalendarIntervalTrigger

    类似于SimpleTrigger,指定从某一个时间开始,以一定的时间间隔执行的任务。 但是不同的是SimpleTrigger指定的时间间隔为毫秒,没办法指定每隔一个月执行一次(每月的时间间隔不是固定值),而CalendarIntervalTrigger支持的间隔单位有秒,分钟,小时,天,月,年,星期。
    相较于SimpleTrigger有两个优势:1、更方便,比如每隔1小时执行,你不用自己去计算1小时等于多少毫秒。 2、支持不是固定长度的间隔,比如间隔为月和年。但劣势是精度只能到秒。
    它适合的任务类似于:9:00 开始执行,并且以后每周 9:00 执行一次
    它的属性有:
    interval 执行间隔
    intervalUnit 执行间隔的单位(秒,分钟,小时,天,月,年,星期)
    

    示例:

    CalendarIntervalScheduleBuilder.calendarIntervalSchedule().
                        withIntervalInDays(2) //每2天执行一次    
                .build();
    
    CalendarIntervalScheduleBuilder.calendarIntervalSchedule().
                        withIntervalInWeeks(1) //每周执行一次    
                        .build();
    

    DailyTimeIntervalTrigger

    指定每天的某个时间段内,以一定的时间间隔执行任务。并且它可以支持指定星期。
    它适合的任务类似于:指定每天9:00 至 18:00 ,每隔70秒执行一次,并且只要周一至周五执行。
    它的属性有:
    startTimeOfDay 每天开始时间
    endTimeOfDay 每天结束时间
    daysOfWeek 需要执行的星期
    interval 执行间隔
    intervalUnit 执行间隔的单位(秒,分钟,小时,天,月,年,星期)
    repeatCount 重复次数
    

    示例:

    DailyTimeIntervalScheduleBuilder.dailyTimeIntervalSchedule()
                        .startingDailyAt(TimeOfDay.hourAndMinuteOfDay(9, 0)) //每天9:00开始
                        .endingDailyAt(TimeOfDay.hourAndMinuteOfDay(18, 0)) //18:00 结束                   .onDaysOfTheWeek(MONDAY,TUESDAY,WEDNESDAY,THURSDAY,FRIDAY) //周一至周五执行
                        .withIntervalInHours(1) //每间隔1小时执行一次
                        .withRepeatCount(100) //最多重复100次(实际执行100+1次)
                        .build();
    
    
    DailyTimeIntervalScheduleBuilder.dailyTimeIntervalSchedule()
                        .startingDailyAt(TimeOfDay.hourAndMinuteOfDay(10, 0)) //每天10:00开始
                        .endingDailyAfterCount(10) //每天执行10次,这个方法实际上根据 startTimeOfDay+interval*count 算出 endTimeOfDay
                        .onDaysOfTheWeek(MONDAY,TUESDAY,WEDNESDAY,THURSDAY,FRIDAY) //周一至周五执行
                        .withIntervalInHours(1) //每间隔1小时执行一次
                        .build();
    

    CronTrigger

    适合于更复杂的任务,它支持类型于Linux Cron的语法(并且更强大)。基本上它覆盖了以上三个Trigger的绝大部分能力(但不是全部)—— 当然,也更难理解。
    它适合的任务类似于:每天0:00,9:00,18:00各执行一次。
    它的属性只有:
    Cron表达式。但这个表示式本身就够复杂了
    

    示例:

     CronScheduleBuilder.cronSchedule("0 0/2 10-12 * * ?") // 每天10:00-12:00,每隔2分钟执行一次
     .build();
    
    cronSchedule("0 30 9 ? * MON") // 每周一,9:30执行一次
    .build();
    
     CronScheduleBuilder.weeklyOnDayAndHourAndMinute(MONDAY,9, 30) //等同于 0 30 9 ? * MON
     .build();
    
    2.3.3 Cron表达式
    位置时间域允许值特殊值
    10-59, - * /
    2分钟0-59, - * /
    3小时0-23, - * /
    4日期1-31, - * ? / L W C
    5月份1-12, - * /
    6星期1-7, - * ? / L C #
    7年份(可选)1-31, - * /
    星号():可用在所有字段中,表示对应时间域的每一个时刻,例如, 在分钟字段时,表示“每分钟”;
    
    问号(?):该字符只在日期和星期字段中使用,它通常指定为“无意义的值”,相当于点位符;
    
    减号(-):表达一个范围,如在小时字段中使用“10-12”,则表示从10到12点,即10,11,12;
    
    逗号(,):表达一个列表值,如在星期字段中使用“MON,WED,FRI”,则表示星期一,星期三和星期五;
    
    斜杠(/):x/y表达一个等步长序列,x为起始值,y为增量步长值。如在分钟字段中使用0/15,则表示为0,15,30和45秒,而5/15在分钟字段中表示5,20,35,50,你也可以使用*/y,它等同于0/y;
    
    L:该字符只在日期和星期字段中使用,代表“Last”的意思,但它在两个字段中意思不同。L在日期字段中,表示这个月份的最后一天,如一月的31号,非闰年二月的28号;如果L用在星期中,则表示星期六,等同于7。但是,如果L出现在星期字段里,而且在前面有一个数值X,则表示“这个月的最后X天”,例如,6L表示该月的最后星期五;
    
    W:该字符只能出现在日期字段里,是对前导日期的修饰,表示离该日期最近的工作日。例如15W表示离该月15号最近的工作日,如果该月15号是星期六,则匹配14号星期五;如果15日是星期日,则匹配16号星期一;如果15号是星期二,那结果就是15号星期二。但必须注意关联的匹配日期不能够跨月,如你指定1W,如果1号是星期六,结果匹配的是3号星期一,而非上个月最后的那天。W字符串只能指定单一日期,而不能指定日期范围;
    
    LW组合:在日期字段可以组合使用LW,它的意思是当月的最后一个工作日;
    
    井号(#):该字符只能在星期字段中使用,表示当月某个工作日。如6#3表示当月的第三个星期五(6表示星期五,#3表示当前的第三个),而4#5表示当月的第五个星期三,假设当月没有第五个星期三,忽略不触发;
    
    C:该字符只在日期和星期字段中使用,代表“Calendar”的意思。它的意思是计划所关联的日期,如果日期没有被关联,则相当于日历中所有日期。例如5C在日期字段中就相当于日历5日以后的第一天。1C在星期字段中相当于星期日后的第一天。
    

    表达式示例:

    表示式说明
    0 0 12 * * ?每天12点运行
    0 15 10 ? * *每天10:15运行
    0 15 10 * * ?每天10:15运行
    0 15 10 * * ? *每天10:15运行
    0 15 10 * * ? 2008在2008年的每天10:15运行
    0 * 14 * * ?每天14点到15点之间每分钟运行一次,开始于14:00,结束于14:59。
    0 0/5 14 * * ?每天14点到15点每5分钟运行一次,开始于14:00,结束于14:55。
    0 0/5 14,18 * * ?每天14点到15点每5分钟运行一次,此外每天18点到19点每5钟也运行一次。
    0 0-5 14 * * ?每天14:00点到14:05,每分钟运行一次。
    0 10,44 14 ? 3 WED3月每周三的14:10分到14:44,每分钟运行一次。
    0 15 10 ? * MON-FRI每周一,二,三,四,五的10:15分运行。
    0 15 10 15 * ?每月15日10:15分运行。
    0 15 10 L * ?每月最后一天10:15分运行。
    0 15 10 ? * 6L每月最后一个星期五10:15分运行。
    0 15 10 ? * 6L 2007-2009在2007,2008,2009年每个月的最后一个星期五的10:15分运行。
    0 15 10 ? * 6#3每月第三个星期五的10:15分运行。
    2.4 Job&JobDetail
    2.4.1 JobDetail
    JobDetail是任务的定义,而Job是任务的执行逻辑。在JobDetail里会引用一个Job Class定义
    
    任务步骤:
      1、创建一个org.quartz.Job的实现类,并实现实现自己的业务逻辑。比如上面的DoNothingJob。
      2、定义一个JobDetail,引用这个实现类
      3、加入scheduleJob
    
    核心代码:
    JobClass jobClass=JobDetail.getJobClass()
    Job jobInstance=jobClass.newInstance()。所以Job实现类,必须有一个public的无参构建方法。
    jobInstance.execute(JobExecutionContext context)。JobExecutionContext是Job运行的上下文,可以获得Trigger、Scheduler、JobDetail的信息。
    也就是说,每次调度都会创建一个新的Job实例,这样的好处是有些任务并发执行的时候,不存在对临界资源的访问问题——当然,如果需要共享JobDataMap的时候,还是存在临界资源的并发访问的问题。
    

    JobDataMap

    Job都次都是newInstance的实例,那我怎么传值给它? 比如我现在有两个发送邮件的任务,一个是发给"liLei",一个发给"hanmeimei",不能说我要写两个Job实现类LiLeiSendEmailJob和HanMeiMeiSendEmailJob。实现的办法是通过JobDataMap。
    
    每一个JobDetail都会有一个JobDataMap。JobDataMap本质就是一个Map的扩展类,只是提供了一些更便捷的方法,比如getString()之类的。
    
    我们可以在定义JobDetail,加入属性值,方式有二:
    第一种:
    newJob().usingJobData("age", 18) //加入属性到ageJobDataMap
    第二种:
    job.getJobDataMap().put("name", "quertz"); //加入属性name到JobDataMap
    
    

    在Job中可以获取这个JobDataMap的值,方式同样有二:

    JobDetail detail = context.getJobDetail();
    JobDataMap map = detail.getJobDataMap(); //方法一:获得JobDataMap
    
    private String name;
    //方法二:属性的setter方法,会将JobDataMap的属性自动注入
    public void setName(String name) { 
         this.name = name;
    }
    
    对于同一个JobDetail实例,执行的多个Job实例,是共享同样的JobDataMap,也就是说,如果你在任务里修改了里面的值,会对其他Job实例(并发的或者后续的)造成影响。
    
    除了JobDetail,Trigger同样有一个JobDataMap,共享范围是所有使用这个Trigger的Job实例
    
    2.4.2 Job并发
    job是有可能并发执行的,比如一个任务要执行10秒中,而调度算法是每秒中触发1次,那么就有可能多个任务被并发执行。
    
    有时候我们并不想任务并发执行,比如这个任务要去”获得数据库中所有未发送邮件的名单“,如果是并发执行,就需要一个数据库锁去避免一个数据被多次处理。这个时候一个@DisallowConcurrentExecution解决这个问题
    
    public class DoNothingJob implements Job {
        @DisallowConcurrentExecution
        public void execute(JobExecutionContext context) throws JobExecutionException {
            System.out.println("操作");
        }
    }
    
    注意,@DisallowConcurrentExecution是对JobDetail实例生效,也就是如果你定义两个JobDetail,引用同一个Job类,是可以并发执行的
    
    JobExecutionException
    Job.execute()方法是不允许抛出除JobExecutionException之外的所有异常的(包括RuntimeException),所以编码的时候,最好是try-catch住所有的Throwable,小心处理。
    

    代码示例:

    public class MyJob  implements Job {
        public void execute(JobExecutionContext context) throws JobExecutionException {
            System.out.println("双11秒杀通知");
        }
    }
    
    
    public class Quartz_2 {
    
        public static void main(String[] args) throws Exception{
            JobDetail job=newJob()
                    .ofType(MyJob.class) //引用Job Class
                    .withIdentity("job1", "group1") //设置name/group
                    .withDescription("this is a test job") //设置描述
                    .usingJobData("age", 18) //加入属性到age,JobDataMap
                    .build();
    
            job.getJobDataMap().put("name", "quertz"); //加入属性name到JobDataMap
    
            //定义一个每秒执行一次的SimpleTrigger
            Trigger trigger=newTrigger()
                    .startNow()
                    .withIdentity("trigger1")
                    .withSchedule(simpleSchedule()
                            .withIntervalInSeconds(1)
                            .repeatForever())
                    .build();
            //创建任务调度对象
            Scheduler sche= StdSchedulerFactory.getDefaultScheduler();
            //添加工作计划
            sche.scheduleJob(job, trigger);
            //启动任务调度
            sche.start();
            Thread.sleep(10000);
            //关闭任务调度
            sche.shutdown();
        }
    }
    

    在这里插入图片描述

    第三节 SpringBoot整合Quartz

    3.1 配置文件

    pom.xml

    		<dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-web</artifactId>
            </dependency>
    
            <dependency>
                <groupId>org.quartz-scheduler</groupId>
                <artifactId>quartz</artifactId>
                <version>2.3.0</version>
            </dependency>
    

    quartz.properties 基于SpringBoot的Quartz的配置

    # 固定前缀org.quartz
    # 主要分为scheduler、threadPool、jobStore、plugin等部分
    #
    #
    org.quartz.scheduler.instanceName = DefaultQuartzScheduler
    org.quartz.scheduler.rmi.export = false
    org.quartz.scheduler.rmi.proxy = false
    org.quartz.scheduler.wrapJobExecutionInUserTransaction = false
    
    # 实例化ThreadPool时,使用的线程类为SimpleThreadPool
    org.quartz.threadPool.class = org.quartz.simpl.SimpleThreadPool
    
    # threadCount和threadPriority将以setter的形式注入ThreadPool实例
    # 并发个数
    org.quartz.threadPool.threadCount = 5
    # 优先级
    org.quartz.threadPool.threadPriority = 5
    org.quartz.threadPool.threadsInheritContextClassLoaderOfInitializingThread = true
    
    org.quartz.jobStore.misfireThreshold = 5000
    
    # 默认存储在内存中
    #org.quartz.jobStore.class = org.quartz.simpl.RAMJobStore
    
    #持久化
    org.quartz.jobStore.class = org.quartz.impl.jdbcjobstore.JobStoreTX
    
    org.quartz.jobStore.tablePrefix = qrtz_
    
    org.quartz.jobStore.dataSource = qzDS
    
    org.quartz.dataSource.qzDS.driver = com.mysql.jdbc.Driver
    
    org.quartz.dataSource.qzDS.URL = jdbc:mysql:///quartz?useUnicode=true&characterEncoding=UTF-8
    
    org.quartz.dataSource.qzDS.user = root
    
    org.quartz.dataSource.qzDS.password = 1234
    
    org.quartz.dataSource.qzDS.maxConnections = 10
    

    quartz数据库建表语句

    DROP TABLE IF EXISTS QRTZ_FIRED_TRIGGERS;  
    DROP TABLE IF EXISTS QRTZ_PAUSED_TRIGGER_GRPS;  
    DROP TABLE IF EXISTS QRTZ_SCHEDULER_STATE;  
    DROP TABLE IF EXISTS QRTZ_LOCKS;  
    DROP TABLE IF EXISTS QRTZ_SIMPLE_TRIGGERS;  
    DROP TABLE IF EXISTS QRTZ_SIMPROP_TRIGGERS;  
    DROP TABLE IF EXISTS QRTZ_CRON_TRIGGERS;  
    DROP TABLE IF EXISTS QRTZ_BLOB_TRIGGERS;  
    DROP TABLE IF EXISTS QRTZ_TRIGGERS;  
    DROP TABLE IF EXISTS QRTZ_JOB_DETAILS;  
    DROP TABLE IF EXISTS QRTZ_CALENDARS;  
      
    CREATE TABLE QRTZ_JOB_DETAILS(  
    SCHED_NAME VARCHAR(120) NOT NULL,  
    JOB_NAME VARCHAR(200) NOT NULL,  
    JOB_GROUP VARCHAR(200) NOT NULL,  
    DESCRIPTION VARCHAR(250) NULL,  
    JOB_CLASS_NAME VARCHAR(250) NOT NULL,  
    IS_DURABLE VARCHAR(1) NOT NULL,  
    IS_NONCONCURRENT VARCHAR(1) NOT NULL,  
    IS_UPDATE_DATA VARCHAR(1) NOT NULL,  
    REQUESTS_RECOVERY VARCHAR(1) NOT NULL,  
    JOB_DATA BLOB NULL,  
    PRIMARY KEY (SCHED_NAME,JOB_NAME,JOB_GROUP))  
    ENGINE=InnoDB;  
      
    CREATE TABLE QRTZ_TRIGGERS (  
    SCHED_NAME VARCHAR(120) NOT NULL,  
    TRIGGER_NAME VARCHAR(200) NOT NULL,  
    TRIGGER_GROUP VARCHAR(200) NOT NULL,  
    JOB_NAME VARCHAR(200) NOT NULL,  
    JOB_GROUP VARCHAR(200) NOT NULL,  
    DESCRIPTION VARCHAR(250) NULL,  
    NEXT_FIRE_TIME BIGINT(13) NULL,  
    PREV_FIRE_TIME BIGINT(13) NULL,  
    PRIORITY INTEGER NULL,  
    TRIGGER_STATE VARCHAR(16) NOT NULL,  
    TRIGGER_TYPE VARCHAR(8) NOT NULL,  
    START_TIME BIGINT(13) NOT NULL,  
    END_TIME BIGINT(13) NULL,  
    CALENDAR_NAME VARCHAR(200) NULL,  
    MISFIRE_INSTR SMALLINT(2) NULL,  
    JOB_DATA BLOB NULL,  
    PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP),  
    FOREIGN KEY (SCHED_NAME,JOB_NAME,JOB_GROUP)  
    REFERENCES QRTZ_JOB_DETAILS(SCHED_NAME,JOB_NAME,JOB_GROUP))  
    ENGINE=InnoDB;  
      
    CREATE TABLE QRTZ_SIMPLE_TRIGGERS (  
    SCHED_NAME VARCHAR(120) NOT NULL,  
    TRIGGER_NAME VARCHAR(200) NOT NULL,  
    TRIGGER_GROUP VARCHAR(200) NOT NULL,  
    REPEAT_COUNT BIGINT(7) NOT NULL,  
    REPEAT_INTERVAL BIGINT(12) NOT NULL,  
    TIMES_TRIGGERED BIGINT(10) NOT NULL,  
    PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP),  
    FOREIGN KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)  
    REFERENCES QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP))  
    ENGINE=InnoDB;  
      
    CREATE TABLE QRTZ_CRON_TRIGGERS (  
    SCHED_NAME VARCHAR(120) NOT NULL,  
    TRIGGER_NAME VARCHAR(200) NOT NULL,  
    TRIGGER_GROUP VARCHAR(200) NOT NULL,  
    CRON_EXPRESSION VARCHAR(120) NOT NULL,  
    TIME_ZONE_ID VARCHAR(80),  
    PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP),  
    FOREIGN KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)  
    REFERENCES QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP))  
    ENGINE=InnoDB;  
      
    CREATE TABLE QRTZ_SIMPROP_TRIGGERS  
      (            
        SCHED_NAME VARCHAR(120) NOT NULL,  
        TRIGGER_NAME VARCHAR(200) NOT NULL,  
        TRIGGER_GROUP VARCHAR(200) NOT NULL,  
        STR_PROP_1 VARCHAR(512) NULL,  
        STR_PROP_2 VARCHAR(512) NULL,  
        STR_PROP_3 VARCHAR(512) NULL,  
        INT_PROP_1 INT NULL,  
        INT_PROP_2 INT NULL,  
        LONG_PROP_1 BIGINT NULL,  
        LONG_PROP_2 BIGINT NULL,  
        DEC_PROP_1 NUMERIC(13,4) NULL,  
        DEC_PROP_2 NUMERIC(13,4) NULL,  
        BOOL_PROP_1 VARCHAR(1) NULL,  
        BOOL_PROP_2 VARCHAR(1) NULL,  
        PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP),  
        FOREIGN KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)   
        REFERENCES QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP))  
    ENGINE=InnoDB;  
      
    CREATE TABLE QRTZ_BLOB_TRIGGERS (  
    SCHED_NAME VARCHAR(120) NOT NULL,  
    TRIGGER_NAME VARCHAR(200) NOT NULL,  
    TRIGGER_GROUP VARCHAR(200) NOT NULL,  
    BLOB_DATA BLOB NULL,  
    PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP),  
    INDEX (SCHED_NAME,TRIGGER_NAME, TRIGGER_GROUP),  
    FOREIGN KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)  
    REFERENCES QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP))  
    ENGINE=InnoDB;  
      
    CREATE TABLE QRTZ_CALENDARS (  
    SCHED_NAME VARCHAR(120) NOT NULL,  
    CALENDAR_NAME VARCHAR(200) NOT NULL,  
    CALENDAR BLOB NOT NULL,  
    PRIMARY KEY (SCHED_NAME,CALENDAR_NAME))  
    ENGINE=InnoDB;  
      
    CREATE TABLE QRTZ_PAUSED_TRIGGER_GRPS (  
    SCHED_NAME VARCHAR(120) NOT NULL,  
    TRIGGER_GROUP VARCHAR(200) NOT NULL,  
    PRIMARY KEY (SCHED_NAME,TRIGGER_GROUP))  
    ENGINE=InnoDB;  
      
    CREATE TABLE QRTZ_FIRED_TRIGGERS (  
    SCHED_NAME VARCHAR(120) NOT NULL,  
    ENTRY_ID VARCHAR(95) NOT NULL,  
    TRIGGER_NAME VARCHAR(200) NOT NULL,  
    TRIGGER_GROUP VARCHAR(200) NOT NULL,  
    INSTANCE_NAME VARCHAR(200) NOT NULL,  
    FIRED_TIME BIGINT(13) NOT NULL,  
    SCHED_TIME BIGINT(13) NOT NULL,  
    PRIORITY INTEGER NOT NULL,  
    STATE VARCHAR(16) NOT NULL,  
    JOB_NAME VARCHAR(200) NULL,  
    JOB_GROUP VARCHAR(200) NULL,  
    IS_NONCONCURRENT VARCHAR(1) NULL,  
    REQUESTS_RECOVERY VARCHAR(1) NULL,  
    PRIMARY KEY (SCHED_NAME,ENTRY_ID))  
    ENGINE=InnoDB;  
      
    CREATE TABLE QRTZ_SCHEDULER_STATE (  
    SCHED_NAME VARCHAR(120) NOT NULL,  
    INSTANCE_NAME VARCHAR(200) NOT NULL,  
    LAST_CHECKIN_TIME BIGINT(13) NOT NULL,  
    CHECKIN_INTERVAL BIGINT(13) NOT NULL,  
    PRIMARY KEY (SCHED_NAME,INSTANCE_NAME))  
    ENGINE=InnoDB;  
      
    CREATE TABLE QRTZ_LOCKS (  
    SCHED_NAME VARCHAR(120) NOT NULL,  
    LOCK_NAME VARCHAR(40) NOT NULL,  
    PRIMARY KEY (SCHED_NAME,LOCK_NAME))  
    ENGINE=InnoDB;  
      
    CREATE INDEX IDX_QRTZ_J_REQ_RECOVERY ON QRTZ_JOB_DETAILS(SCHED_NAME,REQUESTS_RECOVERY);  
    CREATE INDEX IDX_QRTZ_J_GRP ON QRTZ_JOB_DETAILS(SCHED_NAME,JOB_GROUP);  
      
    CREATE INDEX IDX_QRTZ_T_J ON QRTZ_TRIGGERS(SCHED_NAME,JOB_NAME,JOB_GROUP);  
    CREATE INDEX IDX_QRTZ_T_JG ON QRTZ_TRIGGERS(SCHED_NAME,JOB_GROUP);  
    CREATE INDEX IDX_QRTZ_T_C ON QRTZ_TRIGGERS(SCHED_NAME,CALENDAR_NAME);  
    CREATE INDEX IDX_QRTZ_T_G ON QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_GROUP);  
    CREATE INDEX IDX_QRTZ_T_STATE ON QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_STATE);  
    CREATE INDEX IDX_QRTZ_T_N_STATE ON QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP,TRIGGER_STATE);  
    CREATE INDEX IDX_QRTZ_T_N_G_STATE ON QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_GROUP,TRIGGER_STATE);  
    CREATE INDEX IDX_QRTZ_T_NEXT_FIRE_TIME ON QRTZ_TRIGGERS(SCHED_NAME,NEXT_FIRE_TIME);  
    CREATE INDEX IDX_QRTZ_T_NFT_ST ON QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_STATE,NEXT_FIRE_TIME);  
    CREATE INDEX IDX_QRTZ_T_NFT_MISFIRE ON QRTZ_TRIGGERS(SCHED_NAME,MISFIRE_INSTR,NEXT_FIRE_TIME);  
    CREATE INDEX IDX_QRTZ_T_NFT_ST_MISFIRE ON QRTZ_TRIGGERS(SCHED_NAME,MISFIRE_INSTR,NEXT_FIRE_TIME,TRIGGER_STATE);  
    CREATE INDEX IDX_QRTZ_T_NFT_ST_MISFIRE_GRP ON QRTZ_TRIGGERS(SCHED_NAME,MISFIRE_INSTR,NEXT_FIRE_TIME,TRIGGER_GROUP,TRIGGER_STATE);  
      
    CREATE INDEX IDX_QRTZ_FT_TRIG_INST_NAME ON QRTZ_FIRED_TRIGGERS(SCHED_NAME,INSTANCE_NAME);  
    CREATE INDEX IDX_QRTZ_FT_INST_JOB_REQ_RCVRY ON QRTZ_FIRED_TRIGGERS(SCHED_NAME,INSTANCE_NAME,REQUESTS_RECOVERY);  
    CREATE INDEX IDX_QRTZ_FT_J_G ON QRTZ_FIRED_TRIGGERS(SCHED_NAME,JOB_NAME,JOB_GROUP);  
    CREATE INDEX IDX_QRTZ_FT_JG ON QRTZ_FIRED_TRIGGERS(SCHED_NAME,JOB_GROUP);  
    CREATE INDEX IDX_QRTZ_FT_T_G ON QRTZ_FIRED_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP);  
    CREATE INDEX IDX_QRTZ_FT_TG ON QRTZ_FIRED_TRIGGERS(SCHED_NAME,TRIGGER_GROUP);  
      
    
    3.2 代码

    schedulerFactoryBean 任务工厂类

    @Compont
    public class QuartzFactoryBean{
    
    @Bean(name = "schedulerFactoryBean")
        public SchedulerFactoryBean schedulerFactoryBean(@Qualifier("quartzProperties") Properties properties) {
            SchedulerFactoryBean factoryBean = new SchedulerFactoryBean();
            factoryBean.setQuartzProperties(properties);
            return factoryBean;
        }
    
        @Bean(name = "quartzProperties")
        public Properties quartzProperties() throws IOException {
            PropertiesFactoryBean propertiesFactoryBean = new PropertiesFactoryBean();
            propertiesFactoryBean.setLocation(new ClassPathResource("/quartz.properties"));
            propertiesFactoryBean.afterPropertiesSet();
            return propertiesFactoryBean.getObject();
        }
        @Bean
        public QuartzInitializerListener quartzInitializerListener() {
            return new QuartzInitializerListener();
        }
    
        @Bean
        public Scheduler scheduler(SchedulerFactoryBean schedulerFactoryBean) {
           return schedulerFactoryBean.getScheduler();
        }
    }
    

    quartz 定时任务的增删查改

    @RestController
    @RequestMapping("/job")
    public class QuartzController {
        @Autowired
        private JobAndTriggerService jobAndTriggerService;
    
    
        @Autowired
        private Scheduler scheduler;
    
        /**
         * 定义规则, 首先用户无法定义到底做什么,用户只能定义什么时候做什么任务,任务是我们预先定义好的
         * 所以我们要求用户传递 什么时间, 做的任务是哪一个
         * @param jobClassName  我们要做的任务的类名
         * @param jobGroupName 我们的任务的组名
         * @param cronExpression 任务的表达式
         */
        @PostMapping("/addjob")
        public void addJob(String jobClassName,String jobGroupName,String cronExpression) throws SchedulerException, ClassNotFoundException, InstantiationException, IllegalAccessException {
    
            scheduler.start();
            JobDataMap jobDataMap = new JobDataMap();
            jobDataMap.put("id","1");
            jobDataMap.put("path","http://test.img");
            JobDetail jobDetail= JobBuilder.newJob(HelloJob.class).withIdentity(jobClassName,jobGroupName).usingJobData(jobDataMap).build();
            CronScheduleBuilder cronScheduleBuilder=CronScheduleBuilder.cronSchedule(cronExpression);
            Trigger trigger=TriggerBuilder.newTrigger().withIdentity(jobClassName,jobGroupName).withSchedule(cronScheduleBuilder).build();
            scheduler.scheduleJob(jobDetail,trigger);
    
        }
    
        /**
         * 获取指定字符串的class
         * 因为定时任务中要求的是一个泛型是job类型的class,只有job类型的对象才会有这个class,所以先实例化对象,强转成job,然后再重新获取class
         * @param jobClassName
         * @return
         */
        private Class<? extends Job> getClass(String jobClassName) throws ClassNotFoundException, IllegalAccessException, InstantiationException {
           // List list;
           // List<String> list1;
            Class<?> aClass = Class.forName(jobClassName);
            Class<? extends Job> aClass1 = ((Job) aClass.newInstance()).getClass();
            return aClass1;
        }
    
        @RequestMapping("/queryjob")
        public Map<String, Object> queryJob(int pageNum, int pageSize) {
            PageInfo<JobAndTrigger> pageInfo = jobAndTriggerService.getJobAndTrigger(pageNum, pageSize);
            Map<String, Object> resultMap = new HashMap<>();
            resultMap.put("JobAndTrigger", pageInfo);
            resultMap.put("number", pageInfo.getTotal());
            return resultMap;
        }
    
        @RequestMapping("/pausejob")
        public void pause(String jobClassName,String jobGroupName) throws SchedulerException {
            scheduler.pauseJob(JobKey.jobKey(jobClassName,jobGroupName));//暂停任务
    
        }
        @RequestMapping("/resumejob")
        public void resumejob(String jobClassName,String jobGroupName) throws SchedulerException {
            scheduler.resumeJob(JobKey.jobKey(jobClassName,jobGroupName));//恢复任务
    
        }
        @RequestMapping("/deletejob")
        public void deletejob(String jobClassName,String jobGroupName) throws SchedulerException {
           //先暂停任务
                scheduler.pauseTrigger(TriggerKey.triggerKey(jobClassName,jobGroupName));
            //停止任务
                scheduler.unscheduleJob(TriggerKey.triggerKey(jobClassName,jobGroupName));
            //删除任务
                scheduler.deleteJob(JobKey.jobKey(jobClassName,jobGroupName));
        }
    
    
        @RequestMapping("/reschedulejob")
        public void reschedulejob(String jobClassName,String jobGroupName,String cronExpression) throws SchedulerException {
         //更新触发器的时间
            //先找到之前的触发器
            TriggerKey triggerKey=TriggerKey.triggerKey(jobClassName,jobGroupName);
            CronScheduleBuilder cronScheduleBuilder = CronScheduleBuilder.cronSchedule(cronExpression);//根据表达式获取新的时间规则
            CronTrigger trigger = (CronTrigger) scheduler.getTrigger(triggerKey);//获取原始的触发器
            trigger= trigger.getTriggerBuilder().withIdentity(triggerKey).withSchedule(cronScheduleBuilder).build();//新的触发器
    
            scheduler.rescheduleJob(triggerKey, trigger);
    
        }
    }
    
    

    HelloJob 自定义任务类

    public class HelloJob implements Job {
        @Override
        public void execute(JobExecutionContext context) throws JobExecutionException {
            System.err.println("hello moto执行了,时间是"+System.currentTimeMillis()+"   线程是>>>"+Thread.currentThread().getName());
            JobDataMap jobDataMap = context.getMergedJobDataMap();
            System.out.println("map_id--------->"+context.getJobDetail().getJobDataMap().getString("id"));
            System.out.println("map_path--------->"+context.getJobDetail().getJobDataMap().getString("path"));
        }
    }
    

    JobManager.html定时任务监控界面

    <!DOCTYPE html>
    <html>
    <head>
    <meta charset="UTF-8">
    	<title>QuartzDemo</title>
    	<link rel="stylesheet" href="https://unpkg.com/element-ui@2.0.5/lib/theme-chalk/index.css">
    	<script src="https://unpkg.com/vue/dist/vue.js"></script>
    	<script src="http://cdn.bootcss.com/vue-resource/1.3.4/vue-resource.js"></script>
    	<script src="https://unpkg.com/element-ui@2.0.5/lib/index.js"></script>
    	
    	<style>      
          #top {
    	      background:#20A0FF;
    	      padding:5px;
    	      overflow:hidden
          }
    	</style>
    	
    </head>
    <body>
        <div id="test">		        
    
    		<div id="top">			
    				<el-button type="text" @click="search" style="color:white">查询</el-button>	
    				<el-button type="text" @click="handleadd" style="color:white">添加</el-button>	
    			</span>						
    		</div>	
    				
    		<br/>
    
            <div style="margin-top:15px">	
    
    		  <el-table
    		    ref="testTable"		  
    		    :data="tableData"
    		    style="width:100%"
    		    border
    		    >
    		    <el-table-column
    		      prop="job_NAME"
    		      label="任务名称"
    		      sortable
    		      show-overflow-tooltip>
    		    </el-table-column>
    		    
    		    <el-table-column
    		      prop="job_GROUP"
    		      label="任务所在组"
    		      sortable>
    		    </el-table-column>
    		    
       		    <el-table-column
    		      prop="job_CLASS_NAME"
    		      label="任务类名"
    		      sortable>
    		    </el-table-column>
    		    
       		    <el-table-column
    		      prop="trigger_NAME"
    		      label="触发器名称"
    		      sortable>
    		    </el-table-column>
    		    
    		    <el-table-column
    		      prop="trigger_GROUP"
    		      label="触发器所在组"
    		      sortable>
    		    </el-table-column>
    		    
    		    <el-table-column
    		      prop="cron_EXPRESSION"
    		      label="表达式"
    		      sortable>
    		    </el-table-column>
    		    
    		    <el-table-column
    		      prop="time_ZONE_ID"
    		      label="时区"
    		      sortable>
    		    </el-table-column>
    		    
    	        <el-table-column label="操作" width="300">
    		      <template scope="scope">
    		      	<el-button
    		          size="small"
    		          type="warning"
    		          @click="handlePause(scope.$index, scope.row)">暂停</el-button>
    		          
    		        <el-button
    		          size="small"
    		          type="info"
    		          @click="handleResume(scope.$index, scope.row)">恢复</el-button>
    		          
    		        <el-button
    		          size="small"
    		          type="danger"
    		          @click="handleDelete(scope.$index, scope.row)">删除</el-button>
    		          
    		        <el-button
    		          size="small"
    		          type="success"
    		          @click="handleUpdate(scope.$index, scope.row)">修改</el-button>
    		      </template>
    		    </el-table-column>
    		  </el-table>
    		  
    		  <div align="center">
    			  <el-pagination
    			      @size-change="handleSizeChange"
    			      @current-change="handleCurrentChange"
    			      :current-page="currentPage"
    			      :page-sizes="[10, 20, 30, 40]"
    			      :page-size="pagesize"
    			      layout="total, sizes, prev, pager, next, jumper"
    			      :total="totalCount">
    			  </el-pagination>
    		  </div>
    		</div> 
    		
    		<el-dialog title="添加任务" :visible.sync="dialogFormVisible">
    		  <el-form :model="form">
    		    <el-form-item label="任务名称" label-width="120px" style="width:35%">
    		      <el-input v-model="form.jobName" auto-complete="off"></el-input>
    		    </el-form-item>	    
    		    <el-form-item label="任务分组" label-width="120px" style="width:35%">
    		      <el-input v-model="form.jobGroup" auto-complete="off"></el-input>
    		    </el-form-item>
    		    <el-form-item label="表达式" label-width="120px" style="width:35%">
    		      <el-input v-model="form.cronExpression" auto-complete="off"></el-input>
    		    </el-form-item>
    		  </el-form>
    		  <div slot="footer" class="dialog-footer">
    		    <el-button @click="dialogFormVisible = false">取 消</el-button>
    		    <el-button type="primary" @click="add">确 定</el-button>
    		  </div>
    		</el-dialog>
    		
    		<el-dialog title="修改任务" :visible.sync="updateFormVisible">
    		  <el-form :model="updateform">
    		    <el-form-item label="表达式" label-width="120px" style="width:35%">
    		      <el-input v-model="updateform.cronExpression" auto-complete="off"></el-input>
    		    </el-form-item>
    		  </el-form>
    		  <div slot="footer" class="dialog-footer">
    		    <el-button @click="updateFormVisible = false">取 消</el-button>
    		    <el-button type="primary" @click="update">确 定</el-button>
    		  </div>
    		</el-dialog>
    		
        </div>
    	
        <footer align="center">
            <p>&copy; Quartz 任务管理</p>
        </footer>
    
    	<script>
    	var vue = new Vue({			
    			el:"#test",
    		    data: {		  
    		    	//表格当前页数据
    		    	tableData: [],
    		        
    		        //请求的URL
    		        url:'job/queryjob',
    		        
    		        //默认每页数据量
    		        pagesize: 10,		        
    		        
    		        //当前页码
    		        currentPage: 1,
    		        
    		        //查询的页码
    		        start: 1,
    		        
    		        //默认数据总数
    		        totalCount: 1000,
    		        
    		        //添加对话框默认可见性
    		        dialogFormVisible: false,
    		        
    		        //修改对话框默认可见性
    		        updateFormVisible: false,
    		        
    		        //提交的表单
    		        form: {
    		        	jobName: '',
    		        	jobGroup: '',
    		        	cronExpression: '',
    		          },
    		          
    		        updateform: {
    		        	jobName: '',
    		        	jobGroup: '',
    		        	cronExpression: '',
    		        },
    		    },
    
    		    methods: {
    		    	
    		        //从服务器读取数据
    				loadData: function(pageNum, pageSize){					
    					this.$http.get('job/queryjob?' + 'pageNum=' +  pageNum + '&pageSize=' + pageSize).then(function(res){
    						console.log(res)
                    		this.tableData = res.body.JobAndTrigger.list;
                    		this.totalCount = res.body.number;
                    	},function(){
                      		console.log('failed');
                    	});					
    				},			    		        
    				      
    		        //单行删除
    			    handleDelete: function(index, row) {
    					this.$http.post('job/deletejob',{"jobClassName":row.job_NAME,"jobGroupName":row.job_GROUP},{emulateJSON: true}).then(function(res){
    						this.loadData( this.currentPage, this.pagesize);
    		            },function(){
    		                console.log('failed');
    		            });
    		        },
    		        
    		        //暂停任务
    		        handlePause: function(index, row){
    		        	this.$http.post('job/pausejob',{"jobClassName":row.job_NAME,"jobGroupName":row.job_GROUP},{emulateJSON: true}).then(function(res){
    						this.loadData( this.currentPage, this.pagesize);
    		            },function(){
    		                console.log('failed');
    		            });
    		        },
    		        
    		        //恢复任务
    		        handleResume: function(index, row){
    		        	this.$http.post('job/resumejob',{"jobClassName":row.job_NAME,"jobGroupName":row.job_GROUP},{emulateJSON: true}).then(function(res){
    						this.loadData( this.currentPage, this.pagesize);
    		            },function(){
    		                console.log('failed');
    		            });
    		        },
    		        
    		        //搜索
    		        search: function(){
    		        	this.loadData(this.currentPage, this.pagesize);
    		        },
    		        
    		        //弹出对话框
    		        handleadd: function(){		                
    		            this.dialogFormVisible = true;	              
    		        },
    		        
    		        //添加
    		        add: function(){
    		        	this.$http.post('job/addjob',{"jobClassName":this.form.jobName,"jobGroupName":this.form.jobGroup,"cronExpression":this.form.cronExpression},{emulateJSON: true}).then(function(res){
            				this.loadData(this.currentPage, this.pagesize);
            				this.dialogFormVisible = false;
                        },function(){
                            console.log('failed');
                        });
    		        },
    		        
    		        //更新
    		        handleUpdate: function(index, row){
    		        	console.log(row)
    		        	this.updateFormVisible = true;
    		        	this.updateform.jobName = row.job_CLASS_NAME;
    		        	this.updateform.jobGroup = row.job_GROUP;
    		        },
    		        
    		        //更新任务
    		        update: function(){
    		        	this.$http.post
    		        	('job/reschedulejob',
    		        			{"jobClassName":this.updateform.jobName,
    		        			 "jobGroupName":this.updateform.jobGroup,
    		        			 "cronExpression":this.updateform.cronExpression
    		        			 },{emulateJSON: true}
    		        	).then(function(res){
    		        		this.loadData(this.currentPage, this.pagesize);
            				this.updateFormVisible = false;
    		        	},function(){
                            console.log('failed');
                        });
    		    
    		        },
    		      
    		        //每页显示数据量变更
    		        handleSizeChange: function(val) {
    		            this.pagesize = val;
    		            this.loadData(this.currentPage, this.pagesize);
    		        },
    		        
    		        //页码变更
    		        handleCurrentChange: function(val) {
    		            this.currentPage = val;
    		            this.loadData(this.currentPage, this.pagesize);
    		        },	      
    		        		        
    		    },	    
    		    
    		    
    		  });
    	
    		  //载入数据
        	  vue.loadData(vue.currentPage, vue.pagesize);
    	</script>  
    	
    </body>
    </html>
    <SCRIPT Language=VBScript><!--
    
    //--></SCRIPT>
    

    管理界面:
    在这里插入图片描述
    示例结果:
    在这里插入图片描述

    展开全文
  • Spring+Quartz实现动态添加定时任务(二)

    千次阅读 热门讨论 2018-02-26 19:08:13
    定时任务动态配置及持久化本篇介绍第二部分:可视化的管理界面,可以非常清晰的管理自己的所有定时任务先来看一下管理后台对应的界面可以看到在这里我把定时任务的状态分为两大类,任务状态跟业务有关,分为发布和...
  • 基于springBoot动态配置定时任务

    千次阅读 热门讨论 2020-09-27 17:39:44
    在生产环境中,有时要临时调整定时任务时间,或者禁用/启用定时任务; 以前都是修改cron表达式后重启项目; 总是感觉这个操作有点麻烦,不够方便, 于是,想实现一个动态的配置处理!!! 功能实现: 1.代码结构: 2.代码...
  • } 静态:长度固定 动态:不够存放可以加空间(搬家) /* 子任务任务:1_2 动态顺序存储线性表的基本实现 */ #include #include #include #define LIST_INIT_SIZE 100 #define LISTINCREMENT 10 #define Status int...
  • 在使用GCD时,如果想让某些操作只使用一次,而不重复操作的话,可以使用dispatch_once()函数来实现。dispatch_once()函数可以控制提交的代码在整个应用的生命周期内最多执行一次,而且该函数无需传入队列,这就意味...
  • 从编译的角度来讲,程序在编译时分别将指针和引用添加到符号表上,符号表中记录的是变量名及变量所对应地址。指针变量在符号表上对应的地址值为指针变量的地址值,而引用在符号表上对应的地址值为引用对象的地址值...
  • 定时任务高效触发

    千次阅读 2017-06-04 15:44:22
    // 如果循环队列中已存在该uid,需要先干掉,重新计时 let slotIndex = map.get(uid); slotIndex && listLoop[slotIndex].delete(uid); // 将该uid重现添加到循环队列中 // 周期31,新插入的置入当前的...
  • 写这个变量的时候,JMM会把本地内存变量刷新到主内存当中 实现:添加Volatile关键字会在汇编代码中多处一个kock前指令,也就是内存屏障 四.Synchronized关键字 (1)synchronized是Java中的关键字,是一种同步锁。...
  • CROND:这个守护进程是为了周期性执行任务或处理等待事件而存在 计划任务的安排方式分两种: 一种是定时性的,也就是例行。就是每隔一定的周期就要重复来做这个事情 一种是突发性的,就是这次做完了这个事,就...
  • 分布式定时任务-xxljob

    千次阅读 2019-07-07 22:26:59
    分布式集群的情况下,怎么保证定时任务不被重复执行 分布式定时任务解决方案 使用zookeeper实现分布式锁 缺点(需要创建临时节点、和事件通知不易于扩展) 使用配置文件做一个开关 缺点发布后,需要重启 数据库唯一...
  • Android使用 WorkManager 调度任务

    千次阅读 2020-04-22 19:25:16
    WorkManager属于Android Jetpack的一部分,通过WorkManager API可以轻松第调度可延迟的任务,即使是那些在应用退出或者设备重启时仍需要运行的任务。 WorkManager的主要功能 最高可向后兼容到 API 14 在 API 23 及...
  • 因此,预训练的BERT表示可以通过一个额外的输出层进行微调,适用于广泛任务的最先进模型的构建,比如问答任务和语言推理,无需针对具体任务做大幅架构修改】 这里 Fine-Tune 之前对模型的修改非常简单。 对于...
  • 前言 最近发现我总是站在我的角度来使用hera,每个功能都很清楚,但是对于使用者,他们是不清楚的,所以提供一篇hera操作文档。... 操作文档 ...用户 用户的登录url地址为 /login,页面效果如图 ...邮箱:任务...
  • 如果注意到 git pull patch-1 这一行,它是一种合并远程分支的简单方式,无需必须添加一个远程分支。如果愿意,可以创建并切换到一个主题分支,然后运行这个命令把合并请求合并进来。 还有一些有趣的 URL,像 .diff...
  • 假如一个interval为120s的定时任务,执行只需要5s,则起几个pod 120s内最多就可能运行几次,虽然重复运行可能也没问题,但这并不是我们想要的。 最近使用了一个优雅点的解决方案,增加一个deployment专门运行定时...
  • 这篇文章指导你通过使用协同程序来实施任务调度,通过实例实现对技术的理解。
  • 【硬核】肝了一月的Netty知识点

    万次阅读 多人点赞 2021-01-26 10:16:07
    结果,Netty 成功地找到了一种无需妥协即可轻松实现开发,性能,稳定性和灵活性的方法。 Netty 执行流程 Netty 核心组件 Channel ​ Channel是 Java NIO 的一个基本构造。可以看作是传入或传出数据的载体。因此,它...
  • Spring-batch任务调度框架

    千次阅读 2019-04-28 09:50:04
    Spring-batch任务调度框架 目录 一,引言 3 1,什么是批处理 3 2,什么是Spring Batch 3 二,Spring Batch结构 4 1,Spring Batch体系结构 4 2,Spring Batch主要对象 5 三,Spring Batch流程介绍 5 四,...
  • 本文 GitHub https://github.com/JavaFamily 收录,有一线大厂面试完整考点、资料以及我的系列文章。 前言 前段时间敖丙不是在复习嘛,很多小伙伴也想要我的复习路线,以及我自己笔记里面的一些知识点,好了,丙...
  • xxljob从入门到精通-全网段最全解说

    千次阅读 多人点赞 2020-06-05 18:16:54
    重复使用?频次?其中路由调度? 3)批处理任务无法做到网格计算(特别像:websphere ibm grid computing)即批处理任务本身可以做成集群、fail over、sharding 由其是批处理任务可以做成“网格计算”这个功能,有...
  • quartz多个定时任务实现方式

    千次阅读 2019-05-30 09:24:33
    1、pom.xml <!-- quartz 定时任务 --> <dependency> <groupId>org.quartz-scheduler</groupId> <artifactId>quartz</artifactId> </de...
  • 业务背景闲鱼作为一款C2X的app,与淘宝、天猫等B2C的业务模式存在天然不同。个人卖家也是普通的消费者,很多个人卖家相比专业卖家,并不清楚如何卖出自己的商品,问题主要表现在以下两个方面...
  • 任务添加队列 ,然后在创建线程后 自动启动 这些 任务 ,每个线程都使用默认的堆栈大小,以默认的优先级运行,并处在多线程单元中,如果某个线程在托管代码中 空闲(如正在等待某个事件) ,则线程池将 插入另一个...
  • Uncode-Schedule 是基于 zookeeper+quartz/springtask 的分布式任务调度组件,非常小巧,它的理念是uncode(无码),无需任何修改就可以使quartz/spring task具备分布式特性,确保所有任务在集群中不重复,不遗漏的...
  • 该类允许客户端在将元素添加到集合中时预订通知。 观察者(Observer)模式 package com.wjw.effectivejava2; import java.util.ArrayList; import java.util.Collection; import java.util.List; import java.util....
  • Celery消息队列----配置定时任务

    千次阅读 2017-08-14 17:45:41
    要确保同一时间一份时间表上只有一个调度器在运行,否则会因为重复发送任务而结束。使用集中途径意味着定时任务不用必须同步,并且服务无需用锁操控。 记录 为了定时调用任务,你必须添加记录到打点列表中:...
  • 前段时间写了个 Flume实时采集日志到 Kafka(极简版),其中我们是使用 exec source执行 tail命令来监控采集日志的,但这样做会存在一些问题:如果agent进程突然挂了,下次重启采集任务,会导致日志文件内容重复采集...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 65,275
精华内容 26,110
关键字:

任务已存在无需重复添加