1、整体设计html
初来公司时,公司尚未大数据,我是做为大数据架构师招入的,结合公司的线上和线下业务,制定了以下的大数据架构路线图。前端
2、大数据任务开发和调度平台架构设计nginx
在设计完整体架构后,而且搭建完hadoop/yarn的大数据底层计算平台后, 按照整体架构设计思路, 首先须要构建的就是大数据开发平台。这也是一个很是核心的平台,也是最基础最重要的一个环节。web
一开始设计的架构图以下所示。算法
架构设计解释说明以下:数据库
MasterServer:json
MasterServer采用分布式无中心设计理念,MasterServer主要负责 DAG 任务切分、任务提交监控,并同时监听其它MasterServer和WorkerServer的健康状态。 MasterServer服务启动时向Zookeeper注册临时节点,经过监听Zookeeper临时节点变化来进行容错处理。后端
该服务内主要包含:
Distributed 分布式调度组件,主要负责定时任务的启停操做,当Distributed调起任务后,Master内部会有线程池具体负责处理任务的后续操做api
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任务以有向无环图的形式组装起来,从入度为零的节点进行拓扑遍历,直到无后继节点为止。
本文做者:张永清 转载请注明来源博客园:http://www.javashuo.com/article/p-dckjcjdl-ee.html
3、架构设计思想
一、中心化仍是去中心化设计的选择
中心化思想:中心化的设计理念比较简单,分布式集群中的节点按照角色分工,大致上分为两种角色:
中心化思想设计存在的不足:
去中心化思想:
二、分布式锁的设计
使用ZooKeeper实现分布式锁来实现同一时刻集群中只有一台Master执行Scheduler,或者只有一台Worker执行任务的提交处理。
获取分布式锁的核心流程算法以下:
本文做者:张永清 转载请注明来源博客园:http://www.javashuo.com/article/p-dckjcjdl-ee.html
线程分布式锁实现流程图:
线程不足,循环等待问题:
若是一个DAG中没有子流程,则若是Command中的数据条数大于线程池设置的阈值,则直接流程等待或失败。
若是一个大的DAG中嵌套了不少子流程,以下图:
则会产生“死等”状态。MainFlowThread等待SubFlowThread1结束,
SubFlowThread1等待SubFlowThread2结束,SubFlowThread2等待SubFlowThread3结束,而SubFlowThread3等待线程池有新线程,则整个DAG流程不能结束,从而其中的线程也不能释放。这样就造成的子父流程循环等待的状态。此时除非启动新的Master来增长线程来打破这样的”僵局”,不然调度集群将不能再使用。
对于启动新Master来打破僵局,彷佛有点差强人意,因而咱们提出了如下三种方案来下降这种风险:
计算全部Master的线程总和,而后对每个DAG须要计算其须要的线程数,也就是在DAG流程执行以前作预计算。由于是多Master线程池,因此总线程数不太可能实时获取。
对单Master线程池进行判断,若是线程池已经满了,则让线程直接失败。
增长一种资源不足的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节点,MR节点、Spark节点、依赖节点等。
还有一种是逻辑节点,这种节点不作实际的脚本或语句处理,只是整个流程流转的逻辑处理,好比子流程节等。
每个业务节点均可以配置失败重试的次数,当该任务节点失败,会自动重试,直到成功或者超过配置的重试次数。逻辑节点不支持失败重试。可是逻辑节点里的任务支持重试。
若是工做流中有任务失败达到最大重试次数,工做流就会失败中止,失败的工做流能够手动进行重跑操做或者流程恢复操做
4、日志查看实现
因为Web Application和Worker不必定在同一台机器上,因此查看日志不能像查询本地文件那样。有两种方案:
将日志放到ES搜索引擎上存储,经过对es进行查询。
经过gRPC通讯获取远程日志信息
介于考虑到尽量的系统设计的轻量级性,因此选择了gRPC实现远程访问日志信息。
GRPC的传输的性能以及I/O都比较高,日志查询起来也很快。
5、任务优先级设计
若是没有优先级设计,采用公平调度设计的话,会遇到先行提交的任务可能会和后继提交的任务同时完成的状况,而不能作到设置流程或者任务的优先级,所以咱们对此进行了从新设计,目前咱们设计以下:
按照不一样流程实例优先级优先于同一个流程实例优先级优先于同一流程内任务优先级优先于同一流程内任务提交顺序依次从高到低进行任务处理。
具体实现是根据任务实例的json解析优先级,而后把流程实例优先级流程实例id任务优先级_任务id信息保存在ZooKeeper任务队列中,当从任务队列获取的时候,经过字符串比较便可得出最须要优先执行的任务。