Bootstrap

2024年最新Elastic Job 同城主备、同城双活,高可用必备~,四面楚歌的Java工程师该何去何从

最后

做任何事情都要用心,要非常关注细节。看起来不起眼的、繁琐的工作做透了会有意想不到的价值。
当然要想成为一个技术大牛也需要一定的思想格局,思想决定未来你要往哪个方向去走, 建议多看一些人生规划方面的书籍,多学习名人的思想格局,未来你的路会走的更远。

更多的技术点思维导图我已经做了一个整理,涵盖了当下互联网最流行99%的技术点,在这里我将这份导图分享出来,以及为金九银十准备的一整套面试体系,上到集合,下到分布式微服务

本文已被CODING开源项目:【一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码】收录

需要这份系统化的资料的朋友,可以点击这里获取

如图所示,如果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> sharding(List 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);

@Override

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

List jobInstancesCandidates = new ArrayList<>(jobInstances);

List 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或者全部都是slave

log.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{

@Override

protected 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干货及时送达

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

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

以下以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> sharding(List jobInstances, String jobName, int shardingTotalCount)

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

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

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

最后

分享一些资料给大家,我觉得这些都是很有用的东西,大家也可以跟着来学习,查漏补缺。

《Java高级面试》

《Java高级架构知识》

《算法知识》

本文已被CODING开源项目:【一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码】收录

需要这份系统化的资料的朋友,可以点击这里获取

Java高级架构知识》**

[外链图片转存中…(img-osACiko2-1715136420248)]

《算法知识》

[外链图片转存中…(img-MP6e3eAW-1715136420249)]

本文已被CODING开源项目:【一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码】收录

需要这份系统化的资料的朋友,可以点击这里获取

;