美梦&热望


  • 首页

  • 关于

  • 标签

  • 分类

  • 归档

乐观锁&悲观锁

发表于 2019-02-18

乐观锁&悲观锁

悲观锁

总是假设最坏的情况,每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,这样别人想拿这个数据就会阻塞直到它拿到锁。传统的关系型数据库里边就用到了很多这种锁机制,比如行锁,表锁等,读锁,写锁等,都是在做操作之前先上锁。

再比如Java里面的同步原语synchronized关键字的实现也是悲观锁。

乐观锁

顾名思义,就是很乐观,每次去拿数据的时候都认为别人不会修改,所以不会上锁,但是在更新的时候会判断一下在此期间别人有没有去更新这个数据,可以使用版本号等机制。

乐观锁适用于多读的应用类型,这样可以提高吞吐量,像数据库提供的类似于write_condition机制,其实都是提供的乐观锁。在Java中java.util.concurrent.atomic包下面的原子变量类就是使用了乐观锁的一种实现方式CAS实现的。

乐观锁的实现可以通过设置一个version字段,每次更新的时候按照version条件去更新,version不匹配则更新失败

https://www.cnblogs.com/qjjazry/p/6581568.html

中心化与去中心化

发表于 2019-02-18 | 分类于 中心化与去中心化

布莱夫曼2006年出版了一本《海星与蜘蛛》的书。这本书说的是去中心化的组织结构。

中央集权的组织都是自上而下的控制命令,这种组织就像蜘蛛一样。蜘蛛型组织都有一个中央枢纽,如果这个中央枢纽不好使,整个系统就瘫痪了。

海星这种生物是没有头的,它的智能控制分布在肢体的各个部分,砍掉海星的一条手臂,这个手臂可以演化成另一个海星。海星就是一个去中心化的组织系统。

打一个比方来说,现在的美军去打恐怖主义组织。美军就是一个蜘蛛系统,是由美军五角大楼统一指挥的,而恐怖组织就是海星系统,那么,恐怖组织的头是谁呢?如果可以总结的话,去中心化组织的头就是一种理念。

去中心化之所以能够运转,核心就是一种理念,这种理念灌输到每一个小组织中。所有人都认同这种理念,才有可能像开连锁店一样,谁想开就开一家。每一个小组织,就是一个圈子。

所有的圈子都有他自己存活的环境和条件,但只需要所有的圈子都认同一个理念,那么这个去中心化的组织,才有活力才有存活下去的理由。

一个被共同认同的理念不能太长,十个字以内,需要被人能够记住,同时能够被去中心化组织内部的圈子认同。

比如说,以爱连接家庭。

xxl-job是中心化的,调度中心虽然可以多部署几个,也相当与备份;没有调度中心,执行器无法工作。

elastic-job是去中心化的,每个客户端所在应用都有自治功能,都可以触发调度,若干个应用挂了,对其他应用没有影响。

CAS了解

发表于 2019-02-15

CAS(Compare And Set)
Compare And Set(或Compare And Swap),CAS是解决多线程并行情况下使用锁造成性能损耗的一种机制,CAS操作包含三个操作数——内存位置(V)、预期原值(A)、新值(B)

如果内存位置的值与预期原值相匹配,那么处理器会自动将该位置值更新为新值。否则,处理器不做任何操作。
无论哪种情况,它都会在CAS指令之前返回该位置的值。
CAS有效地说明了“我认为位置V应该包含值A;如果包含该值,则将B放到这个位置;否则,不要更改该位置,只告诉我这个位置现在的值即可。

使用场景

众所周知锁有两种:乐观锁与悲观锁。独占锁是一种悲观锁,而 synchronized 就是一种独占锁,synchronized 会导致其它所有未持有锁的线程阻塞,而等待持有锁的线程释放锁。
所谓乐观锁就是,每次不加锁而是假设没有冲突而去完成某项操作,如果因为冲突失败就重试,直到成功为止。而乐观锁用到的机制就是CAS。

https://286.iteye.com/blog/2295165

xxl-job学习笔记

发表于 2019-01-30

###

quartz简介

发表于 2019-01-29

Quartz Scheduler 开源框架
Quartz 是 OpenSymphony 开源组织在任务调度领域的一个开源项目,完全基于 Java 实现

Spring 默认的调度框架,Quartz 很容易与 Spring 集成实现灵活可配置的调度功能

名词解释

  • Trigger 触发器,用于定义任务调度时间规则

  • Scheduler 任务调度器,Scheduler有两个重要组件:ThreadPool和JobStore

  • Job 任务 也就是表示被调度的任务.JobDetail是任务的定义,而Job是任务的执行逻辑。在JobDetail里会引用一个Job Class定义

  • misfire 错过的,指本来应该被执行但实际没有被执行的任务调度

