紫金大数据平台架构之路(一)----大数据任务开发和调度平台架构设计

news2024/11/17 5:28:05

一、总体设计

初来公司时,公司还没有大数据,我是作为大数据架构师招入的,结合公司的线上和线下业务,制定了如下的大数据架构路线图。

二、大数据任务开发和调度平台架构设计

在设计完总体架构后,并且搭建完hadoop/yarn的大数据底层计算平台后, 按照总体架构设计思路, 首先需要构建的就是大数据开发平台。这也是一个非常核心的平台,也是最基础最重要的一个环节。

一开始设计的架构图如下所示。

架构设计解释说明如下:

MasterServer:

MasterServer采用分布式无中心设计理念,MasterServer主要负责 DAG 任务切分、任务提交监控,并同时监听其它MasterServer和WorkerServer的健康状态。 MasterServer服务启动时向Zookeeper注册临时节点,通过监听Zookeeper临时节点变化来进行容错处理。

该服务内主要包含:
Distributed 分布式调度组件,主要负责定时任务的启停操作,当Distributed调起任务后,Master内部会有线程池具体负责处理任务的后续操作

MasterScheduler是一个扫描线程,定时扫描数据库中的 command 表,根据不同的命令类型进行不同的业务操作

MasterExecThread主要是负责DAG任务切分、任务提交监控、各种不同命令类型的逻辑处理

MasterTaskExecThread主要负责任务的持久化

WorkerServer:

WorkerServer同样也采用分布式无中心设计理念,WorkerServer主要负责任务的执行和提供日志服务。WorkerServer服务启动时向Zookeeper注册临时节点,并维持心跳。

该服务包含:
FetchTaskThread主要负责不断从Task Queue中领取任务,并根据不同任务类型调用TaskScheduleThread对应执行器。

LoggerServer是一个GRPC服务,提供日志分片查看、刷新和下载等功能

ZooKeeper:

ZooKeeper服务,系统中的MasterServer和WorkerServer节点都通过ZooKeeper来进行集群管理和容错。另外系统还基于ZooKeeper进行事件监听和分布式锁。 也曾经想过基于Redis实现过队列,不过还是想依赖到的组件尽量地少,减少研发的学习成本,所以最后还是去掉了Redis实现。

Task Queue:

提供任务队列的操作,队列也是基于Zookeeper来实现。由于队列中存的信息较少,不必担心队列里数据过多的情况,对系统稳定性和性能没影响。

告警服务:

提供告警相关接口,接口主要包括告警两种类型的告警数据的存储、查询和通知功能。其中通知功能又有邮件通知和SNMP(暂未实现)两种。

API(web App 应用动态请求处理)

API接口层,主要负责处理前端UI层的请求。该服务统一提供RESTful api向外部提供请求服务。 接口包括工作流的创建、定义、查询、修改、发布、下线、手工启动、停止、暂停、恢复、从该节点开始执行等等。

UI(web app前端)

系统的前端页面,提供系统的各种可视化操作界面,详见系统使用手册部分。

web application采用前后端分离的方式, UI(web app前端) 中的静态资源采用nginx进行管理。
nginx.conf中的配置(前后端分离配置):

server {
listen 8888;# 监听端口
server_name bigdata-manager;
#charset koi8-r;
access_log /var/log/nginx/access.log main;
location / {
root /opt/app/dist; 静态资源文件的路径
index index.html index.html;
}
location /webPortal{
proxy_pass http://127.0.0.1:12345;# 动态请求处理,请求后端的API
}
}

DAG: 全称Directed Acyclic Graph,简称DAG。工作流中的Task任务以有向无环图的形式组装起来,从入度为零的节点进行拓扑遍历,直到无后继节点为止。

本文作者:张永清 转载请注明来源博客园:https://www.cnblogs.com/laoqing/p/12692566.html

三、架构设计思想

1、中心化还是去中心化设计的选择

中心化思想:中心化的设计理念比较简单,分布式集群中的节点按照角色分工,大体上分为两种角色:

  • Master的角色主要负责任务分发并监督Slave的健康状态,可以动态的将任务均衡到Slave上,以致Slave节点不至于“忙死”或”闲死”的状态。
  • Worker的角色主要负责任务的执行工作并维护和Master的心跳,以便Master可以分配任务给Slave。

