热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

dataNode秒退的故障排查过程

情景在今年年末的时候,公司安排任务,让我临时搭建一个hadoop3.1.1版本的集群来测试一些功能,本以为是一件很轻松的任务,结果还是在半路翻车了集群配置完成后,在启动data

情景

  在今年年末的时候,公司安排任务,让我临时搭建一个hadoop3.1.1版本的集群来测试一些功能,本以为是一件很轻松的任务,结果还是在半路翻车了

集群配置完成后,在启动dataNode的时候,大概不到2秒,就出错启动不起来,按照以往经验,二话不说直接看 dataNode的日志

 

  打开一看,傻眼了,日志截图如下:



 

    日志就只报了一个下面这样的报错给我,没给个异常栈,我连猜都不好猜,并且这个退出信息,每次启动dataNode的时候,都会在不同的位置突然跳出来,所以我猜测,这还是个异步的线程触发的这个关闭dataNode操作

1 1 2021-11-17 21:16:23,098 ERROR org.apache.hadoop.hdfs.server.datanode.DataNode: RECEIVED SIGNAL 15: SIGTERM
2 2 2021-11-17 21:16:23,102 INFO org.apache.hadoop.hdfs.server.datanode.DataNode: SHUTDOWN_MSG:
3 3 /************************************************************
4 4 SHUTDOWN_MSG: Shutting down DataNode at hadoop01/192.168.55.40
5 5 ************************************************************/

 

  接着就去翻源码,希望从源码中发现点什么,找到了这条消息打印的地方,果然是个异步调用,是一个钩子,当被关闭的时候,触发调用这个run方法中的打印消息

  org.apache.hadoop.util.StringUtils#startupShutdownMessage(java.lang.Class, java.lang.String[], org.apache.hadoop.util.LogAdapter)

1 static void startupShutdownMessage(Class clazz, String[] args,
2 final LogAdapter LOG) {
3 final String hostname = NetUtils.getHostname();
4 final String classname = clazz.getSimpleName();
5 LOG.info(createStartupShutdownMessage(classname, hostname, args));
6
7 if (SystemUtils.IS_OS_UNIX) {
8 try {
9 SignalLogger.INSTANCE.register(LOG);
10 } catch (Throwable t) {
11 LOG.warn("failed to register any UNIX signal loggers: ", t);
12 }
13 }
14 ShutdownHookManager.get().addShutdownHook(
15 new Runnable() {
16 @Override
17 public void run() {
18 //【重点】打印退出消息
19 LOG.info(toStartupShutdownString("SHUTDOWN_MSG: ", new String[]{
20 "Shutting down " + classname + " at " + hostname}));
21 }
22 }, SHUTDOWN_HOOK_PRIORITY);
23
24 }

 

  具体打印方法的就是org.apache.hadoop.util.StringUtils#toStartupShutdownString

1 public static String toStartupShutdownString(String prefix, String[] msg) {
2 StringBuilder b = new StringBuilder(prefix);
3 b.append("\n/************************************************************");
4 for(String s : msg)
5 b.append("\n").append(prefix).append(s);
6 b.append("\n************************************************************/");
7 return b.toString();
8 }

  

  这时候就麻烦了,我们也不知道谁异步会来触发这玩意,这时候有显示个异常栈就好了

  所以,接下来,我准备用Javassist 字节码技术,对jar包中的这段代码进行注入修改,让它在退出的时候,顺便给我打印一个异常栈出来


重头戏:Javassist

  javassist具体原理网上很多,我就不多说了,直接上代码


  1、首先将要修改的jar包下载下来

  要下载哪个jar包呢?

  通过源码,我们知道,这个类在hadoop-common这个模块,在pom.xml中也可以确认它的jar包应该是hadoop-common.jar 或者是 hadoop-common-3.1.1.jar 这样

  



  

  pom.xml

