让 DolphinScheduler 1.3.4 开启 Netty 日志打印,解决流程实例一直在运行中的问题
关于新一代大数据任务调度 - Apache DolphinScheduler
Apache DolphinScheduler(incubator) 于 17 年在易观数科立项, 19 年 8 月进入 Apache 孵化器,已有 400+ 公司在生产上使用,代码+文档贡献者 200+ 位,贡献者主要来自 80+ 家公司及机构。DolphinScheduler (简称DS)致力于使大数据任务调度开箱即用,它以拖拉拽的可视化方式将各种任务间的关系组装成 DAG(有向无环图),并实时监控整个数据pipeline的运行状态,同时支持失败重试、重跑、恢复失败、补数等大数据常用操作
01
问题描述
我们在 DS 1.3.4 新版本基础上添加了 “获取 sql 类型任务运行结果数据保存到文件” 的功能,在运行工作流的时候,调度一直在运行中,api-server 日志正常,master-server 没有报错,worker-server 也没有报错,流程实例在运行中,任务实例处于已提交状态,然后不动了,卡死在这里了。
02
问题定位
流程实例在执行中,说明 master-server 改变了流程实例状态,排查到 master-server 日志中没有 Netty 发送部分,于是对源码进行了分析:
将任务添加到备用队列
private void addTaskToStandByList(TaskInstance taskInstance){
logger.info("add task to stand by list: {}", taskInstance.getName());
try {
readyToSubmitTaskQueue.put(taskInstance);
} catch (Exception e) {
logger.error("add task instance to readyToSubmitTaskQueue error");
}
}
TaskPriorityQueueConsumer队列优先级消费线程会从备用任务队列中不断的取出优先级高的队列,进行分发
public void run() {
List<String> failedDispatchTasks = new ArrayList<>();
while (Stopper.isRunning()){
try {
int fetchTaskNum = masterConfig.getMasterDispatchTaskNumber();
failedDispatchTasks.clear();
for(int i = 0; i < fetchTaskNum; i++){
if(taskPriorityQueue.size() <= 0){
Thread.sleep(Constants.SLEEP_TIME_MILLIS);
continue;
}
// 从任务备用队列中取出优先级高的任务
String taskPriorityInfo = taskPriorityQueue.take();
TaskPriority taskPriority = TaskPriority.of(taskPriorityInfo);
//分发备用任务队列中的任务
boolean dispatchResult = dispatch(taskPriority.getTaskId());
if(!dispatchResult){
failedDispatchTasks.add(taskPriorityInfo);
}
}
if (!failedDispatchTasks.isEmpty()) {
for (String dispatchFailedTask : failedDispatchTasks) {
taskPriorityQueue.put(dispatchFailedTask);
}
// If there are tasks in a cycle that cannot find the worker group,
// sleep for 1 second
if (taskPriorityQueue.size() <= failedDispatchTasks.size()) {
TimeUnit.MILLISECONDS.sleep(Constants.SLEEP_TIME_MILLIS);
}
}
}catch (Exception e){
logger.error("dispatcher task error",e);
}
}
}
接着进行分析,TaskPriorityQueueConsumer#dispatch 任务分发方法新增日志。
protected boolean dispatch(int taskInstanceId) {
logger.info("dispatch taskInstanceId:{}", taskInstanceId);
boolean result = false;
try {
TaskExecutionContext context = getTaskExecutionContext(taskInstanceId);
ExecutionContext executionContext = new ExecutionContext(context.toCommand(), ExecutorType.WORKER, context.getWorkerGroup());
//新增打印分发内容日志,否则不知道TaskPriorityQueueConsumer是否在分发任务
logger.info("dispatch executionContext:{}", JSONObject.toJSONString(executionContext));
if (taskInstanceIsFinalState(taskInstanceId)){
// when task finish, ignore this task, there is no need to dispatch anymore
return true;
}else{
result = dispatcher.dispatch(executionContext);
}
} catch (ExecuteException e) {
logger.error("dispatch error",e);
}
return result;
}
ExecutorDispatcher#dispatch实际的任务分发执行器,将会根据最小权重算法获取到work执行机器的host,然后去执行最终的netty发送操作,这里新增了打印host机器日志操作,我们就知道到时候接收任务的机器是哪一台,可以到那一台上去看日志。
public Boolean dispatch(final ExecutionContext context) throws ExecuteException {
/**
* get executor manager
*/
ExecutorManager<Boolean> executorManager = this.executorManagers.get(context.getExecutorType());
if(executorManager == null){
throw new ExecuteException("no ExecutorManager for type : " + context.getExecutorType());
}
/**
* host select
*/
Host host = hostManager.select(context);
logger.info("host info:{}", JSONObject.toJSONString(host));
if (StringUtils.isEmpty(host.getAddress())) {
throw new ExecuteException(String.format("fail to execute : %s due to no suitable worker , " +
"current task need to %s worker group execute",
context.getCommand(),context.getWorkerGroup()));
}
context.setHost(host);
executorManager.beforeExecute(context);
try {
/**
* task execute
*/
return executorManager.execute(context);
} finally {
executorManager.afterExecute(context);
}
}
NettyExecutorManager#doExecute Netty最终发送命令到 worker-server 的方法,打印一下发送的 command 和 host
private void doExecute(final Host host, final Command command) throws ExecuteException {
/**
* retry count,default retry 3
*/
int retryCount = 3;
boolean success = false;
do {
try {
logger.info("send command:{} host:{}", command, host);
nettyRemotingClient.send(host, command);
success = true;
} catch (Exception ex) {
logger.error(String.format("send command : %s to %s error", command, host), ex);
retryCount--;
try {
Thread.sleep(100);
} catch (InterruptedException ignore) {}
}
} while (retryCount >= 0 && !success);
if (!success) {
throw new ExecuteException(String.format("send command : %s to %s error", command, host));
}
}
03
尝试解决
首先判断是否 master- server 这出了异常
我们在 master-server 模块里添加了一些日志,修改完 master-server 后,重新启动一下,运行一个流程,查看日志,发现最终打印了 NettyExecutorManager#doExecute 方法的发送日志里面包括了command 命令和 host,说明 master-server 无异常
根据 host 查看接受命令的 worker-server
发现 work-server 依然没有任何日志打印。
观察 logback-worker.xml 发现 worker 只打印线程名称开头为 “Worker-” 的日志
public class WorkerLogFilter extends Filter<ILoggingEvent> {
/**
* level
*/
Level level;
/**
* Accept or reject based on thread name
* @param event event
* @return FilterReply
*/
@Override
public FilterReply decide(ILoggingEvent event) {
if (event.getThreadName().startsWith("Worker-")){
return FilterReply.ACCEPT;
}
return FilterReply.DENY;
}
public void setLevel(String level) {
this.level = Level.toLevel(level);
}
}
下面开启新一波的修改:
1、手动修改if 判断为event.getThreadName().startsWith("Worker-") || event.getThreadName().startsWith("Netty"),然后将 netty 线程名命名为以 “Netty” 开头。
private final ExecutorService defaultExecutor = Executors.newFixedThreadPool(Constants.CPUS, new ThreadFactoryBuilder()
.setDaemon(true)
.setNameFormat("NettyRemotingServer")
.build());
2、work-server 中 netty 接收命令的地方新增日志打印,修改部分 warn 级别日志为info,打印到日志文件方便分析。
private void processReceived(final Channel channel, final Command msg) {
logger.info("processReceived command:{} channel:{}", JSONObject.toJSONString(msg), channel);
final CommandType commandType = msg.getType();
final Pair<NettyRequestProcessor, ExecutorService> pair = processors.get(commandType);
if (pair != null) {
Runnable r = new Runnable() {
@Override
public void run() {
try {
pair.getLeft().process(channel, msg);
} catch (Throwable ex) {
logger.error("process msg {} error", msg, ex);
}
}
};
try {
pair.getRight().submit(r);
} catch (RejectedExecutionException e) {
//修改warn为info
logger.info("thread pool is full, discard msg {} from {}", msg, ChannelUtils.getRemoteAddress(channel));
}
} else {
//修改warn为info
logger.info("commandType {} not support", commandType);
}
}
根据 host 查看接收命令的 worker-server
重启后运行流程,发现 work-server 日志打印正常了,接收到任务了,在获取 sql 类型任务数据路径失败报错了。
04
最终解决
获取 sql 类型任务数据这部分是我们在 DS 上新增的功能,定位到问题后,我们修复了获取 sql 类型任务数据路径失败问题,至此流程实例运行中卡死问题解决!
05
参与开源贡献
贡献不限于代码,答疑,完善文档,编写相关文章也有可能成为 Committer 。文章内容包含不限于:DS 部署,使用,经验分享,故障处理,源码分析等等。
DolphinScheduler 社区参与贡献的方式,包括:
投稿欢迎联系微信:
社区汇总了以下适合新手的问题列表:https://github.com/apache/incubator-dolphinscheduler/issues/4124
如何参与贡献链接:https://dolphinscheduler.apache.org/zh-cn/docs/development/contribute.html
来吧,DolphinScheduler开源社区需要您的参与,为中国开源崛起添砖加瓦吧,哪怕只是小小的一块瓦,汇聚起来的力量也是巨大的
如果您想参与贡献,我们也有个开发者种子孵化群,可以添加微信(easyworkflow) ,添加时请说明想参与贡献哈
关于滴普科技
北京滴普科技成立于2018年,是全场景数据智能服务商。公司致力于以云原生微服务框架的研发,综合5G、IoT、大数据、AI、云计算等新技术,形成可高度扩展的商业智能和产业智能的平台产品,为组织提供全场景数据智能服务。
喜欢 DolphinScheduler 的话,别忘了「分享」「收藏」「点赞」和「在看」,让更多人知道我们哦????
点击“阅读原文”,直达 Apache DolphinScheduler 官网
让 DolphinScheduler 1.3.4 开启 Netty 日志打印,解决流程实例一直在运行中的问题的更多相关文章
- LOG4NET开源日志dll引用流程,在net3.5中已经实践ok
一,在app.config中配置 <?xml version="1.0"?><configuration> <configSections> & ...
- ubuntu14.04开启crontab日志
ubuntu默认没有开启cron日志记录 1. 修改rsyslog sudo vim /etc/rsyslog.d/50-default.conf cron.* /var/log/cron.log # ...
- Nginx 开启 debug 日志的办法
译序:一般来讲,Nginx 的错误日志级别是 error,作为 Nginx 用户来讲,你设置成 info 就足够用了. 但有时有些难以挖掘的 bug,需要看到更详细的 debug 级别 ...
- 开启bin-log日志mysql报错:This function has none of DETERMINISTIC, NO SQL解决办法
开启bin-log日志mysql报错:This function has none of DETERMINISTIC, NO SQL解决办法: 创建存储过程时 出错信息: ERROR 1418 (HY ...
- mysql开启binlog日志和慢查询日志
1)首先,为什么要开启binlog日志和慢查询日志呢? binlog日志会记录下数据库的所以增删改操作,当不小心删除.清空数据,或数据库系统出错,这时候就可以使用binlog日志来还原数据库,简单来说 ...
- ubuntu开启慢日志
ubuntu 开启mysql日志记录 1.找到mysql的配置文件sudo vim /etc/mysql/my.cnf将下面两行的#去掉#general_log_file = /var/log/mys ...
- mysql开启查询日志功能
1.开启查询日志 https://www.cnblogs.com/kerrycode/p/7130403.html MYsql 查询日志配置 mysql> show variables ...
- 转载Linux下开启MySQL日志
转载https://blog.csdn.net/weixin_38187469/article/details/79273962 开启mysql日志 1.查看日志是否启用 mysql> sh ...
- 【MySQL解惑笔记】Mysql5.7.x无法开启二进制日志
一.开启二进制日志 1)未开启二进制日志之前: mysql> show variables like 'log_bin'; +---------------+-------+ | Variabl ...
随机推荐
- Redis - 为什么 Redis 是单线程的?
Redis中work线程是单线程的.也就是对于业务数据的操作是单线程的. Redis中存在多线程操作 异步关闭文件 异步将缓冲区冲洗到磁盘文件中 异步删除键值对 Redis是基于内存的,所以cpu不是 ...
- Docker容器编译安装Redis
Docker容器编译安装Redis 1.创建容器 -i 交互模式 -d 后端运行 -h 容器的hostname --name 容器名 --network 网卡 --ip IP地址 -p 端口映射 -- ...
- 关于swiper插件在vue2的使用
最近做项目用到了vue-awesome-swiper,总结一下使用方法 第一步:安装依赖 npm install swiper vue-awesome-swiper --save or npm ins ...
- ansible在linux和windows批量部署zabbix-agent2
--- - hosts: linux tasks: - name: copy centos 7 zabbix-agent2 copy: src=zabbix-agent2-5.0.11-1.el7.x ...
- Google Colab初次使用
网页无法加载,出现HTTP ERROR 407 开启chrome时不要在最下面的固定栏打开,否则会出错.
- CF484A Bits
CF484A Bits 题目 https://codeforces.com/problemset/problem/484/A 题解 思路 知识点:贪心,位运算. 每位独立考虑,要使 \(1\) 的数量 ...
- 霍普菲尔得神经网络(Hopfield Neural Network)
设计一个反馈网络存储下列目标平衡点: T = [ 1 -1; -1 1 ]; 并用6组任意随机初始列矢量,包括一组在目标平衡点连线的垂直平分线上的一点作为输入矢量对所设计的网络的平衡点进行测试,观 ...
- mongodb 数据块迁移的源码分析
1. 简介 上一篇我们聊到了mongodb数据块的基本概念,和数据块迁移的主要流程,这篇文章我们聊聊源码实现部分. 2. 迁移序列图 数据块迁移的请求是从配置服务器(config server)发给( ...
- NewApiDay03_File类
File类创建一个新文件 File类的每一个实例可以表示硬盘(文件系统)中的一个文件或目录(实际上表示的是一个抽象路径) 使用File可以做到: 1:访问其表示的文件或目录的属性信息,例如:名字,大小 ...
- Solution -「Luogu 4135」作诗
写在前面 & 前置芝士 好像是好久没有打理 blog 了.感觉上学期是有点颓.嘶,初三了好好冲一次吧. 那么回到这道题目.你会分块就能看懂. 题目大意 先挂个来自洛谷的 link. ...