以下文章来源方志朋的博客,回复”666“获面试宝典

来源:https://jaskey.github.io/blog/2020/05/25/elastic-job-timmer-active-standby/

在使用Elastic Job Lite做定时任务的时候,我发现很多开发的团队都是直接部署单点,这对于一些离线的非核心业务(如对账、监控等)或许无关紧要,但对于一些高可用补偿、核心数据定时修改(如金融场景的利息更新等),单点部署则“非常危险”。实际上,Elastic Job Lite是支持高可用的。网上关于Elastic Job的较高级的博文甚少,本文试图结合自身实践的一些经验,大致讲解其方案原理,并延伸至同城双机房的架构实践。

注:本文所有讨论均基于开源版本的Elastic Job Lite, 不涉及Elastic Job Cloud部分。

单点部署到高可用

如本文开头所说,很多系统的部署是采取以下部署架构:

原因是开发者担心定时任务在同一时刻被触发多次,导致业务有问题。实际上这是对于框架最基本的原理不了解。在官方文档的功能列表里http://elasticjob.io/docs/elastic-job-lite/00-overview/ 就已说明其最基本的功能之一就是:

作业分片一致性,保证同一分片在分布式环境中仅一个执行实例

Elastic Job会依赖zookeeper选举出对应的实例做sharding,从而保证只有一个实例在执行同一个分片(如果任务没有采取分片(即分片数是0),就意味着这个任务只有一个实例在执行)

所以如下图所示的部署架构是完全没问题的——一来,服务只会被一个实例调用,二来,如果某个服务挂了,其他实例也能接管继续提供服务从而实现高可用。

双机房高可用

随着互联网业务的发展,慢慢地,对架构的高可用会有更高的要求。下一步可能就是需要同城两机房部署,那这时候为了保证定时服务在两个机房的高可用,我们架构上可能会变成这样的:

这样如果A机房的定时任务全部不可用了,B机房的确也能接手提供服务。而且由于集群是一个,Elastic Job能保证同一个分片在两个机房也只有一个实例运行。看似挺完美的。

注:本文不讨论zookeeper如何实现双机房的高可用,实际上从zookeeper的原理来看,仅仅两个机房组成一个大集群并不可以实现双机房高可用。

优先级调度?

以上的架构解决了定时任务在两个机房都可用的问题,但是实际的生产中,定时任务很可能是依赖存储的数据源的。而这个数据源,通常是有主备之分(这里不考虑单元化的架构的情况):例如主在A机房,备在B机房做实时同步。

如果这个定时任务只有读操作,可能没问题,因为只要配置数据源连接同机房的数据源即可。但是如果是要写入的,就有一个问题——如果所有任务都在B机房被调度了,那么这些数据的写入都会跨机房地往A机房写入,这样延迟就大大提升了,如下图所示。

如图所示,如果Elastic Job把任务都调度到了B机房,那么流量就一直跨机房写了,这样对于性能来说是不好的事情。

那么有没有办法达到如下效果了:

  1. 保证两个机房都随时可用,也就是一个机房的服务如果全部不可用了,另外一个机房能提供对等的服务

  2. 但一个任务可以优先指定A机房执行

Elastic Job分片策略