1 <project xmlns="http://maven.apache.org/POM/4.0.0"
2 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
3 xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
4 https://maven.apache.org/xsd/maven-4.0.0.xsd">
5 <modelVersion>4.0.0modelVersion>
6 <parent>
7 <groupId>org.apache.hadoopgroupId>
8 <artifactId>hadoop-project-distartifactId>
9 <version>3.1.3version>
10 <relativePath>../../hadoop-project-distrelativePath>
11 parent>
12
13 <artifactId>hadoop-commonartifactId>
14 <version>3.1.3version>
15 <description>Apache Hadoop Commondescription>
16 <name>Apache Hadoop Commonname>
17 <packaging>jarpackaging>

 

  然后通过find命令找到jar包

1 [hdfs@hadoop01 hsperfdata_hdfs]$ find /opt/hadoop-3.1.1/ -name *hadoop-common*
2 /opt/hadoop-3.1.1/share/hadoop/common/hadoop-common-3.1.1-tests.jar
3 /opt/hadoop-3.1.1/share/hadoop/common/hadoop-common-3.1.1.jar
4 /opt/hadoop-3.1.1/share/hadoop/common/sources/hadoop-common-3.1.1-sources.jar
5 /opt/hadoop-3.1.1/share/hadoop/common/sources/hadoop-common-3.1.1-test-sources.jar
6 /opt/hadoop-3.1.1/share/doc/hadoop/hadoop-project-dist/hadoop-common
7 /opt/hadoop-3.1.1/share/doc/hadoop/hadoop-project-dist/hadoop-common/build/source/hadoop-common-project
8 /opt/hadoop-3.1.1/share/doc/hadoop/hadoop-project-dist/hadoop-common/build/source/hadoop-common-project/hadoop-common
9 /opt/hadoop-3.1.1/share/doc/hadoop/hadoop-common-project

  所以就找到了/opt/hadoop-3.1.1/share/hadoop/common/hadoop-common-3.1.1.jar

    下载下来,放到一个目录,备用,放到C:\Users\Administrator\Desktop\test\hadoop-common-3.1.1.jar


2、写代码,对jar包进行读取和写出修改的文件

  使用依赖

1 <dependency>
2 <groupId>org.javassistgroupId>
3 <artifactId>javassistartifactId>
4 <version>3.20.0-GAversion>
5 dependency>

  Demo.java

  在代码中,我插入了一个Exception对象,用printStackTrace() 方法打印出异常堆栈

1 package aaa.bbb.ccc.day2021_11_17;
2
3 import javassist.*;
4 import javassist.bytecode.*;
5
6 import java.io.IOException;
7 import java.util.Arrays;
8 import java.util.List;
9 import java.util.TreeMap;
10 import java.util.stream.Collectors;
11
12 public class Demo {
13 public static void main(String[] args) throws Exception {
14 //这个是得到反编译的池
15 ClassPool pool = ClassPool.getDefault();
16 try {
17 //取得需要反编译的jar文件,设定路径
18 pool.insertClassPath("C:\\Users\\Administrator\\Desktop\\test\\hadoop-common-3.1.1.jar");
19 //取得需要反编译修改的文件,注意是完整路径
20 CtClass cc1 = pool.get("org.apache.hadoop.util.StringUtils");
21
22 //取得需要修改的方法(正常要用这样的方法,但是无奈hadoop这个方法同名重载了3个,我不知道如何写这个方法名,该方法适合方法名没有重载适可以使用)
23 CtMethod method = cc1.getDeclaredMethod("toStartupShutdownString");
24
25 //可以用insertBefore、insertAfter、setBody多种方法一起,来设置
26 method.setBody("{System.out.println(\"==============\" + Thread.currentThread().getName() + \"======================\");\n" +
27 " new java.lang.Exception(\"========test myError==========\").printStackTrace();\n" +
28 "\n" +
29 " StringBuilder b = new StringBuilder($1);\n" +
30 " b.append(\"\\n/************************************************************\");\n" +
31 " for (int i = 0; i <$2.length; i++) {\n" +
32 " String s = $2[i];\n" +
33 " b.append(\"\\n\").append($1).append(s);\n" +
34 " }\n" +
35 " b.append(\"\\n************************************************************/\");\n" +
36 " return b.toString();}");
37
38 //遇到复杂的,有重载的情况,用下面这种方法。。。虽然土一点,但是管用
39 // CtMethod method = Arrays.stream(cc1.getMethods())
40 // .filter(row -> "startupShutdownMessage".equals(row.getName())) //过滤出方法名为startupShutdownMessage的所有方法
41 // .collect(Collectors.toList())
42 // .get(1);//取出需要的
43
44 //将修改出来的类的class,输出到指定路径
45 cc1.writeFile("C:\\Users\\Administrator\\Desktop\\test");
46 } catch (NotFoundException e) {
47 e.printStackTrace();
48 } catch (CannotCompileException e) {
49 e.printStackTrace();
50 } catch (IOException e) {
51 e.printStackTrace();
52 }
53 }
54 }

 