中心化思想设计存在的不足:

  • 一旦Master出现了问题,则集群就会瘫痪,甚至整个集群就会崩溃。为了解决这个问题,大多数Master/Slave架构模式都采用了主备Master的设计方案,可以是热备或者冷备,也可以是自动切换或手动切换,而且越来越多的新系统都开始具备自动选举切换Master的能力,以提升系统的可用性。
  • 另外一个问题是如果Scheduler在Master上,虽然可以支持一个DAG中不同的任务运行在不同的机器上,但是会产生Master的过负载。如果Scheduler在Slave上,则一个DAG中所有的任务都只能在某一台机器上进行作业提交,则并行任务比较多的时候,Slave的压力可能会比较大。

去中心化思想:

  • 在去中心化设计里,通常没有Master/Slave的概念,所有的角色都是一样的,地位是平等的,任意节点设备down机,都只会影响很小范围的功能。
  • 去中心化设计的核心设计在于整个分布式系统中不存在一个区别于其他节点的”管理者”,因此不存在单点故障问题。但由于不存在” 管理者”节点所以每个节点都需要跟其他节点通信才得到必须要的机器信息,而分布式系统通信的不可靠行,则大大增加了上述功能的实现难度。
  • 真正去中心化的分布式系统并不多见。反而动态中心化分布式系统正在不断涌出。在这种架构下,集群中的管理者是被动态选择出来的,而不是预置的,并且集群在发生故障的时候,集群的节点会自发的举行"会议"来选举新的"管理者"去主持工作。最典型的案例就是ZooKeeper及Go语言实现的Etcd。
  • 我们设计的去中心化是Master/Worker注册到Zookeeper中,实现Master集群和Worker集群无中心,并使用Zookeeper分布式锁来选举其中的一台Master或Worker为“管理者”来执行任务。

2、分布式锁的设计

使用ZooKeeper实现分布式锁来实现同一时刻集群中只有一台Master执行Scheduler,或者只有一台Worker执行任务的提交处理。

获取分布式锁的核心流程算法如下:

 本文作者:张永清 转载请注明来源博客园:https://www.cnblogs.com/laoqing/p/12692566.html

分布式锁的代码实现:

一般不建议自己去实现,逻辑比较复杂,可以直接使用org.apache.curator 框架,引入如下依赖

        <dependency>
            <groupId>org.apache.curator</groupId>
            <artifactId>curator-framework</artifactId>
            <version>${curator.version}</version>
        </dependency>
        <dependency>
            <groupId>org.apache.curator</groupId>
            <artifactId>curator-recipes</artifactId>
            <version>${curator.version}</version>
        </dependency>

参考代码如下:

public class AbstractZKClient
{
    private CuratorFramework zkClient = null;

    public AbstractZKClient(String zookeeperConnectionString, Integer zookeeperRetrySleep, Integer zookeeperRetryMaxtime, Integer zookeeperSessionTimeout, Integer zookeeperConnectionTimeout) {
        RetryPolicy retryPolicy = new ExponentialBackoffRetry(zookeeperRetrySleep, zookeeperRetryMaxtime);
        zkClient = CuratorFrameworkFactory.builder()
                .connectString(zookeeperConnectionString)
                .retryPolicy(retryPolicy)
                .sessionTimeoutMs(1000 * zookeeperSessionTimeout)
                .connectionTimeoutMs(1000 * zookeeperConnectionTimeout)
                .build();
        zkClient.start();
        initStateLister();
    }
    private void initStateLister() {
        if (zkClient == null) {
            return;
        }
        ConnectionStateListener csLister = (client, newState) -> {
            log.info("state changed , current state : " + newState.name());
            /**
             * probably session expired
             */
            if (newState == ConnectionState.LOST) {
                // if lost , then exit
                log.info("current zookeepr connection state : connection lost ");
            }
        };

        zkClient.getConnectionStateListenable().addListener(csLister);
    }
    public void start() {
        if (null != zkClient) {
            if(!zkClient.isStarted()){
                zkClient.start();
            }
            log.info("zookeeper start ...");
        } else {
            log.info("zkClient need to init,please check...");
        }
    }


    public <T, R> R distributedLockExec(String lockPath, Long time, TimeUnit unit) {
        InterProcessMutex lock = null;
        try {
            lock = new InterProcessMutex(zkClient, lockPath);
            if (null == time && null == unit) {
                lock.acquire();
//执行的处理
                return exec.exec(parameters);
            } else if (null != time && null != unit) {
                if (lock.acquire(time, unit)) {
                  //执行的处理
                    return exec.exec(parameters);
                } else {
                    log.info("zk distributedLockExec timeout...");
                }
            } else {
                log.error("zk distributedLockExec   time or unit is null");
            }
        } catch (Exception e) {
            log.error("zk distributed lock  exec failed", e);
        } finally {
            try {
                if (null != lock) {
                    lock.release();
                }
            } catch (Exception e) {
                log.error("zk distributed lock  relase failed", e);
            }
        }
        return null;
    }


}