原理

Quartz 任务调度的核心元素是 scheduler, trigger 和 job,其中 trigger 和 job 是任务调度的元数据, scheduler 是实际执行调度的控制器。

trigger 是用于定义调度时间的元素,即按照什么时间规则去执行任务

  • SimpleTrigger
  • CronTirgger
  • DateIntervalTrigger
  • NthIncludedDayTrigger

job 用于表示被调度的任务

  • 无状态的(stateless)
  • 有状态的(stateful)

对于同一个 trigger 来说,有状态的 job 不能被并行执行,只有上一次触发的任务被执行完之后,才能触发下一次执行
Job 主要有两种属性:
volatility
durability

其中 volatility 表示任务是否被持久化到数据库存储,而 durability 表示在没有 trigger 关联的时候任务是否被保留。
两者都是在值为 true 的时候任务被持久化或保留。一个 job 可以被多个 trigger 关联,但是一个 trigger 只能关联一个 job。

scheduler

scheduler 由 scheduler 工厂创建:DirectSchedulerFactory 或者 StdSchedulerFactory。
第二种工厂 StdSchedulerFactory 使用较多,因为 DirectSchedulerFactory 使用起来不够方便,需要作许多详细的手工编码设置。
Scheduler 主要有三种:RemoteMBeanScheduler, RemoteScheduler 和 StdScheduler。
本文以最常用的 StdScheduler 为例讲解。

Quartz 核心元素之间的关系如下图所示
https://www.ibm.com/developerworks/cn/opensource/os-cn-quartz/image001.gif

线程视图
在 Quartz 中,有两类线程,Scheduler 调度线程和任务执行线程,其中任务执行线程通常使用一个线程池维护一组线程

https://www.ibm.com/developerworks/cn/opensource/os-cn-quartz/image002.gif

Scheduler 调度线程主要有两个: 执行常规调度的线程,和执行 misfired trigger 的线程。
常规调度线程轮询存储的所有 trigger,如果有需要触发的 trigger,即到达了下一次触发的时间,则从任务执行线程池获取一个空闲线程,执行与该 trigger 关联的任务。
Misfire 线程是扫描所有的 trigger,查看是否有 misfired trigger,如果有的话根据 misfire 的策略分别处理。下图描述了这两个线程的基本流程:

https://www.ibm.com/developerworks/cn/opensource/os-cn-quartz/image003.png

数据存储

Quartz 中的 trigger 和 job 需要存储下来才能被使用。
Quartz 中有两种存储方式:RAMJobStore, JobStoreSupport,

  • RAMJobStore 是将 trigger 和 job 存储在内存中,
  • JobStoreSupport 是基于 jdbc 将 trigger 和 job 存储到数据库中。

RAMJobStore 的存取速度非常快,但是由于其在系统被停止后所有的数据都会丢失,所以在通常应用中,都是使用 JobStoreSupport。

在 Quartz 中,JobStoreSupport 使用一个驱动代理来操作 trigger 和 job 的数据存储:StdJDBCDelegate。
StdJDBCDelegate 实现了大部分基于标准 JDBC 的功能接口,但是对于各种数据库来说,需要根据其具体实现的特点做某些特殊处理,因此各种数据库需要扩展 StdJDBCDelegate 以实现这些特殊处理。
Quartz 已经自带了一些数据库的扩展实现,可以直接使用,如下图所示:

https://www.ibm.com/developerworks/cn/opensource/os-cn-quartz/image004.png

一个简单实例

Quartz 开发包中有一个 examples 目录,其中有 15 个基本实例。建议读者阅读并实践这些例子。本文这里只列举一个小的实例,介绍基本的开发方法。

准备数据库和 Quartz 用的数据表

本文使用 IBM DB2 数据库:将 jdbc 驱动程序 db2jcc.jar 加入到项目中;
在数据库中创建一个新库 QUARTZDB;
执行 /quartz-1.8.4/docs/dbTables/tables_db2_v8.sql,创建数据表;表建好后如下所示:

图 6. Quartz 数据表
https://www.ibm.com/developerworks/cn/opensource/os-cn-quartz/image006.png

准备配置文件,加入到项目中

图 7. 实例配置文件

https://www.ibm.com/developerworks/cn/opensource/os-cn-quartz/image007.png

通过实现 job 接口定义我们自己的任务类,如下所示:

图 8. 定义任务类

https://www.ibm.com/developerworks/cn/opensource/os-cn-quartz/image008.png

然后,实现任务调度的主程序,如下所示:

本实例中,我们利用 DateIntervalTrigger 实现一个每两分钟执行一次的任务调度。

图 9. 实现主程序

https://www.ibm.com/developerworks/cn/opensource/os-cn-quartz/image009.png

