20161008

猜测在学习新工具中的作用,一个实例

猜测在学习新工具中的作用,一个实例

1.

当年我参加李YL老师组织的Linux内核源码讨论班,大家坐教室里,人手一本厚厚的《Linux内核源代码情景分析》。多厚呢,上下册,单册800页左右。我们都把书摊在桌面上,举起来太累了,听轮值的某个人讲,说我看到哪哪儿了,这一段的意思是啥啥,这段代码引用了某个数据结构,在哪哪页呢,那个人讲,我们就哗哗翻书。

用翻书的办法讨论,因为那是2002年前后,教室里没有计算机,没有笔记本,没有投影仪,也没有平板。

轮到我讲了。我讲了两页,然后提到某某处我猜是怎么怎么回事,假设如此,那么我们继续往下看,某某段的说法就容易解释。在我结束的时候,康凯同学点评,他说: 杨老师你不能这么整啊,咋能"假设"呢,你应该找*到底*是什么回事。我问,在哪找,咱们没机器代码查起来慢啊。他说:在书上找啊。

我拒绝了他的读书方法建议,答复如下。我本科是学电子的,是物理系出身,我所受到的教育就是,先假设,然后用实验事实证实或推翻假设,从我假设以后书上后面的那些段落,都是用来证实或推翻我的假设的依据。

2.

这让我想起书上讲的妈妈给孩子讲睡前故事。妈妈每当讲到关键时刻,就对孩子说,你猜后面会发生什么呢。然后孩子就自发地产生了编故事的能力。书上是这么讲的,我妈没给我讲过睡前故事,所以我也不知道是不是真的。不过,听说过一千零一夜故事,我担心那孩子被悬念勾着更睡不着了吧。

当听到真正的故事时,还会和自己头脑中的对比,"这不合理,杜丘怎么就会忘了装子弹","这个设定太取巧了,谁能想到杀人犯还有个双胞胎"。

我们在听故事的时候,对情节和人物确实是有预设 (猜测) 的。知识不仅来自书本 (!),而且来自猜测。然后我们需要根据猜测设计实验,检验我们的猜测正确与否。如果猜测对了,我们就快捷地得到了新的知识,虽然书上可能没写--书上并未写全你要用到的所有的知识,所以也不要预期可以依照书本生活和工作。

猜测-设计实验验证-使用知识,这是成熟的工程师区别于初学者的一个重要标志,因为工程的一部分正是实验科学。我很多年以后才知道,从培根的实证主义到波普尔的证伪主义,到罗素的对一致性的追求,设计实验的朴素观点背后还有哲学理论的支撑。

不知道这么年过去了,康凯同学对当年的讨论作何感想。他们毕业的时候聚餐叫我去喝酒,刚好没有成行,所以没有来得及问他那时的看法。不过计算机系的同学可能因为缺乏充分的实验,所以一直以来给我这样的印象,重课本而轻实验设计。谷同学和孙同学曾经非常惊讶地对我说:鞠同学怎么可以这么做呢,她根本不知道书上确切地是怎么写的 (当然也不知道知识是不是就是那样) ,就说,那咱们试试吧;一试,行,就说咱们就这么整吧。我当时的评论,这正是鞠同学厉害的地方。

猜测是建模/谓词判断/命题,设计实验是根据逻辑推论--如果猜测正确,那么这样设定实验,结果该当如此,如果结果不如此,那么猜测是错误的。这样,猜测-设计实验验证,以后我们就得到了新的模型/定理,作为工具可以应用于符合条件的场合,用于预测结果,用于改变环境。

老师有的时候提到"学活了"或者"学死了",区别大体指的就应该是这个吧。

3. 一个实例,用rrdtool提取cacti数据

以下的故事,用以展示猜测在整个学习使用新工具流程中的位置。

3.1. 要显示流量,读cacti,是图片,不能 XML parse

需求是这样,要在程序中显示校园网各汇聚层交换机的流量 (上行和下行)。你可以想像客户站在大屏幕前,指着天气图说,你看就是这条线上的数据,要显示在这里这里和这里。

(天气图)

天气图是cacti展示出来的,cacti是网络流量监控的图形化工具,配置以后通过cacti已经可以查看各汇取层交换机的流量数据了,点击线路,就可以看到。

(流量图)