3、检查生成出来的代码,果然有编译进去



 

 


4、将编译出来的新class覆盖旧的jar里面的旧class

  在windows,打开命令行,进入jar包的目录下,执行

 1 jar uf hadoop-common-3.1.1.jar org\apache\hadoop\util\StringUtils.class 

  将StringUtils.class 覆盖掉jar包中的旧的StringUtils.class

 


5、将注入的新jar,上传到服务器上代替旧jar

  旧的就是:/opt/hadoop-3.1.1/share/hadoop/common/hadoop-common-3.1.1.jar

 


效果

  有效果了,看得出是一个线程池调用的,并且有两个地方调用了(下面两个蓝框),在第二个蓝框里,我们看不出任何有用的信息,只能知道是一个线程池异步调用打印退出信息,所以我们来看下第一个地方,说不定有所收获



 

 


对源码进行分析

  第二个蓝框看不出东西,所以我们到第一个蓝框看看能不能看出点啥

  我看到org.apache.hadoop.hdfs.server.datanode.DataNode.secureMain 这个时,我怀疑是这里面的操作导致的,但我并不太确定问题是不是出在这个方法中,所以我做了一个测试,用javassist工具,在createDataNode的开始和结束做4个标记,每个标记做完都会休眠5秒钟,如果都打印出来,证明不在这个方法中,如果打印了stage1没打印stage2,就证明在这方法之前就有问题

  所以还是老思路,把这个包含DataNode类的jar包找出来,然后给DataNode类下的createDataNode方法进行头尾加上标记,具体我就不细讲,和前面流程一样,就是find找到jar,然后下载下来,用代码进行注入,再打包放回去

  这里给出注入的代码,比较有意思的是,这里由于createDataNode 方法中,包含了一个别的jar包类Configuration作为参数,所以我们同时也要把别的jar作为依赖放到classpath下,用这句代码来实现pool.appendClassPath("C:\\hadoop-common-3.1.1.jar");

 