完成后项目结构如下所示:

图 10. 实例项目结构图

https://www.ibm.com/developerworks/cn/opensource/os-cn-quartz/image010.png

运行程序,查看数据库表和运行结果

数据库中,QRTZ_TRIGGERS 表中添加了一条 trigger 记录,如下所示:

图 11. QRTZ_TRIGGERS 表中的记录

https://www.ibm.com/developerworks/cn/opensource/os-cn-quartz/image011.png

QRTZ_JOB_DETAILS 表中添加了一条 job 记录,如下所示:

图 12. QRTZ_JOB_DETAILES 表中的记录
https://www.ibm.com/developerworks/cn/opensource/os-cn-quartz/image012.png

从运行结果来看,任务每两分钟被执行一次:

图 13. 运行结果
https://www.ibm.com/developerworks/cn/opensource/os-cn-quartz/image013.png

参考文章

https://www.ibm.com/developerworks/cn/opensource/os-cn-quartz/
http://ifeve.com/quartz-tutorial-04-trigger/

kong管理界面搭建

发表于 2019-01-29 | 分类于 Kong

安装nodejs

-下载

1
2
3
$ wget -p ~ https://nodejs.org/dist/v10.15.0/node-v10.15.0-linux-x64.tar.xz
$ tar -C ~ -xf node-v10.15.0-linux-x64.tar.xz
$ mv ~/node-v10.15.0-linux-x64 /export/servers/nodejs

  • 配置环境变量
  • nodejs

    1
    2
    export NODEJS_HOME=/export/servers/nodejs
    PATH=$PATH:$NODEJS_HOME/bin
  • 检测

    1
    $ npm version

安装konga

安装过程比较简单,唯一要注意的是,正常情况下,解压好konga的包后,执行npm install下载各种依赖就可以了,但是公司线上服务器无法直接git下载代码,可以找一台可以联网和执行git的linux服务器把konga的依赖安装好,然后整体打包,放到线上服务器部署,下面的步骤就是这个方式。

下载konga

1
2
3
$ wget http://duck.jdfin.local/dav/test.tar  #已经安装好依赖的konga
$ tar -xf test.tar
$ mv konga /export/servers

修改konga的配置

  • 先复制一份local的配置信息,里面主要是启动的端口,选择的数据库

    1
    $ cp /export/servers/konga-0.13.0/config/local_example.js /export/servers/konga-0.13.0/config/local.js
  • 修改local.js示例如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
module.exports = {
connections: {
},
models: {
connection: 'postgres',
},
session: {
secret: ''
},
port: 9338,
environment: 'development',
log: {
level: 'info'
}
};

修改connection中数据库配置信息

1
$ vi /export/servers/konga-0.13.0/config/connections.js

修改connections.js示例如下:

  • 因为只用了postgres,只保留它的配置就可以了
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    module.exports.connections = {
    postgres: {
    adapter: 'sails-postgresql',
    url: process.env.DB_URI,
    host: 'localhost',
    user: 'admin',
    password: 'ds2f84#@!5Q1',
    port: 5432,
    database: 'konga_database',
    // schema: process.env.DB_PG_SCHEMA ||'public',
    poolSize: 20,
    ssl: false // If set, assume it's true
    },
    };

初始化数据库

1
$ node /export/servers/konga-0.13.0/bin/konga.js prepare --adapter postgres --uri postgresql://:kong@localhost:5432/konga_database

启动konga

1
2
3
$ cd /export/servers/konga-0.13.0/
$ npm run production
$ nohup npm run production > /export/Logs/konga-admin/nohup.log 2>&1 & #后台运行

kong搭建

发表于 2019-01-29 | 分类于 Kong

安装 postgersql

  • 安装,默认安装在/usr/pgsql-11

    1
    2
    3
    $ yum install https://download.postgresql.org/pub/repos/yum/11/redhat/rhel-6-x86_64/pgdg-centos11-11-2.noarch.rpm
    $ yum install postgresql11
    $ yum install postgresql11-server
  • 初始化 -D是指定的数据文件,-X是指定的wal目录,-U 是创建数据账户 -W 会提示输入密码

    1
    $ /usr/pgsql-11/bin/initdb -D /export/Data/postgres -X /export/Logs/postgres -U admin -W
  • 启动

    1
    $ /usr/pgsql-11/bin/pg_ctl -D /export/Data/postgres -l /export/Logs/postgres/postgres.log start
  • 本地登录数据

    1
    $ /usr/pgsql-11/bin/psql -d template1

升级openssl

  • 查看当前openssl版本,如果输出的版本低于:OpenSSL 1.0.2e则需要升级

    1
    $ openssl version
  • 下载

    1
    $ wget https://www.openssl.org/source/openssl-1.0.2-latest.tar.gz
  • 编译安装

    1
    2
    $ ./config --prefix=/usr --openssldir=/usr/openssl shared
    $ make && make install