凡能看到的,就能抽取。所以主力研发郑同学就说,那我先研究一下 java httpclient 吧。我否定了这个计划。我否定的并非使用 httpclient,而是投入时间去尝试学习研究,而是先假设这一部分不会有问题。httpclient是著名的apache的项目,而且濮同学早就用过,所以是确定地技术路线,没有风险。反倒是另一件事情有高风险,并非"凡能看到的,就能抽取"。httpclient能模仿浏览器的各种行为 (js解释呢?),但是 xml parser 部分 图片是不可以"抽取"成文字的。只要没有变成文字,那么就不能放心,客户可能打算换字体字型字号呢。万一天气图里的数据是图片,httpclient这条技术路线整体需要放弃。

天气图里的数据是图片。

而且从图片的 caption 和 URL 里得不到文字。

故事的第一幕就此结束。英雄遇到灾难,离开故乡,开始历险。

3.2 cacti数据可以用rrdtool读取,UNIX时间戳,zhumao同学说

我和向龙同学分别对着自己的显示器,显示器后面很多公里线路以后是郑同学,我们都愁眉苦脸。zhumao同学催,啥时候能去吃午饭呐。我敲键盘,"要完了啊。"

zhumao同学远程说,cacti的数据都在数据库里,可以抽出来。

峰回路转,午饭可以下咽了。

下午,我和向龙同学研究从哪个"数据库"里抽数据。向龙介绍cacti的数据库配置的是mysql,然后我俩把mysql数据库翻个遍,各个表,各个字段。从尺寸上看就不会容纳那么多日志。

但是zhumao同学技术上很可靠,应该不是虚言。所以我们判定,"数据库"一词不是指关系型数据库,而是泛指能存信息的容器。

zhumao同学远程说,抽取的工具是rrdtool。过了一会又说,时间节点用的是UNXI的1970年时间戳。

希望重新燃起,我看到大半夜的微信上向龙同学和郑同学进行各种尝试。

3.3 猜不到数据在哪里

我以为故事快结束了,但是向龙同学和郑同学说,"猜不到数据在哪里"。

"不是说用rrdtool么,zhumao用过啊。"

"zhumao只是说可以用rrdtool,他并没有声称自己用过。"

郑同学已经把rrdtool的语法研究得八九不离十了,就是抽出来的数据和天气图上显示的对不上号。

故事一般都是这个路线,一到关键时刻--钥匙就丢了,计算机刚好没电,掌握密码的人被打死了,主角的最主要助手突然叛变,"其实我是卧底"。为什么到关键时刻才会这样,之前一点也不。因为关键时刻是这么定义的,出现了困难的时候,如果不能克服,那就是完蛋。而在实验和工程中,每一个困难如果不能克服或者绕过去,那么就会完蛋,所以有N多关键时刻。比看警匪片和侦探故事还刺激。

4. graph properties 可以查看源代码 rrdtool graph

向龙同学提供我授权登录cacti (天气图及设置) 和 ssh到rrdtool工具所在操作系统,更关键地,他随时提供给我现场的部署、状态,比如天气图上的线路和rrdtool数据库的对应关系,他和郑同学尝试过的指令和结果。

长弓已经架好,重装步兵阵列在前,巨龙就在前方洞穴之中,只差最后一击。当然,这一击以后发现还要再出击N次也是常有的事,不过如果这次失手了,那就是最后一击,然后我们全军覆没。

4.1. 猜测及实验验证

(流量图)

向龙给我展示的是这张图。整张chart以及图例和注释全都是一整张图片,PNG格式。我确认了我看到的和战友们所看到的相同。天气图是一张图片,我在郑同学这样声称以后也确认过一次。

猜测: 这张图是此刻实时生成的,是从"数据库"里读出来的,不是原来在磁盘中的。

否定: 向龙指出,zhumao在磁盘确实找到过这张图片。 (赞叹zhumao的反向工程能力)

再猜测: 磁盘里的这张图片也应该是生成的,用rrdtool工具从"数据库"里读出来,然后生成的。

猜测的依据: rrdtool 是 round robin database,文本或压缩文本的存储效率要比图片高很多。理智的设计不会存储图片。

鼠标在图示右上角的小扳手上悬停,显示tips为 properties,左键单击,是这个界面。

(rrdtool graph)

长得真是如我期待,上面是代码,下面是代码生成的图。

那段代码是:

----代码开始----
/usr/bin/rrdtool graph - \
--imgformat=PNG \
--start='1474880074' \
--end='1474880360' \
--title='H3C12508 - Traffic - Ten-GigabitEthe' \
--rigid \
--base='1000' \
--height='120' \
--width='500' \
--alt-autoscale-max \
--lower-limit='0' \
COMMENT:"From 2016/09/26 16\:54\:34 To 2016/09/26 16\:59\:20\c" \
COMMENT:"  \n" \
--vertical-label='bits per second' \
--slope-mode \
--font TITLE:10: \
--font AXIS:7: \
--font LEGEND:8: \
--font UNIT:7: \
DEF:a='/var/www/html/cacti/rra/h3c12508_traffic_in_190.rrd':'traffic_in':AVERAGE \
DEF:b='/var/www/html/cacti/rra/h3c12508_traffic_in_190.rrd':'traffic_out':AVERAGE \
CDEF:cdefa='a,8,*' \
CDEF:cdefe='b,8,*' \
AREA:cdefa#00CF00FF:'Inbound'  \
GPRINT:cdefa:LAST:' Current\:%8.2lf%s'  \
GPRINT:cdefa:AVERAGE:'Average\:%8.2lf%s'  \
GPRINT:cdefa:MAX:'Maximum\:%8.2lf%s\n'  \
LINE1:cdefe#002A97FF:'Outbound'  \
GPRINT:cdefe:LAST:'Current\:%8.2lf%s'  \
GPRINT:cdefe:AVERAGE:'Average\:%8.2lf%s'  \
GPRINT:cdefe:MAX:'Maximum\:%8.2lf%s'
----代码结束----

"/usr/bin/rrdtool",这是一段命令行。

ssh到服务器,向龙告诉我去哪里可以找到 rrdtool 和 数据库文件。我跑了一遍上述代码,输出一个png文件到文件系统中,正是我想期待的。

故事发展到这里,我们知道,"/usr/bin/rrdtool graph"及正确的参数可以从数据库中抽取数据。只是抽取得到的形式还不尽人意,我们想要文本,得到的是图片。

就此验证zhumao的猜测都是正确的。

事实上,在另一条分支上,向龙和郑同学走得更远。他们已经得到了纯文本的结果,用的指令是"rrdtool fetch",只是抽得的结果还没有与天气图对应上。"rrdtool graph"直接证实了zhumao的猜测是正确的,抽取数据可行。

4.2 手册

运维和编程的著名格言: RTFM -- Read The Fxxking Manual.