线程分布式锁实现流程图:

 线程不足,循环等待问题:

  • 如果一个DAG中没有子流程,则如果Command中的数据条数大于线程池设置的阈值,则直接流程等待或失败。

  • 如果一个大的DAG中嵌套了很多子流程,如下图:

则会产生“死等”状态。MainFlowThread等待SubFlowThread1结束,

SubFlowThread1等待SubFlowThread2结束,SubFlowThread2等待SubFlowThread3结束,而SubFlowThread3等待线程池有新线程,则整个DAG流程不能结束,从而其中的线程也不能释放。这样就形成的子父流程循环等待的状态。此时除非启动新的Master来增加线程来打破这样的”僵局”,否则调度集群将不能再使用。

对于启动新Master来打破僵局,似乎有点差强人意,于是我们提出了以下三种方案来降低这种风险:

  1. 计算所有Master的线程总和,然后对每一个DAG需要计算其需要的线程数,也就是在DAG流程执行之前做预计算。因为是多Master线程池,所以总线程数不太可能实时获取。

  2. 对单Master线程池进行判断,如果线程池已经满了,则让线程直接失败。

  3. 增加一种资源不足的Command类型,如果线程池不足,则将主流程挂起。这样线程池就有了新的线程,可以让资源不足挂起的流程重新唤醒执行。

注意:Master Scheduler线程在获取Command的时候是FIFO的方式执行的。

 3、集群节点挂掉等异常容错处理

容错设计依赖于Zookeeper的Watcher机制,实现原理如下

Master监控其他Master和Worker的目录,如果监听到remove事件,则会根据具体的业务逻辑进行流程实例容错或者任务实例容错。

Master容错流程图:

 ZooKeeper Master容错完成之后则重新由EasyScheduler中Scheduler线程调度,遍历 DAG 找到”正在运行”和“提交成功”的任务,对”正在运行”的任务监控其任务实例的状态,对”提交成功”的任务需要判断Task Queue中是否已经存在,如果存在则同样监控任务实例的状态,如果不存在则重新提交任务实例。

Worker容错流程图:

 Master Scheduler线程一旦发现任务实例为” 需要容错”状态,则接管任务并进行重新提交。

由于“网络抖动”可能会使得节点短时间内失去和zk的心跳,从而发生节点的remove事件。对于这种情况,我们使用最简单的方式,那就是节点一旦和zk发生超时连接,则直接将Master或Worker服务停掉。

任务失败重试处理

失败分为:任务失败重试、流程失败恢复、流程失败重跑。

  • 任务失败重试是任务级别的,是调度系统自动进行的,比如一个Shell任务设置重试次数为3次,那么在Shell任务运行失败后会自己再最多尝试运行3次
  • 流程失败恢复是流程级别的,是手动进行的,恢复是从只能从失败的节点开始执行或从当前节点开始执行
  • 流程失败重跑也是流程级别的,是手动进行的,重跑是从开始节点进行

我们将工作流中的任务节点分了两种类型。

  • 一种是业务节点,这种节点都对应一个实际的脚本或者处理语句,比如Shell节点,MR节点、Spark节点、依赖节点等。

  • 还有一种是逻辑节点,这种节点不做实际的脚本或语句处理,只是整个流程流转的逻辑处理,比如子流程节等。

每一个业务节点都可以配置失败重试的次数,当该任务节点失败,会自动重试,直到成功或者超过配置的重试次数。逻辑节点不支持失败重试。但是逻辑节点里的任务支持重试。

如果工作流中有任务失败达到最大重试次数,工作流就会失败停止,失败的工作流可以手动进行重跑操作或者流程恢复操作

 4、日志查看实现

由于Web Application和Worker不一定在同一台机器上,所以查看日志不能像查询本地文件那样。有两种方案:

  • 将日志放到ES搜索引擎上存储,通过对es进行查询。

  • 通过gRPC通信获取远程日志信息

介于考虑到尽可能的系统设计的轻量级性,所以选择了gRPC实现远程访问日志信息。

GRPC的传输的性能以及I/O都比较高,日志查询起来也很快。

 5、任务优先级设计