安装openresty

  • 下载

    1
    $ wget https://openresty.org/download/openresty-1.13.6.2.tar.gz
  • 编译安装

    1
    2
    $ ./configure --with-pcre-jit --with-http_ssl_module --with-http_realip_module --with-http_stub_status_module --with-http_v2_module --prefix=/export/servers/openresty
    $ make && make install

安装luarocks

  • 下载源码
    1
    2
    $ wget http://luarocks.github.io/luarocks/releases/luarocks-3.0.4.tar.gz
    $ tar -xf luarocks-3.0.4.tar.gz

…

  • 编译安
  • 这里我们指定的安装目录是:/export/servers/luarocks-3.0.4
  • 注意这里设置–with-lua和–with-lua-include,需要制定openresty目录下的luajit

    1
    2
    $ ./configure --prefix=/export/servers/luarocks-3.0.4 --with-lua=/export/servers/openresty/luajit --with-lua-include=/export/servers/openresty/luajit/include/luajit-2.1
    $ make && make install
  • 设置环境变量

    1
    2
    3
    export LUAROCKS_HOME=/export/servers/luarocks-3.0.4
    PATH=$PATH:$LUAROCKS_HOME/bin
    export PATH
  • 验证luarocks命令是否可用

    1
    $ luarocks
  • 设置LUA_PATH

    1
    $ luarocks path
  • 把上一步输出的环境变量,写入profile中,示例如下:

1
2
export LUA_PATH='/home/admin/.luarocks/share/lua/5.1/?.lua;/home/admin/.luarocks/share/lua/5.1/?/init.lua;/export/servers/luarocks-3.0.4/share/lua/5.1/?.lua;/export/servers/luarocks-3.0.4/share/lua/5.1/?/init.lua;./?.lua;/usr/local/share/luajit-2.1.0-beta3/?.lua;/usr/local/share/lua/5.1/?.lua;/usr/local/share/lua/5.1/?/init.lua'
export LUA_CPATH='/home/admin/.luarocks/lib/lua/5.1/?.so;/export/servers/luarocks-3.0.4/lib/lua/5.1/?.so;./?.so;/usr/local/lib/lua/5.1/?.so;/usr/local/lib/lua/5.1/loadall.so'

kong依赖的lua包

正常情况下,编译kong的源码时,luarocks会自动下载相关依赖,但在公司服务器上无法通过git下载github上多个lua module源码进行编译打包(连接github一直超时),这里记录了需要手动下载源码在编译安装的lua包。

  • lua-resty-jit-uuid

    1
    2
    3
    4
    $ wget https://codeload.github.com/thibaultcha/lua-resty-jit-uuid/tar.gz/0.0.7
    $ tar -xf 0.0.7
    $ cd lua-resty-jit-uuid-0.0.7
    $ luarocks make
  • kong-lapis

    1
    2
    3
    4
    $ wget https://github.com/Kong/lapis/archive/v1.6.0.1.tar.gz
    ...

    $ luarocks make kong-lapis-1.6.0.1-1.rockspec
  • lua-pack

    1
    2
    3
    $ wget https://github.com/Kong/lua-pack/archive/1.0.5.tar.gz
    ...
    $ luarocks make
  • lua-resty-cookie

    1
    2
    $ wget https://github.com/cloudflare/lua-resty-cookie/archive/v0.1.0.tar.gz
    ...
  • 由于该包解压后没有rockspec文件,无法通过luarocks打包,需要单独下载rockspec文件到解压后的目录下

1
2
   $ wget --no-check-certificate https://raw.githubusercontent.com/cloudflare/lua-resty-cookie/master/rockspecs/lua-resty-cookie-0.1.0-1.rockspec
$ luarocks make
  • lua-resty-mlcache

    1
    2
    3
    $ wget https://github.com/thibaultcha/lua-resty-mlcache/archive/2.2.0.tar.gz
    ...
    $ luarocks make
  • kong-plugin-serverless-functions

    1
    2
    $ wget https://github.com/Kong/kong-plugin-serverless-functions/archive/0.1.0.tar.gz
    ...
  • 由于该包的rockspec文件命名与需要的不符,需要单独下载rockspec文件到解压后的目录下

    1
    2
    3
    $ wget http://webmail.luarocks.org/manifests/kong/kong-plugin-serverless-functions-0.1.0-0.rockspec
    luarocks make kong-plugin-serverless-functions-0.1.0-0.rockspec
    $ luarocks make
  • kong-plugin-prometheus

    1
    2
    wget https://github.com/Kong/kong-plugin-prometheus/archive/0.1.0.tar.gz
    ...
  • 需要修改rockspec配置文件,把dependencies注释掉

    1
    $ luarocks make