我找到官方站点,[https://oss.oetiker.ch/rrdtool/doc/index.en.html],去读手册。此前郑同学也已经独立到过这里。

然后我根据 rrdtool graph 手册解释了刚刚那段代码里的这一小段

CDEF:cdefa='a,8,*

其含义中的重点是: 乘以8. 因为数据库里以字节为单位的,输出的图形是比特为单位的。

我执行了下面这段
----代码开始----
$ rrdtool fetch h3c12508_traffic_in_190.rrd \
> --start='1474880074' \
> --end='1474880360' \
> AVERAGE
                     traffic_in         traffic_out

1474880100: 1.9393791780e+08 4.9525977422e+08
1474880400: 1.9499877230e+08 4.8637343825e+08
----代码结束----

然后手动 (用 windows calc)计算

traffic_in :
1.9393791780e+08 * 8/1000000000 = 1.5515033424
1.9499877230e+08 * 8/1000000000 = 1.5599901784
平均值为 (1.5515033424+1.5599901784)/2 = 1.5557467604
作为对比,图示中的数据 Inbound 为 1.56G.

traffic_out :
4.9525977422e+08*8/1000000000 = 3.96207819376
4.8637343825e+08*8/1000000000 = 3.890987506
平均值为(3.96207819376+3.890987506)/2 = 3.92653284988
作为对比,图示中的数据 Outbound 为 3.93G.

抽取出的数据与图示吻合了。

4.3 放大图示,对比数据,向龙同学说

我展示方法,向龙同学说,我乘以8了啊。我问你从哪边往哪边乘的。他说,因为不确定到底如何,做了不少猜测,从两边分别都乘过。这是本故事的另一个寓意了,猜测的时候如果知道那样一定正确,尽可能减少其他可能,就更好。不就此展开。

事实上,这个实验的最后一击并不那么简单。向龙同学提供了对比数据的spec说明: 放大图示到某个具体的分钟,抽取的数据与这个具体的分钟的数据对比,避免在时间段内平均等造成的误差 (和复杂性)。同时,向龙同学提供了放大图示的手段,郑同学此前不知道放大的方法,我没有想到选取具体的时间节点。

4.4 时间转换,双向;新的关键环节:时区

有的同学可能已经注意到我上面引用的代码中的时间是UNIX时间戳,这是zhumao提示的。具体使用的时候,我的时间戳来自两个来源,(1) 从 rrdtool graph 的代码中抄,(2) 利用在线工具 [http://www.epochconverter.com/] 双向转换。

(图)

我这样找到在线工具的, bing: unix time stamp convert,然后右上角"Switch to Bing in English"。第一页前几个就很好使。

(3)命令行工具,Linux内置

----代码开始----
$ date --date='2016-09-26 17:02 CST' +%s
1474880520

$ date --date @1474880520
Mon Sep 26 17:02:00 CST 2016
----代码结束----

(4) 等实验完成了我才注意到,rrdtool fetch 接受一般的时间格式,还有倒退几分钟这样的功能。可以不用UNIX时间戳的。

在转换时间格式时我注意到有时区,所以为了避免可能的问题,确认了一下操作系统本地时区是东八区,北京时间。


----代码开始----
$ date -R
Mon, 26 Sep 2016 17:36:37
+0800

$ date +%z
+0800
$ date +%Z
CST
----代码结束----

5. 加强、改进,玩

大局已定,我们对手册里的其他工具做了些尝试。与其说是加强和改进实验结果,不如说像猫抓到老鼠以后行为,"总算抓到了,快让我玩会。"我在程序跑对了以后,也总喜欢再多跑两次,好像生怕它会跑出不同的结果,或者多享受几次正确地运行。

5.1 用命令行过滤数据

为了后续开发方便,我用命令行去除了数据中无用的部分。

----代码开始----
$ rrdtool fetch h3c12508_traffic_in_190.rrd -s -5min AVERAGE | tail -2 | head -1
1474879500: 1.9791172526e+08 5.0410319243e+08
----代码结束----

其中的 -s -5min 指数据截取的开始时间为从此刻回退5分钟,结束时间为此刻。

5.2. 查手册,得到解释及更丰富的语法

还有导出为xml格式

----代码开始----
$ rrdtool xport \
> --start='1474595681' \
> --end='1474595683' \
> DEF:a=h3c12508_traffic_in_190.rrd:traffic_in:AVERAGE \
> CDEF:cdefa='a,8,*' \
> XPORT:cdefa:"IN"
<?xml version="1.0" encoding="ISO-8859-1"?>

<xport>
  <meta>
    <start>1474596000</start>
    <step>1800</step>
    <end>1474596000</end>
    <rows>1</rows>
    <columns>1</columns>
    <legend>
      <entry>IN</entry>
    </legend>
  </meta>
  <data>
    <row><t>1474596000</t><v>9.0713941203e+08</v></row>
  </data>
</xport>
----代码结束----


导出为json格式

----代码开始----
$ rrdtool xport \
> --start='1474595681' \
> --end='1474595683' \
> --json \
> DEF:a=h3c12508_traffic_in_190.rrd:traffic_in:AVERAGE \
> CDEF:cdefa='a,8,*' \
> XPORT:cdefa:"IN"
{ about: 'RRDtool xport JSON output',
  meta: {
    start: 1474596000,
    step: 1800,
    end: 1474596000,
    legend: [
      'IN'
          ]
     },
  data: [
    [ 9.0713941203e+08  ]
  ]
}
----代码结束----

5.3. 后备方法 rrdcgi, librrd, rrddump

我们还知道些后备的方法,在当前项目中不会用到。

估计可以用于http输出的rrdcgi, 估计可以与其他语言 (C?)联合开发用的librrd,估计可以把数据库倒出来的工具rrddump。

6. 总结

猜测在学习新工具中的作用如此重要,以至于如果你没有猜测的话,就不要按下回车。哪怕你有很多种猜测也好,只要列出一张表来,一个个试过去,留下看起来最正确的那个。但是如果你没有猜测,就失去了主动性,而是被动地观察实验现象,却对支配实验现象背后的物理图景毫无察觉。

据说,李靖与李世民讨论兵法,他说: 千章万句,不出乎"致人而不致于人"而已。

------------------------------------------------------------

博客会手工同步到以下地址:

[http://zhuanlan.zhihu.com/younggift]

[https://younggift.net/] 悄然支持http2,再次感谢高博先生

[http://blog.csdn.net/younggift]

[http://giftdotyoung.blogspot.com]