1 import javassist.*;
2
3 import java.io.IOException;
4
5 public class Demo1 {
6 public static void main(String[] args) throws Exception {
7 //这个是得到反编译的池
8 ClassPool pool = ClassPool.getDefault();
9 try {
10
11 //取得需要反编译的jar文件,设定路径
12 pool.insertClassPath("C:\\Users\\Administrator\\Desktop\\test\\hadoop-hdfs-3.1.1.jar");
13
14 //如果我们的要注入的方法中,参数使用其他jar包的类,这时候要把依赖类添加到classPath中
15 pool.appendClassPath("C:\\Users\\Administrator\\Desktop\\test\\hadoop-common-3.1.1.jar");
16 //取得需要反编译修改的文件,注意是完整路径
17 CtClass cc1 = pool.get("org.apache.hadoop.hdfs.server.datanode.DataNode");
18
19 // //取得需要修改的方法(正常要用这样的方法,但是无奈hadoop这个方法同名重载了3个,我不知道如何写这个方法名,该方法适合方法名没有重载适可以使用)
20 CtMethod method = cc1.getDeclaredMethod("secureMain");
21
22 //遇到复杂的,有重载的情况,用下面这种方法。。。虽然土一点,但是管用
23 // CtMethod method = Arrays.stream(cc1.getMethods())
24 // .filter(row -> "createDataNode".equals(row.getName())) //过滤出方法名为startupShutdownMessage的所有方法
25 // .collect(Collectors.toList())
26 // .get(1);//取出需要的
27
28
29 //可以用insertBefore、insertAfter、setBody多种方法一起,来设置
30 method.insertBefore("{" +
31 "System.out.println(\"===============secureMain stage1=================\");\n" +
32 "Thread.sleep(5000L);" +
33 "System.out.println(\"===============secureMain stage2=================\");\n" +
34 "}");
35
36 method.insertAfter("{" +
37 "System.out.println(\"===============secureMain stage3=================\");\n" +
38 "Thread.sleep(5000L);" +
39 "System.out.println(\"===============secureMain stage4=================\");\n" +
40 "}");
41
42 //将修改出来的类的class,输出到指定路径
43 cc1.writeFile("C:\\Users\\Administrator\\Desktop\\test");
44 } catch (NotFoundException e) {
45 e.printStackTrace();
46 } catch (CannotCompileException e) {
47 e.printStackTrace();
48 } catch (IOException e) {
49 e.printStackTrace();
50 }
51 }
52 }

  

  结果如下,发现在secureMain还没开始前就出问题了,难道是DataNode的main函数就出问题了?

1 ===============secureMain stage1=================
2 core file size (blocks, -c) 0
3 data seg size (kbytes, -d) unlimited
4 scheduling priority (-e) 0
5 file size (blocks, -f) unlimited
6 pending signals (-i) 63445
7 max locked memory (kbytes, -l) 64
8 max memory size (kbytes, -m) unlimited
9 open files (-n) 65535
10 pipe size (512 bytes, -p) 8
11 POSIX message queues (bytes, -q) 819200
12 real-time priority (-r) 0
13 stack size (kbytes, -s) 8192
14 cpu time (seconds, -t) unlimited
15 max user processes (-u) 65535
16 virtual memory (kbytes, -v) unlimited
17 file locks (-x) unlimited

  然后马上查看了一下main方法的源码:org.apache.hadoop.hdfs.server.datanode.DataNode#main

  发现更懵了,因为这里的代码就更简单了,不是secureMain方法,那就是DFSUtil.parseHelpArgument出问题了?

1 public static void main(String args[]) {
2 if (DFSUtil.parseHelpArgument(args, DataNode.USAGE, System.out, true)) {
3 System.exit(0);
4 }
5
6 secureMain(args, null);
7 }

  带着这种用javassist 在方法头尾做标记的排查的思路,我发现居然也不是在main方法中产生的!

  难道是DataNode的静态代码块?

  DataNode的静态代码块是

 1 static{ 2 HdfsConfiguration.init(); 3 } 

 

  追踪进去org.apache.hadoop.hdfs.HdfsConfiguration#init,里面没东西,看来不是这里

   1 public static void init() { }  

 

  难道是父类的静态代码块,或者是静态字段的初始化有问题?但是我看进去后,无论是DataNode类,还是其父类,都没有发现这样静态代码块或者会导致dataNode退出的静态属性

   所以DataNode的异常退出,和Hadoop的代码没有关系


对系统日志进行分析

  那问题在哪里?我想到会不会是一启动占用资源过多,被linux系统杀死,所以我查阅了/var/log/messages 日志,里面也只有正常的用户登录等一些简单的操作,看来问题也不在这里