如果没有优先级设计,采用公平调度设计的话,会遇到先行提交的任务可能会和后继提交的任务同时完成的情况,而不能做到设置流程或者任务的优先级,因此我们对此进行了重新设计,目前我们设计如下:

  • 按照不同流程实例优先级优先于同一个流程实例优先级优先于同一流程内任务优先级优先于同一流程内任务提交顺序依次从高到低进行任务处理。

    • 具体实现是根据任务实例的json解析优先级,然后把流程实例优先级流程实例id任务优先级_任务id信息保存在ZooKeeper任务队列中,当从任务队列获取的时候,通过字符串比较即可得出最需要优先执行的任务。

      • 流程定义的优先级是考虑到有些流程需要先于其他流程进行处理,这个可以在流程启动或者定时启动时配置,共有5级,依次为HIGHEST、HIGH、MEDIUM、LOW、LOWEST
      • 任务的优先级也分为5级,依次为HIGHEST、HIGH、MEDIUM、LOW、LOWEST,如下图所示

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2088050.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

ArkUI-布局(四)

ArkUI-布局 轮播部分属性及方法循环播放及自动轮播导航点样式页面切换方式轮播方向每页显示多个子页面自定义切换动画 选项卡使用方式部分属性及方法顶部导航和底部导航侧边导航限制导航栏的滑动固定导航栏和滚动导航栏自定义导航栏切换至指定页签 轮播 Swiper组件提供滑动轮播…

大语言模型从零开始训练全面指南:预训练、Tokenizer训练、指令微调、奖励模型、强化学习

在这篇文章中&#xff0c;我们将尽可能详细地梳理一个完整的 LLM 训练流程。包括模型预训练&#xff08;Pretrain&#xff09;、Tokenizer 训练、指令微调&#xff08;Instruction Tuning&#xff09;、奖励模型&#xff08;Reward Model&#xff09;和强化学习&#xff08;RLH…

SprinBoot+Vue线上教学平台的设计与实现

目录 1 项目介绍2 项目截图3 核心代码3.1 Controller3.2 Service3.3 Dao3.4 application.yml3.5 SpringbootApplication3.5 Vue 4 数据库表设计5 文档参考6 计算机毕设选题推荐7 源码获取 1 项目介绍 博主个人介绍&#xff1a;CSDN认证博客专家&#xff0c;CSDN平台Java领域优质…

基于yolov8的雾天行人车辆检测系统python源码+onnx模型+评估指标曲线+精美GUI界面

【算法介绍】 基于YOLOv8的雾天行人车辆检测系统是一种高效且先进的解决方案&#xff0c;专门用于在恶劣的雾天条件下检测和识别道路上的行人和车辆。YOLOv8作为最新的YOLO系列模型&#xff0c;自2023年推出以来&#xff0c;在目标检测领域展现了卓越的性能。该系统利用YOLOv8…

Java 入门指南:Java NIO —— Buffer(缓冲区)

NIO 的引入 在传统的 Java I/O 模型&#xff08;BIO&#xff09;中&#xff0c;I/O 操作是以阻塞的方式进行的。当一个线程执行一个 I/O 操作时&#xff0c;它会被阻塞直到操作完成。这种阻塞模型在处理多个并发连接时可能会导致性能瓶颈&#xff0c;因为需要为每个连接创建一…

Java-数据结构-时间和空间复杂度 (ಥ_ಥ)

目录&#xff1a; 一、算法效率&#xff1a; 1、我们如何衡量一个算法的好与坏&#xff1a; 2、算法效率&#xff1a; 二、时间复杂度&#xff1a; 1、概念&#xff1a; 2、大O的渐进表示法&#xff1a; 3、推导大O渐进方法&#xff1a; 4、时间复杂度的举例&#xff1…

【推荐】Linux 推荐软件

【推荐】Linux 推荐软件 星火应用商店 Spark-Store: 专注Linux应用适配的应用商店 专注Linux应用适配的应用商店 微信 基于wine工具;wine中的windows涉及很多DLL需要配置&#xff0c;可以借助winetricks、Q4wine&#xff0c;另外还需要一个windwos系统&#xff0c;用来复制其中…

私人诊所|基于SprinBoot+vue的私人诊所管理系统(源码+数据库+文档)

私人诊所管理系统 基于SprinBootvue的私人诊所管理系统 一、前言 二、系统设计 三、系统功能设计 系统功能实现 后台模块实现 管理员功能实现 患者功能实现 医生功能实现 四、数据库设计 五、核心代码 六、论文参考 七、最新计算机毕设选题推荐 八、源码获取&am…

el-table自定义合并表格

前沿 &#xff1a; 为了更好的展示数据&#xff0c;很多地方用到表格合并&#xff0c;但是element文档里面没有好的合并方法&#xff0c;只能自定义合并表格来解决需求。于是乎&#xff0c;写了以下方法&#xff0c;方面以后拿来即用。 自定义合并表格 表格数据 tableData: [{i…

