PYTHON August 24, 2020

从CDH Agent错误日志学习Python拆包语法

Words count 5.2k Reading time 5 mins. Read count 0

国庆七天长假回来上班,迟迟没有进入工作状态,可能祖国的强大,让我不自觉的也飘了起来,哈哈,该收拾收拾心情,为祖国之繁荣而努力加班挣money了。今天通过解决公司开发环境的一个问题,让我瞬间进入了战斗状态,并且顺便学了一下Python拆包的一些语法,还是挺有意思的。

1 CDH Agent错误日志解决

放假回来,同事发现公司开发环境的CDH集群挂了,好长时间没关注了,可能早就挂了吧。挂成什么样了呢?两台节点与Manager失去心跳,所有服务都起不来了,简直废了,处于不可用状态。

当时但看服务状态以为没啥问题,可能是放假期间重启服务器导致的,于是便心不在焉的重启了一下集群,结果可想而知,服务根本起不来。再一看有两台节点已经飘红了,心想问题就出在这了,于是跑到这两台节点上,分别重启了cloudera-scm-agent服务,没想到还是不行。再去/var/log/cloudera-scm-agent/下查看一下agent的日志,发现日志里报错了。

当时没有细看错误信息,看了一句unexpected exception in main loop,心想之前没遇到过这个错误啊,先google一下,没想到还真有类似的问题。https://community.cloudera.com/t5/Support-Questions/ERROR-MESSAGE-This-host-is-not-in-contact-with-the-Host/td-p/52279/page/2

参照网上的说法,在细看一下报错信息,确实是代码中一个地方出错了,原来的写法是对的,但是可能会出现问题,所以就参考网上说的改了一下,重启Agent服务,结果主机正常了。

查看vusr/lib64/cmf/agent/build/env/lib/python2.7/site-packages/cmf-5.11.0-py2.7.egg/cmf/client_configs.py代码446行

def _parse_alternatives(self, name, output):
         """
         Parses output from "update-alternatives --display". Returns a dictionary
         mapping ClientConfigKey to ClientConfigValue.
     
         Alternatives not managed by CM are ignored.
         """
         ret = {}
         for line in output.splitlines():
           if line.startswith("/"):
             path, _, _, priority_str = line.rstrip().split(" ")
     
             # Ignore the alternative if it's not managed by CM.
             if CM_MAGIC_PREFIX not in os.path.basename(path):
               continue
     
             try:
               priority = int(priority_str)
             except ValueError:
               THROTTLED_LOG.info("Failed to parse %s: %s", name, line)
     
             key = ClientConfigKey(name, path)
             value = ClientConfigValue(priority, self._read_generation(path))
             ret[key] = value
     
         return ret

该部分代码如上所示,报错行为 path, _, _, priority_str = line.rstrip().split(“ “),错误信息是

ValueError: too many values to unpack。我猜测可能是line.rstrip().split(“ “)之后的元素个数超过了四个,导致此错误。这个语法很有意思,之前遇到过,但是没有深入研究,先记得这个语法在python里叫做拆包,后面后详细介绍。

既然问题出在这里,那我们简单暴力的参考网上的改法,修改一下,修改之后如下所示:

             # path, _, _, priority_str = line.rstrip().split(" ")
             thisLine = line.rstrip().split(" ")
             path = thisLine[0]
             priority_str = thisLine[-1]

显而易见,只是取第一个元素和最后一个元素即可。

通过这种方式,依次将两台失联的主机都修改了,然后重启了集群,很ok。

2 Python 拆包语法

上面分析到了错误是由于拆包时,元素个数超过预期导致的。但是path, _, _, priority_str = line.rstrip().split(“ “)这种代码写法,确实值得学习,很装逼有没有。于是我来好好学习一下这个写法。

假设,我有一个数组array_1,里面有3个元素10,20,30,我想把这三个元素分别赋值给a,b,c,通常写法是:

array_1 = [10,20,30]
a = array_1[0]
b = array_1[1]
c = array_1[2]

这样没毛病,老铁。但是我们可以通过拆包语法更装逼的写一下:

array_1 = [10,20,30]
a,b,c = array_1

很nice有没有,人家Cloudera团队就是这样写的,但是同样这样写会有一个问题,如果我的数组里有四个元素的话,你这样写是不是会有问题?

>>> array_2 = [10,20,30,40]
>>> a,b,c = array_2
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: too many values to unpack (expected 3)

发现了没有,这个错误有没有很相似,与CDH Agent日志里的错误如出一辙。那么问题来,如果你数组个数不确定,而你又使用这种方式,很容易出错,那我们就没法使用该语法装逼了吗,答案是可以的。

假如,无论数组多长,我们只想取数组的前三个元素赋值给a,b,c,可以这样写:

array_3 = [10,20,30,40,50]
a,b,c,*_ = array_3

那么问题又来了,如果我们只想取第一个,第二个,以及最后一个元素赋值给a,b,c该怎么写呢?

array_3 = [10,20,30,40,50]
a,b,*_,c = array_3

是不是很舒服?我们还可以这样玩,第一个、第二个元素赋值给a,b,剩下所有的元素以list的形式赋值给c

>>> a,b,*c = array_3
>>> type(c)
<class 'list'>

3 总结

接触Python有一段时间了,对于这个语法第一次碰到,还是挺新鲜的。类似的语法在不同的编程语言中都有体现,比如scala里也有类似的写法,叫做提取器,如下所示:

scala> val array1 = Array(10,20,30,40)
array1: Array[Int] = Array(10, 20, 30, 40)

scala> val Array(a,b,c,_*) = array1
a: Int = 10
b: Int = 20
c: Int = 30

scala> val Array(a,b,c @_*) = array1
a: Int = 10
b: Int = 20
c: Seq[Int] = Vector(30, 40)

总的来说,今天通过与老师一起排查问题,到学习新的语法,沉淀了一下浮躁的心情,今天开始步入正轨,好好学习,天天向上。

0%