安装kong

  • 下载

    1
    $ wget https://github.com/Kong/kong/archive/0.14.1.tar.gz
  • 解压

    1
    2
    3
    $ tar -xf 0.14.1.tar.gz
    $ mv kong-0.14.1 /export/servers
    $ cd /export/servers/kong-0.14.1
  • 编译

    1
    $ make install

配置kong

  • 拷贝默认配置文件
    1
    $ cp /export/servers/kong-0.14.1/kong.conf.default /export/servers/kong-0.14.1/kong.conf

需要修改的配置文件内容如下:

  • kong安装的目录

    1
    prefix = /export/servers/kong-0.14.1/
  • 日志级别

    1
    log_level = notice
  • 日志目录,注意确保启动kong的账户有日志目录的写权限,否则会启动失败,建议直接执行

    1
    2
    3
    4
    5
    mkdir -p /export/Logs/kong/logs/
    proxy_access_log = /export/Logs/kong/logs/access.log
    proxy_error_log = /export/Logs/kong/logs/error.log
    admin_access_log = /export/Logs/kong/logs/admin_access.log
    admin_error_log = /export/Logs/kong/logs/error.log
  • 启动nginx的账户

    1
    nginx_user = admin admin
  • 8000是对外提供访问服务的

    1
    proxy_listen = 0.0.0.0:8000, 0.0.0.0:8443 ssl
  • 8001是admin接口使用,进行配置相关操作的

    1
    admin_listen = 0.0.0.0:8001, 127.0.0.1:8444 ssl
  • 数据库相关配置,数据库主要存放配置的信息

    1
    2
    3
    4
    5
    6
    database = postgres
    pg_host = 127.0.0.1
    pg_port = 5432
    pg_user = kong
    pg_password =
    pg_database = kong

创建kong的数据库账户,注意和kong.conf的数据库配置相匹配(注意: 只有安装第一个节点需要初始化数据库)

1
$ /usr/pgsql-11/bin/psql -d template1

  • 执行的sql
    1
    CREATE USER kong; CREATE DATABASE kong OWNER kong;

修改kong默认启动的nginx路径
kong会默认从几个路径寻找nginx命令来启动openresty,如果openresty安装的目录不再这几个目录下,机会启动失败,这里我们手动修改成我们自己安装的目录。

1
$ vi /export/servers/kong-0.14.1/kong/cmd/utils/nginx_signals.lua

  • 上面文件中,这个部分需要修改

    1
    2
    3
    4
    5
    local nginx_search_paths = {
    "/usr/local/openresty/nginx/sbin",
    "/opt/openresty/nginx/sbin",
    ""
    }
  • 修改后的

    1
    2
    3
    local nginx_search_paths = {
    "/export/servers/openresty/nginx/sbin"
    }

初始化和启动kong

  • 会连接数据库,创建kong需要的表

    1
    $ /export/servers/kong-0.14.1/bin/kong migrations up -c /export/servers/kong-0.14.1/kong.conf
  • 启动kong(root账户下)

    1
    $ /export/servers/kong-0.14.1/bin/kong start --vv -c /export/servers/kong-0.14.1/kong.conf
  • 验证kong的admin接口

    1
    $ curl -i http://localhost:8001/
  • 停止kong

    1
    $ /export/servers/kong-0.14.1/bin/kong stop -p /export/servers/kong-0.14.1/

未命名

发表于 2019-01-28
xxl-job
★★★★★
elastic-job
★★★★
TBSchedule
★★★★
Easy Task
★★★
开源时间 2015年11月22日 2016年2月 2012年 2018年1月
厂商 许雪里,贡献者16人,大众点评内部: Ferrari-法拉利 当当网开源,贡献者17人 阿里开源,github无正式下载,无贡献者 淘宝彩票调度平台,jar包公司时铁甲网
社区力量 ★6759 fork 2893 公司:155+ ★4540 fork 2152 公司:50 无 ★17 fork 18
更新时间 20天前 1年前 无 3个月前
周期执行 支持 支持 支持 支持
定时执行 最终一致 最终一致 支持
Dataflow任务 支持
事件触发 支持
脚本任务 Shell、Python、NodeJS、PHP shell,python,perl
弹性扩容缩容 基于DB的 基于ZK的
故障转移 支持,Failover 支持 支持 支持
阻塞处理 单机串行(默认) zk的session timeout
动态分片 支持 支持
广播任务 支持 支持
并行调度 多线程 将任务分片成小任务 timer
任务依赖 支持 支持
任务进度监控 支持 ??
任务失败重试 支持自定义次数 ??
日志 支持,有界面 有API,需调用 很简单
报警 邮件,API支持短信 多样,可定制
报表 支持 ??
openAPI ?? 支持
Spring 支持,springboot 支持,springboot
调度中心支持集群部署 支持 支持
文档完善 支持 支持 不支持
难易程度 简单 较复杂
高级功能 弹性扩容,分片广播,故障转移,Rolling实时日志,GLUE(支持在线编辑代码,免发布),任务进度监控,任务依赖,数据加密,邮件报警,运行报表,国际化 弹性扩容,多种作业模式,失效转移,运行状态收集,多线程处理数据,幂等性,容错处理,spring命名空间支持
缺点 调度中心通过获取 DB锁来保证集群中执行任务的唯一性, 如果短任务很多,随着调度中心集群数量增加,那么数据库的锁竞争会比较厉害,性能不好。 需要引入zookeeper , mesos, 增加系统复杂度, 学习成本较高
场景推荐 用户基数相对少,服务器数量在一定范围内 数据量庞大,且部署服务器数量较多
使用企业 大众点评,运满满,优信二手车,拍拍贷 36氪,当当网,国美,金柚网,联想,唯品会,亚信,平安,猪八戒
依赖 mysql ,jdk1.7+ , maven3.0+ jdk1.7+, zookeeper 3.4.6+ ,maven3.0.4+ ,mesos