图片怎么裁剪中间部分?这几种裁剪方法每个人都学的会!

图片怎么裁剪中间部分&#xff1f;在数字生活的广阔图景中&#xff0c;图片裁剪作为一项基本技能&#xff0c;其重要性日益凸显&#xff0c;这一操作不仅是对图像边界的精准界定&#xff0c;更是通往个性化表达与标准化应用的桥梁。从日常社交媒体的瞬间分享&#xff0c;到专业…

多模态工业异常检测算法整理

本文统计了MVTec 3D-AD上的多个多模态异常检测算法&#xff0c;仅对比其I-AUROC指标。数据的来源为多模态工业异常检测Benchmark | Ziuch の Blog&#xff0c;这位博主经常分享很多工业异常检测的优秀博文&#xff0c;质量很高。 MVTec 3D-AD相关的异常检测算法包含3大类&#…

智慧公厕系统,重塑公共卫生间新生态@卓振思众

在快节奏的现代生活中&#xff0c;公共卫生间作为城市基础设施的重要组成部分&#xff0c;其管理效率和使用体验直接关系到市民的生活质量。近年来&#xff0c;随着科技的飞速发展&#xff0c;智慧卫生间系统应运而生&#xff0c;以其智能化、便捷化的特点&#xff0c;正逐步改…

低代码用户中心的构建与应用

引言 在现代软件开发中&#xff0c;低代码平台因其高效、灵活、用户友好的特性而逐渐受到青睐。特别是在用户中心的构建方面&#xff0c;低代码平台能够显著提升开发效率&#xff0c;降低开发成本。本文将探讨如何利用低代码平台构建一个高效的用户中心&#xff0c;并分享一些…

树形dp + 位运算 + 差分,MC0362 异或

目录 一、题目 1、题目描述 2、输入输出 2.1输入 2.2输出 3、原题链接 二、解题报告 1、思路分析 2、复杂度 3、代码详解 一、题目 1、题目描述 2、输入输出 2.1输入 2.2输出 3、原题链接 码题集OJ-异或 (matiji.net) 二、解题报告 1、思路分析 考虑每个结点u&a…

工作 6 年,@Transactional 注解用的一塌糊涂

接手新项目一言难尽&#xff0c;别的不说单单就一个 Transactional 注解用的一塌糊涂&#xff0c;五花八门的用法&#xff0c;很大部分还失效无法回滚。 有意识的在涉及事务相关方法上加Transactional注解&#xff0c;是个好习惯。不过&#xff0c;很多同学只是下意识地添加这个…

Redis的ZSet底层数据结构

一、ZSet底层数据结构 typedef struct zset{// 跳表zskiplist *zsl;// 字典dict *dic; }zset类型的底层数据结构是由压缩列表或跳表、**字典&#xff08;哈希表&#xff09;**实现的。 如果zset中元素个数小于128个&#xff0c;并且每个元素的值小于64字节时&#xff0c;redi…

Prompt提示词如何写才能发挥大语言模型LLM的最大潜力

提示词 提示工程学是一门相对较新的学科&#xff0c;用于开发和优化提示&#xff0c;以便高效地利用语言模型&#xff08;LM&#xff09;来进行各种应用和研究主题。提示工程技能有助于更好地了解大型语言模型&#xff08;LLM&#xff09;的能力和限制。研究人员使用提示工程来…

错误处理与日志记录:在自动化脚本中实施的有效策略

目录 引言 错误处理机制 1. 异常捕获与处理 2. finally子句 3. 异常信息的打印输出 日志记录 1. 使用logging模块 基本配置 日志级别 日志回滚 2. 自定义日志格式 3. 多处理器和过滤器 实践案例 自动化测试中的错误处理与日志记录 脚本示例 结论 在自动化测试领…

载流子的产生与复合

文章目录 前言有三种形式能够产生载流子 前言 半导体中能贡献导电作用的电子和空穴称为载流子 有三种形式能够产生载流子 热激发&#xff08;本征激发&#xff09;&#xff1a;价带中的电子受到热激发可能会跃迁至导带&#xff0c;成为能够起导电作用的电子&#xff0c;同时对…

给自己复盘用的tjxt笔记day12第一部分

优惠券使用 优惠券规则定义 对优惠券的下列需求: 判断一个优惠券是否可用,也就是检查订单金额是否达到优惠券使用门槛 按照优惠规则计算优惠金额,能够计算才能比较并找出最优方案 生成优惠券规则描述,目的是在页面直观的展示各种方案,供用户选择 因此,任何一张优惠券都…