在回答这个问题之前,我们需要了解下Elastic Job的分片策略,根据官网的说明(http://elasticjob.io/docs/elastic-job-lite/02-guide/job-sharding-strategy/ ) ,Elastic Job是内置了一些分片策略可选的,其中有平均分配算法,作业名的哈希值奇偶数决定IP升降序算法和作业名的哈希值对服务器列表进行轮转;同时也是支持自定义的策略,实现实现JobShardingStrategy接口并实现sharding方法即可。

public Map<JobInstance, List<Integer>> sharding(List<JobInstance> jobInstances, String jobName, int shardingTotalCount)

假设我们可以实现这一的自定义策略:让做分片的时候知道哪些实例是A机房的,哪些是B机房的,然后我们知道A机房是优先的,在做分片策略的时候先把B机房的实例踢走,再复用原来的策略做分配。这不就解决我们的就近接入问题(接近数据源)了吗?

以下是利用装饰器模式自定义的一个装饰器类(抽象类,由子类判断哪些实例属于standby的实例),读者可以结合自身业务场景配合使用。

public abstract class JobShardingStrategyActiveStandbyDecorator implements JobShardingStrategy {//内置的分配策略采用原来的默认策略:平均private JobShardingStrategy inner = new AverageAllocationJobShardingStrategy();/*** 判断一个实例是否是备用的实例,在每次触发sharding方法之前会遍历所有实例调用此方法。* 如果主备实例同时存在于列表中,那么备实例将会被剔除后才进行sharding* @param jobInstance* @return*/protected abstract boolean isStandby(JobInstance jobInstance, String jobName);@Overridepublic Map<JobInstance, List<Integer>> sharding(List<JobInstance> jobInstances, String jobName, int shardingTotalCount) {List<JobInstance> jobInstancesCandidates = new ArrayList<>(jobInstances);List<JobInstance> removeInstance = new ArrayList<>();boolean removeSelf = false;for (JobInstance jobInstance : jobInstances) {boolean isStandbyInstance = false;try {isStandbyInstance = isStandby(jobInstance, jobName);} catch (Exception e) {log.warn("isStandBy throws error, consider as not standby",e);}if (isStandbyInstance) {if (IpUtils.getIp().equals(jobInstance.getIp())) {removeSelf = true;}jobInstancesCandidates.remove(jobInstance);removeInstance.add(jobInstance);}}if (jobInstancesCandidates.isEmpty()) {//移除后发现没有实例了,就不移除了,用原来的列表(后备)的顶上jobInstancesCandidates = jobInstances;log.info("[{}] ATTENTION!! Only backup job instances exist, but do sharding with them anyway {}", jobName, JSON.toJSONString(jobInstancesCandidates));}if (!jobInstancesCandidates.equals(jobInstances)) {log.info("[{}] remove backup before really do sharding, removeSelf :{} , remove instances: {}", jobName, removeSelf, JSON.toJSONString(removeInstance));log.info("[{}] after remove backups :{}", jobName, JSON.toJSONString(jobInstancesCandidates));} else {//全部都是master或者全部都是slavelog.info("[{}] job instances just remain the same {}", jobName, JSON.toJSONString(jobInstancesCandidates));}//保险一点,排序一下,保证每个实例拿到的列表肯定是一样的jobInstancesCandidates.sort((o1, o2) -> o1.getJobInstanceId().compareTo(o2.getJobInstanceId()));return inner.sharding(jobInstancesCandidates, jobName, shardingTotalCount);}

利用自定义策略实现同城双机房下的优先级调度

以下是一个很简单的就近接入的例子:指定在ip白名单的,就是优先执行的,不在的都认为是备用的。我们看如何实现。

一、继承此装饰器策略,指定哪些实例是standby实例

public class ActiveStandbyESJobStrategy extends JobShardingStrategyActiveStandbyDecorator{@Overrideprotected boolean isStandby(JobInstance jobInstance, String jobName) {String activeIps = "10.10.10.1,10.10.10.2";//只有这两个ip的实例才是优先执行的,其他都是备用的String ss[] = activeIps.split(",");return !Arrays.asList(ss).contains(jobInstance.getIp());//不在active名单的就是后备}}

很简单吧!这样实现之后,就能达到以下类似的效果

二、 在任务启动前,指定使用这个策略

以下以Java的方式示意,

JobCoreConfiguration simpleCoreConfig = JobCoreConfiguration.newBuilder(jobClass.getName(), cron, shardingTotalCount).shardingItemParameters(shardingItemParameters).build();
SimpleJobConfiguration simpleJobConfiguration = new SimpleJobConfiguration(simpleCoreConfig, jobClass.getCanonicalName());
return LiteJobConfiguration.newBuilder(simpleJobConfiguration).jobShardingStrategyClass("com.xxx.yyy.job.ActiveStandbyESJobStrategy")//使用主备的分配策略,分主备实例(输入你的实现类类名).build();

这样就大功告成了。

同城双活模式

以上这样改造后,针对定时任务就已经解决了两个问题:

1、定时任务能实现在两个机房下的高可用

2、任务能优先调度到指定机房

这种模式下,对于定时任务来说,B机房其实只是个备机房——因为A机房永远都是优先调度的。

对于B机房是否有一些实际问题其实我们可能是不知道的(常见的例如数据库权限没申请),由于没有流量的验证,这时候真的出现容灾问题,B机房是否能安全接受其实并不是100%稳妥的。

我们能否再进一步做到同城双活呢?也就是,B机房也会承担一部分的流量?例如10%?

回到自定义策略的sharding接口:

public Map<JobInstance, List<Integer>> sharding(List<JobInstance> jobInstances, String jobName, int shardingTotalCount)

在做分配的时候,是能拿到一个任务实例的全景图(所有实例列表),当前的任务名,和分片数。

基于此其实是可以做一些事情把流量引流到B机房实例的,例如:

  1. 指定任务的主机房让其是B机房优先调度(例如挑选部分只读任务,占10%的任务数)

  2. 对于分片的分配,把末尾(如1/10)的分片优先分配给B机房。

以上两种方案都能实现让A、B两个机房都有流量(有任务在被调度),从而实现所谓的双活。

以下针对上面抛出来的方案一,给出一个双活的示意代码和架构。

假设我们定时任务有两个任务,TASK_A_FIRST,TASK_B_FIRST,其中TASK_B_FIRST是一个只读的任务,那么我们可以让他配置读B机房的备库让他优先运行在B机房,而TASK_A_FIRST是一个更为频繁的任务,而且带有写操作,我们则优先运行在A机房,从而实现双机房均有流量。

注:这里任意一个机房不可用了,任务均能在另外一个机房调度,这里增强的只是对于不同任务做针对性的优先调度实现双活

public class ActiveStandbyESJobStrategy extends JobShardingStrategyActiveStandbyDecorator{@Overrideprotected boolean isStandby(JobInstance jobInstance, String jobName) {String activeIps = "10.10.10.1,10.10.10.2";//默认只有这两个ip的实例才是优先执行的,其他都是备用的if ("TASK_B_FIRST".equals(jobName)){//选择这个任务优先调度到B机房activeIps = "10.11.10.1,10.11.10.2";}String ss[] = activeIps.split(",");return !Arrays.asList(ss).contains(jobInstance.getIp());//不在active名单的就是后备}}

在这里,我为大家准备了一份2021年最新最全的《史上最简单的java面试题》,这套电子书涵盖了诸多java技术栈的面试知识题,是作者面试BAT大厂的前的总结,作者顺利拿下AT的offer,相信可以帮助大家在最短的时间内复习Java后端的大多数面试题,从而拿到自己心仪的offer。截了张图,大家可以仔细查看左边的菜单栏,覆盖的知识面真的很广,而且质量都很不错。资料获取方法扫描下方二维码
后台回复关键词:BAT
明天见(。・ω・。)

Elastic Job从单点到高可用、同城主备、同城双活相关推荐

  1. Elastic Job 从单点到高可用、同城主备、同城双活

    点击上方蓝色"石杉的架构笔记",选择"设为星标"回复"PDF"获取独家整理的学习资料! 长按扫描上方一元购买 前言 在使用Elastic J ...

  2. 浅入浅出keepalived+nginx实现高可用双机热备

    对应用keepalived+nginx技术实现nginx高可用进行简单的分析,下面是通过对keepalived添加校验nginx存活脚本,监控nginx的状态,应用keepalived的主备模式实现n ...

  3. 【软件工程】容错、高可用、灾备の辨析

    容错.高可用.灾备 标题里面的三个术语,很容易混淆,专业人员有时也会用错. 本文就用图片解释它们有何区别. 容错 容错(fault tolerance)指的是, 发生故障时,系统还能继续运行. 飞机有 ...

  4. MySQL高可用和灾备调研

    1.高可用和灾备方案概览 高可用方案的评价以组件能正常对外提供服务为主,而灾备方案的评价以数据稳定同步和恢复时间尽量短为主,其他的还要求方案实现起来较简单,后期运维服务压力较小等. 当下业界比较流行的 ...

  5. ACS 5.4高可用——双机热备

    ACS 5.4高可用--双机热备 一.安装 安装ACS_v5.4.0.46.0a.iso 1.过程等同于安装Centos,安装完成后输入setup按提示进行acs相关基础配置. 2.配置完成服务会重启 ...

  6. 高可用演进方案(同城灾备、两地三中心、异地双活)

    目录 01 系统可用性 02 单机架构 03 主从副本 04 风险不可控 05 同城灾备 06 同城双活 07 两地三中心 08 伪异地双活 09 真正的异地双活 10 如何实施异地双活 11 异地多 ...

  7. HA-heartbeat高可用双击热备安装配置详解

    前期准备工作 *两台节点必须是双网卡,一个口外网,一个口内网心跳 改网卡: 三块网卡,设置一个外网网卡的网关为外网地址的网关 心跳网卡,设置外网网卡的IP为网关 内网网卡,设置外网网卡的IP为网关   ...

  8. 使用sqlserver搭建高可用双机热备的Quartz集群部署

    一般拿 Timer 和 Quartz 相比较的,简直就是对 Quartz 的侮辱,两者的功能根本就不在一个层级上,如本篇介绍的Quartz强大的集群机制,可以采用基于sqlserver,mysql的集 ...

  9. 使用sqlserver搭建高可用双机热备的Quartz集群部署【附源码】

    一般拿Timer和Quartz相比较的,简直就是对Quartz的侮辱,两者的功能根本就不在一个层级上,如本篇介绍的Quartz强大的集群机制,可以采用基于 sqlserver,mysql的集群方案,当 ...

最新文章

  1. linux mysql 事务_linux下mysql Insert update delete 事务 用户管理
  2. HTML标签元素的分类
  3. Java Socket 教程
  4. feedback from waic
  5. Android之Error: ‘L‘ is not a valid file-based resource name character解决办法
  6. 系统垃圾清理.cmd
  7. Linux部署oracle11g,linux环境下部署Oracle11g
  8. 资深架构专家讲解微服务治理的架构演进
  9. 直播课:5G来了,互联网百亿级创新的机遇在哪里?
  10. 统信UOS安装flash浏览器插件
  11. 使用.pk8 和.pem签名生成.keystore 签名
  12. WinCC7.5 笔记3-用表格和趋势图显示温度变化和风扇运转(变量记录)
  13. 光电开关与光耦的区别 - 光电开关电子电路图
  14. 异常-银行账户异常处理
  15. Django连接MySQL8.0报错django.db.utils.OperationalError: (1045, “Access denied for user ‘16066‘@‘localhos
  16. 计算机课件制作总结,课件制作比赛活动总结范文
  17. 怎样阅读论文(台湾彭明辉)ZT
  18. 【Linux】解决可恶的 “NIC Link is Down”
  19. 知家发布国内首个DTC白皮书,小仙炖创始人、五菱高管为其助威
  20. s7 200 c语言编程,s7 200 子程序 局部变量表

热门文章

  1. 自动驾驶技术现状与需求分析
  2. matlab中cumsum函数的使用
  3. 余额宝技术架构读后感
  4. 搭建私有npm私库(使用verdaccio)
  5. 【C#技术】一篇文章搞掂:Infragistics组件库
  6. WPF XAML 资源样式模板属性存放位置
  7. ORA-01747: user.table.column, table.column 或列说明无效 异常解决方法总结
  8. 【转帖】SQLServer登录连接失败(error:40-无法打开到SQLServer的连接)的解决方案...
  9. NPOI读取Excel数据应用
  10. java myeclipse jar 导出问题