对可疑进程进行分析

  但是我觉得,被杀死,既然不是DataNode自身的问题,那么一定有一个外部的东西,去杀死它,可能不是linux系统杀死,但是说不定是启动脚本中,有什么守护进程之类的东西杀死它,所以我写了一个脚本,每隔50毫秒打印出和hdfs用户相关的进程(因为我是用hdfs用户权限启动的)

 

  脚本如下:

1 #!/bin/bash
2 while [ true ]
3 do
4 ps aux | grep hdfs
5 echo "=============================="
6 sleep 0.05
7 done

  

  接着,我再次启动dataNode,然后我看到了下面这些进程

1 ==============================
2 root 18771 0.0 0.0 191880 2348 pts/1 S 14:45 0:00 su hdfs
3 hdfs 18773 0.0 0.0 115548 2112 pts/1 S 14:45 0:00 bash
4 hdfs 21215 4.0 0.0 113832 2200 pts/1 S+ 14:50 0:00 bash /opt/hadoop-3.1.1/sbin/hadoop-daemons.sh start datanode
5 hdfs 21245 5.0 0.0 113924 2284 pts/1 S+ 14:50 0:00 bash /opt/hadoop-3.1.1/bin/hdfs --workers --daemon start datanode
6 hdfs 21277 0.0 0.0 113924 1180 pts/1 S+ 14:50 0:00 bash /opt/hadoop-3.1.1/bin/hdfs --workers --daemon start datanode
7 hdfs 21278 2.0 0.0 130816 2992 pts/1 S+ 14:50 0:00 ssh -o BatchMode=yes -o StrictHostKeyChecking=no -o COnnectTimeout=10s localhost -- /opt/hadoop-3.1.1/bin/hdfs --daemon start datanode
8 hdfs 21279 0.0 0.0 117044 956 pts/1 S+ 14:50 0:00 sed s/^/localhost: /
9 root 21280 1.0 0.0 33692 3068 ? Ss 14:50 0:00 sshd: hdfs [priv]
10 hdfs 21287 0.0 0.0 33692 1408 ? S 14:50 0:00 sshd: hdfs@notty
11 hdfs 21289 9.0 0.0 113880 2304 ? Ss 14:50 0:00 bash /opt/hadoop-3.1.1/bin/hdfs --daemon start datanode
12 hdfs 21353 19.0 0.1 5918412 31872 ? Sl 14:50 0:00 /opt/jdk1.8.0_271/bin/java -Dproc_datanode -Djava.net.preferIPv4Stack=true -Dhadoop.security.logger=ERROR,RFAS -Dyarn.log.dir=/var/log/hadoop -Dyarn.log.file=hadoop-hdfs-datanode-hadoop01.log -Dyarn.home.dir=/opt/hadoop-3.1.1 -Dyarn.root.logger=INFO,console -Djava.library.path=/opt/hadoop-3.1.1/lib/native -Dhadoop.log.dir=/var/log/hadoop -Dhadoop.log.file=hadoop-hdfs-datanode-hadoop01.log -Dhadoop.home.dir=/opt/hadoop-3.1.1 -Dhadoop.id.str=hdfs -Dhadoop.root.logger=INFO,RFA -Dhadoop.policy.file=hadoop-policy.xml org.apache.hadoop.hdfs.server.datanode.DataNode
13 hdfs 21354 0.0 0.0 108056 616 ? S 14:50 0:00 sleep 1
14 root 21397 0.0 0.0 112816 956 pts/0 S+ 14:50 0:00 grep hdfs

  

  其中下面这三个进程,引起我的关注