指标解释:

弹性扩容缩容:一旦有新执行器机器上线或者下线,下次调度时将会重新分配任务;

故障转移:任务路由策略选择”故障转移”情况下,如果执行器集群中某一台机器故障,将会自动Failover切换到一台正常的执行器发送调度请求。

阻塞处理策略:调度过于密集执行器来不及处理时的处理策略

任务失败重试:支持自定义任务失败重试次数,当任务失败时将会按照预设的失败重试次数主动进行重试;其中分片任务支持分片粒度的失败重试;

动态分片:分片广播任务以执行器为维度进行分片,支持动态扩容执行器集群从而动态增加分片数量,协同进行业务处理;在进行大数据量业务操作时可显著提升任务处理能力和速度。

事件触发:支持基于事件的触发任务方式

广播执行:

img

并行计算:

job就是将一个耗时很长的大任务拆分成多个小的子任务然后分发到多台机器去并行执行。

img

任务依赖:

这种特性往往用于有业务数据依赖的多个任务之间按照严格先后顺序执行的场景。阿里内部有很多这种场景,两个或者多个任务之间按照某种业务逻辑顺序去执行,比如两个任务A,B其中A执行结束之后B才能开始执行

共同点

共同点: E-Job和X-job都有广泛的用户基础和完整的技术文档,都能满足定时任务的基本功能需求。

不同点

X-Job 侧重的业务实现的简单和管理的方便,学习成本简单,失败策略和路由策略丰富。推荐使用在“用户基数相对少,服务器数量在一定范围内”的情景下使用

E-Job 关注的是数据,增加了弹性扩容和数据分片的思路,以便于更大限度的利用分布式服务器的资源。但是学习成本相对高些,推荐在“数据量庞大,且部署服务器数量较多”时使用

Elastic-Job-Lite定位为轻量级无中心化解决方案,使用jar包的形式提供分布式任务的协调服务。

Elastic-Job-Cloud使用Mesos + Docker(TBD)的解决方案,额外提供资源治理、应用分发以及进程隔离等服务

亮点:

基于quartz 定时任务框架为基础的,因此具备quartz的大部分功能

使用zookeeper做协调,调度中心,更加轻量级

支持任务的分片

支持弹性扩容 , 可以水平扩展 , 当任务再次运行时,会检查当前的服务器数量,重新分片,分片结束之后才会继续执行任务

失效转移,容错处理,当一台调度服务器宕机或者跟zookeeper断开连接之后,会立即停止作业,然后再去寻找其他空闲的调度服务器,来运行剩余的任务

架构图:

XXL

输入图片说明

E-JOB

E-JOB-Lite
E-JOB-CLOUD

img

THE END

Hexo常用操作

发表于 2019-01-27 | 分类于 Hexo

Welcome to Hexo! This is your very first post. Check documentation for more info. If you get any problems when using Hexo, you can find the answer in troubleshooting or you can ask me on GitHub.

Quick Start

Create a new post

1
$ hexo new "My New Post"

More info: Writing

Run server

1
$ hexo server

More info: Server

Generate static files

1
$ hexo generate

More info: Generating

Deploy to remote sites

1
$ hexo deploy

More info: Deployment

创建新博文

1
$ hexo new blog "jacqueline"

分类同介绍网址

https://hexo.io/zh-cn/docs/front-matter.html#分类和标签

如何写作网址

https://hexo.io/zh-cn/docs/writing
http://www.lixu.fun/2018/11/15/hexo-gihub/

部署后:https://levin80.github.io/index.html

参考网址链接:

  1. http://baixin.io:8000/2015/08/HEXO搭建个人博客/
  2. https://www.simon96.online/2018/10/12/hexo-tutorial/

Callable、Future和FutureTask

发表于 2019-01-27

Callable、Future和FutureTask

[TOC]

【引言】

线程的2种方式,一种是直接继承Thread,另外一种就是实现Runnable接口,这2种方式都有一个缺陷就是:在执行完任务之后无法获取执行结果
如果需要获取执行结果, 就必须通过共享变量或者使用线程通信的方式来达到效果,这样使用起来就比较麻烦。

从Java 1.5开始,就提供了Callable和Future,通过它们可以在任务执行完毕之后得到任务执行结果。

一.Callable与Runnable

先说一下java.lang.Runnable吧,它是一个接口,在它里面只声明了一个run()方法:

public interface Runnable {
  public abstract void run();
}

由于run()方法返回值为void类型,所以在执行完任务之后无法返回任何结果。

Callable位于java.util.concurrent包下,它也是一个接口,在它里面也只声明了一个方法,只不过这个方法叫做call():

public interface Callable<V> {
/**
 * Computes a result, or throws an exception if unable to do so.
 *
 * @return computed result
 * @throws Exception if unable to compute a result
 */
V call() throws Exception;
}

可以看到,这是一个泛型接口,call()函数返回的类型就是传递进来的V类型。

那么怎么使用Callable呢?一般情况下是配合ExecutorService来使用的,在ExecutorService接口中声明了若干个submit方法的重载版本:

<T> Future<T> submit(Callable<T> task);
<T> Future<T> submit(Runnable task, T result);
Future<?> submit(Runnable task);

第一个submit方法里面的参数类型就是Callable。

暂时只需要知道Callable一般是和ExecutorService配合来使用的,具体的使用方法讲在后面讲述。

一般情况下我们使用第一个submit方法和第三个submit方法,第二个submit方法很少使用。

区别是
1、run方法是没有返回值的,而且不能抛出checked异常,只能在run方法内部try catch住。
2、调用方式也有所区别,Runnable对象可以传入Thread构造器中,直接启动。Callable对象则需要与线程池ExecutorService一起使用。

二.Future

Future就是对于具体的Runnable或者Callable任务的执行结果进行取消、查询是否完成、获取结果。
必要时可以通过get方法获取执行结果,该方法会阻塞直到任务返回结果。

Future类位于java.util.concurrent包下,它是一个接口:

public interface Future<V> {
boolean cancel(boolean mayInterruptIfRunning);
boolean isCancelled();
boolean isDone();
V get() throws InterruptedException, ExecutionException;
V get(long timeout, TimeUnit unit)
    throws InterruptedException, ExecutionException, TimeoutException;
}

在Future接口中声明了5个方法,下面依次解释每个方法的作用:

  • cancel方法用来取消任务,如果取消任务成功则返回true,如果取消任务失败则返回false。参数mayInterruptIfRunning表示是否允许取消正在执行却没有执行完毕的任务,如果设置true,则表示可以取消正在执行过程中的任务。如果任务已经完成,则无论mayInterruptIfRunning为true还是false,此方法肯定返回false,即如果取消已经完成的任务会返回false;如果任务正在执行,若mayInterruptIfRunning设置为true,则返回true,若mayInterruptIfRunning设置为false,则返回false;如果任务还没有执行,则无论mayInterruptIfRunning为true还是false,肯定返回true。

  • isCancelled方法表示任务是否被取消成功,如果在任务正常完成前被取消成功,则返回 true。

  • isDone方法表示任务是否已经完成,若任务完成,则返回true;

  • get()方法用来获取执行结果,这个方法会产生阻塞,会一直等到任务执行完毕才返回;

  • get(long timeout, TimeUnit unit)用来获取执行结果,如果在指定时间内,还没获取到结果,就直接返回null。
      也就是说Future提供了三种功能:

  - 判断任务是否完成;

  - 能够中断任务;

  - 能够获取任务执行结果。

  因为Future只是一个接口,所以是无法直接用来创建对象使用的,因此就有了下面的FutureTask。

三.FutureTask
我们先来看一下FutureTask的实现

public class FutureTask<V> implements RunnableFuture<V>

FutureTask类实现了RunnableFuture接口,我们看一下RunnableFuture接口的实现:

public interface RunnableFuture<V> extends Runnable, Future<V> {
    void run();
}

可以看出RunnableFuture继承了Runnable接口和Future接口,而FutureTask实现了RunnableFuture接口。
所以它既可以作为Runnable被线程执行,又可以作为Future得到Callable的返回值。

  FutureTask提供了2个构造器:

public FutureTask(Callable<V> callable) {
 }