1 hdfs 21215 4.0 0.0 113832 2200 pts/1 S+ 14:50 0:00 bash /opt/hadoop-3.1.1/sbin/hadoop-daemons.sh start datanode
2 hdfs 21277 0.0 0.0 113924 1180 pts/1 S+ 14:50 0:00 bash /opt/hadoop-3.1.1/bin/hdfs --workers --daemon start datanode
3 hdfs 21353 19.0 0.1 5918412 31872 ? Sl 14:50 0:00 /opt/jdk1.8.0_271/bin/java -Dproc_datanode -Djava.net.preferIPv4Stack=true -Dhadoop.security.logger=ERROR,RFAS -Dyarn.log.dir=/var/log/hadoop -Dyarn.log.file=hadoop-hdfs-datanode-hadoop01.log -Dyarn.home.dir=/opt/hadoop-3.1.1 -Dyarn.root.logger=INFO,console -Djava.library.path=/opt/hadoop-3.1.1/lib/native -Dhadoop.log.dir=/var/log/hadoop -Dhadoop.log.file=hadoop-hdfs-datanode-hadoop01.log -Dhadoop.home.dir=/opt/hadoop-3.1.1 -Dhadoop.id.str=hdfs -Dhadoop.root.logger=INFO,RFA -Dhadoop.policy.file=hadoop-policy.xml org.apache.hadoop.hdfs.server.datanode.DataNode

  

  分析如下:

    1、hadoop-daemons.sh start datanode 是我启动dataNode的命令,

    2、/opt/hadoop-3.1.1/bin/hdfs --workers --daemon start datanode 通过翻阅shell脚本,就知道,这是由 hadoop-daemons.sh 产生而执行的具体操作

    3、最后一个的java进程,是 hdfs 脚本 启动dataNode的具体java命令

 

  抱着试试的心态,我按照第三个java的命令,启动dataNode,哎呀,能够正常运行,更加证明dtaNode是没问题的,那么第一个 hadoop-daemons.sh 能执行出 第二个命令,所以第一个hadoop-daemons.sh命令 是没问题的,那么问题应该就出在 hdfs 命令上,也就是下面这条命令

 1 /bin/hdfs --workers --daemon start datanode  

  

  我用这条命令执行一下,果然问题就会复现

  现在开始定位问题

 


定位hdfs脚本具体怎么执行代码的 

  bin/hdfs 入口

1 if [[ -f "${HADOOP_LIBEXEC_DIR}/hdfs-config.sh" ]]; then
2 #【重点】
3 . "${HADOOP_LIBEXEC_DIR}/hdfs-config.sh"
4 else
5 echo "ERROR: Cannot execute ${HADOOP_LIBEXEC_DIR}/hdfs-config.sh." 2>&1
6 exit 1
7 fi

  sbin/hadoop-config.sh:104

  解析传入的所有参数,也就是 --workers --daemon start datanode

1#【重点】
3 hadoop_parse_args "$@"

  

  bin/hadoop-functions.sh:2519

  解析第一个参数,也就是--workers,将变量HADOOP_WORKER_MODE设置为true

1 function hadoop_parse_args
2 {
3 HADOOP_DAEMON_MODE="default"
4 HADOOP_PARSE_COUNTER=0
5
6 # not all of the options supported here are supported by all commands
7 # however these are:
8 hadoop_add_option "--config dir" "Hadoop config directory"
9 hadoop_add_option "--debug" "turn on shell script debug mode"
10 hadoop_add_option "--help" "usage information"
11
12 while true; do
13 hadoop_debug "hadoop_parse_args: processing $1"
14 case $1 in
15 ....
16 --workers)
17 shift
18 # shellcheck disable=SC2034
19 #【重点】
20 HADOOP_WORKER_MODE=true
21 ((HADOOP_PARSE_COUNTER=HADOOP_PARSE_COUNTER+1))
22 ;;
23 *)
24 break
25 ;;
26 esac
27 done
28
29 hadoop_debug "hadoop_parse: asking caller to skip ${HADOOP_PARSE_COUNTER}"
30 }

  

  bin/hdfs:269

  执行hadoop_common_worker_mode_execute 函数