public FutureTask(Runnable runnable, V result) {
 }

事实上,FutureTask是Future接口的一个唯一实现类。JDK 1.7之后 ForkJoinTask 也实现了Future接口

四.使用示例

1.使用Callable+Future获取执行结果

public class Test {
public static void main(String[] args) {
    ExecutorService executor = Executors.newCachedThreadPool();
    Task task = new Task();
    Future<Integer> result = executor.submit(task);
    executor.shutdown();

    try {
        Thread.sleep(1000);
    } catch (InterruptedException e1) {
        e1.printStackTrace();
    }

    System.out.println("主线程在执行任务");

    try {
        System.out.println("task运行结果"+result.get());
    } catch (InterruptedException e) {
        e.printStackTrace();
    } catch (ExecutionException e) {
        e.printStackTrace();
    }   

    System.out.println("所有任务执行完毕");
}
}
class Task implements Callable<Integer>{
@Override
public Integer call() throws Exception {
    System.out.println("子线程在进行计算");
    Thread.sleep(3000);
    int sum = 0;
    for(int i=0;i<100;i++)
        sum += i;
    return sum;
}
}

执行结果:


子线程在进行计算
主线程在执行任务
task运行结果4950
所有任务执行完毕

2.使用Callable+FutureTask获取执行结果

public class Test {
    public static void main(String[] args) {
    //第一种方式
    ExecutorService executor = Executors.newCachedThreadPool();
    Task task = new Task();
    FutureTask<Integer> futureTask = new FutureTask<Integer>(task);
    //executor.submit(futureTask);
   // executor.shutdown();

   //需要处理各类线程的异常情况
   try {
              futureTask = executor.submit(task);
              result = futureTask.get(time,timeUnit);
          } catch (Exception e) {
              if(e instanceof InterruptedException){
                    //中端异常处理
              }else if(e instanceof RejectedExecutionException){
                  //提交线程池被拒绝异常处理
              }else if(e instanceof ExecutionException){
                  //线程执行异常处理
              }else if(e instanceof TimeoutException){
                  //等待结果超时异常处理
              }else{

              }
     }

    //第二种方式,注意这种方式和第一种方式效果是类似的,只不过一个使用的是ExecutorService,一个使用的是Thread
    /*Task task = new Task();
    FutureTask<Integer> futureTask = new FutureTask<Integer>(task);
    Thread thread = new Thread(futureTask);
    thread.start();*/

    try {
        Thread.sleep(1000);
    } catch (InterruptedException e1) {
        e1.printStackTrace();
    }

    System.out.println("主线程在执行任务");

    try {
        System.out.println("task运行结果"+futureTask.get());
    } catch (InterruptedException e) {
        e.printStackTrace();
    } catch (ExecutionException e) {
        e.printStackTrace();
    }

    System.out.println("所有任务执行完毕");
    }
}
class Task implements Callable<Integer>{
    @Override
    public Integer call() throws Exception {
        System.out.println("子线程在进行计算");
        Thread.sleep(3000);
        int sum = 0;
        for(int i=0;i<100;i++)
            sum += i;
        return sum;
        }
}

  如果为了可取消性而使用 Future 但又不提供可用的结果,则可以声明 Future<?> 形式类型、并返回 null 作为底层任务的结果。

Future 同FutureTask 区别是:FutureTask可以用一个线程来启动,因为FutureTask间接实现了Runnable接口

附带点线程池类型– Executor框架

Executors
从上一节内容看出,ThreadPoolExecutor的新建需要传入很多参数,使用起来极不方便。为了便于使用,Executors为我们提供了几个静态工厂方法,大大简化了线程池的创建,它们分别是:

newFixedThreadPool:newFixedThreadPool将创建一个固定大小的线程池,每当提交一个任务就创建一个线程,直到达到线程池的最大数量,这时线程池的规模将不再变化;

newCachedThreadPool:newCachedThreadPool将创建一个可缓存的线程池,如果线程池的当前规模超过了处理需求,那么将回收空闲线程;而当需求增加时,可以添加新的线程,线程池的规模不存在任何限制。

newSingleThreadExecutor:newSingleThreadExecutor是一个单线程的Executor,它创建单个工作者线程执行任务,如果这个线程异常结束,会创建另一个线程代替。

newScheduledThreadPool:创建一个可延迟执行或定期执行的线程池

作者:thinkChao
链接:https://www.jianshu.com/p/75fbabac2e5c
來源:简书
简书著作权归作者所有,任何形式的转载都请联系作者获得授权并注明出处。

12
Levin

Levin

岁月如冰河,热望如鲸歌

12 日志
3 分类
3 标签
© 2019 Levin
由 Hexo 强力驱动
|
主题 — NexT.Gemini v5.1.4