1 if [[ ${HADOOP_WORKER_MODE} = true ]]; then
2 #【重点】
3 hadoop_common_worker_mode_execute "${HADOOP_HDFS_HOME}/bin/hdfs" "${HADOOP_USER_PARAMS[@]}"
4 exit $?
5 fi

  

  sbin/hadoop-functions.sh:1068

  执行hadoop_connect_to_hosts 函数

1 function hadoop_common_worker_mode_execute
2 {
3 #【重点】
4 hadoop_connect_to_hosts -- "${argv[@]}"
5 }

  

  sbin/hadoop-functions.sh:991

  执行hadoop_connect_to_hosts_without_pdsh 函数

1 function hadoop_connect_to_hosts
2 {
3 ....
4
5 if [[ -e '/usr/bin/pdsh' ]]; then
6 ...
7 else
8 if [[ -z "${HADOOP_WORKER_NAMES}" ]]; then
9 HADOOP_WORKER_NAMES=$(sed 's/#.*$//;/^$/d' "${worker_file}")
10 fi
11 #【重点】
12 hadoop_connect_to_hosts_without_pdsh "${params}"
13 fi
14 }

  

  sbin/hadoop-functions.sh:1047

  执行hadoop_actual_ssh 函数

1 function hadoop_connect_to_hosts_without_pdsh
2 {
3 # shellcheck disable=SC2124
4 local params="$@"
5 local workers=(${HADOOP_WORKER_NAMES})
6 for (( i = 0; i <${#workers[@]}; i++ ))
7 do
8 if (( i != 0 && i % HADOOP_SSH_PARALLEL == 0 )); then
9 wait
10 fi
11 #【重点】
12 hadoop_actual_ssh "${workers[$i]}" ${params} &
13 done
14 wait
15 }

  

  sbin/hadoop-functions.sh:973

  最终的效果是,用ssh执行命令

1 function hadoop_actual_ssh
2 {
3 # we are passing this function to xargs
4 # should get hostname followed by rest of command line
5 local worker=$1
6 shift
7
8 #【重点】
9 ssh ${HADOOP_SSH_OPTS} ${worker} $"${@// /\\ }" 2>&1 | sed "s/^/$worker: /"
10 }

  使用 ssh ${HADOOP_SSH_OPTS} ${worker} $"${@// /\\ }" 2>&1 | sed "s/^/$worker: /" 命令执行的时候,发现依然出现dataNode被杀死的功能,

       但是在salve节点上执行 ${worker} $"${@// /\\ }" 2>&1 | sed "s/^/$worker: /" 命令时,却能正常运行,但是退出当前session后,dataNode马上被杀死

       这时候,我大概知道为什么了,这是因为我的系统是centos7,搭配新版的sshd服务,在新版的sshd中,退出ssh后,默认配置会杀死当前控制组里面的所有子进程,修改策略即可

详细文章可以看 https://www.cnblogs.com/byzgss/p/15573344.html

 


解决方案

  (1)、在 /lib/systemd/system/sshd@.service 配置文件的[Service]下追加KillMode=process

1 [Unit]
2 Description=OpenSSH per-connection server daemon
3 Documentation=man:sshd(8) man:sshd_config(5)
4 Wants=sshd-keygen.service
5 After=sshd-keygen.service
6
7 [Service]
8 EnvirOnmentFile=-/etc/sysconfig/sshd
9 ExecStart=-/usr/sbin/sshd -i $OPTIONS
10 KillMode=process #追加这个配置
11 StandardInput=socket

 

  (2)、重启sshd

 1 systemctl restart sshd.service  

 

 

 

 



推荐阅读
  • 技术日志:深入探讨Spark Streaming与Spark SQL的融合应用
    技术日志:深入探讨Spark Streaming与Spark SQL的融合应用 ... [详细]
  • 传统上,Java 的 String 类一直使用 char 数组来存储字符数据。然而,在 Java 9 及更高版本中,String 类的内部实现改为使用 byte 数组。本文将探讨这一变化的原因及其带来的好处。 ... [详细]
  • 本文总结了Java初学者需要掌握的六大核心知识点,帮助你更好地理解和应用Java编程。无论你是刚刚入门还是希望巩固基础,这些知识点都是必不可少的。 ... [详细]
  • Hadoop的文件操作位于包org.apache.hadoop.fs里面,能够进行新建、删除、修改等操作。比较重要的几个类:(1)Configurati ... [详细]
  • Spring – Bean Life Cycle
    Spring – Bean Life Cycle ... [详细]
  • DAO(Data Access Object)模式是一种用于抽象和封装所有对数据库或其他持久化机制访问的方法,它通过提供一个统一的接口来隐藏底层数据访问的复杂性。 ... [详细]
  • 深入解析 Lifecycle 的实现原理
    本文将详细介绍 Android Jetpack 中 Lifecycle 组件的实现原理,帮助开发者更好地理解和使用 Lifecycle,避免常见的内存泄漏问题。 ... [详细]
  • 本文详细介绍了 PHP 中对象的生命周期、内存管理和魔术方法的使用,包括对象的自动销毁、析构函数的作用以及各种魔术方法的具体应用场景。 ... [详细]
  • HBase Java API 进阶:过滤器详解与应用实例
    本文详细探讨了HBase 1.2.6版本中Java API的高级应用,重点介绍了过滤器的使用方法和实际案例。首先,文章对几种常见的HBase过滤器进行了概述,包括列前缀过滤器(ColumnPrefixFilter)和时间戳过滤器(TimestampsFilter)。此外,还详细讲解了分页过滤器(PageFilter)的实现原理及其在大数据查询中的应用场景。通过具体的代码示例,读者可以更好地理解和掌握这些过滤器的使用技巧,从而提高数据处理的效率和灵活性。 ... [详细]
  • 本文详细介绍了 com.apollographql.apollo.api.internal.Optional 类中的 orNull() 方法,并提供了多个实际代码示例,帮助开发者更好地理解和使用该方法。 ... [详细]
  • 本文详细解析了使用C++实现的键盘输入记录程序的源代码,该程序在Windows应用程序开发中具有很高的实用价值。键盘记录功能不仅在远程控制软件中广泛应用,还为开发者提供了强大的调试和监控工具。通过具体实例,本文深入探讨了C++键盘记录程序的设计与实现,适合需要相关技术的开发者参考。 ... [详细]
  • Spring框架中枚举参数的正确使用方法与技巧
    本文详细阐述了在Spring Boot框架中正确使用枚举参数的方法与技巧,旨在帮助开发者更高效地掌握和应用枚举类型的数据传递,适合对Spring Boot感兴趣的读者深入学习。 ... [详细]
  • 本文详细介绍了HDFS的基础知识及其数据读写机制。首先,文章阐述了HDFS的架构,包括其核心组件及其角色和功能。特别地,对NameNode进行了深入解析,指出其主要负责在内存中存储元数据、目录结构以及文件块的映射关系,并通过持久化方案确保数据的可靠性和高可用性。此外,还探讨了DataNode的角色及其在数据存储和读取过程中的关键作用。 ... [详细]
  • 深入理解Spark框架:RDD核心概念与操作详解
    RDD是Spark框架的核心计算模型,全称为弹性分布式数据集(Resilient Distributed Dataset)。本文详细解析了RDD的基本概念、特性及其在Spark中的关键操作,包括创建、转换和行动操作等,帮助读者深入理解Spark的工作原理和优化策略。通过具体示例和代码片段,进一步阐述了如何高效利用RDD进行大数据处理。 ... [详细]
  • 本文介绍了如何使用Hive分析用户最长连续登录天数的方法。首先对数据进行排序,然后计算相邻日期之间的差值,接着按用户ID分组并累加连续登录天数,最后求出每个用户的最大连续登录天数。此外,还探讨了该方法在其他领域的应用,如股票市场中最大连续涨停天数的分析。 ... [详细]
author-avatar
书友74562696
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有