2012年3月30日星期五

分享一个很久以前的前公司的例子

俺过去曾经啥都干过,包括开发、排查Bug、数据库部署维护、服务器采购、机房迁移,乃至公司的电话机布线和程控机的编程……好吧,事情是杂了些,但不代表不专业。

不过销售为主导的公司,一定会发生一件事情,就是在技术上销售比技术还要牛叉。在以前的公司就曾经有这么一件事情发生在我身上:

某天开始,服务器每天都会时不时的速度很慢,然后就突然无法访问。该问题长时间排查都找不到原因,现象倒是挺明确的:不存在数据库连接泄露,某种偶然情况下Web进程内存占用会突然缓慢持续增加,当到达接近1GB时会突然崩溃,日志显示内存不足。

这个现象很不好理解,因为:

  1. 并非必然发生,而是阵发性的;
  2. 网络带宽只占用了30%-50%左右;
  3. 内存并没有占用满32位操作系统的用户内存空间上限,甚至连一半都没有用到,怎么就内存不足呢?
  4. 代码拉网筛查,以及跑profile也没有发现任何内存泄漏;
  5. 按道理来说,.NET托管内存是不可能存在泄漏的,而Web应用很少会有大量对象长期生存,也不可能占用内存至内存不足。

无论原因如何,也不可能是网络带宽的原因。可是搞笑的是,销售的一个头头居然大发雷霆,发邮件短信说技术反应迟钝不作为,这么久都不增加带宽。当时真是非常无语,尽管之前已经通报过情况,也说明不可能是带宽的问题。但既然已经惊动老总,只好按照其要求将带宽翻倍。可想而知,该现象依然如故,于是就没话可说了。

此问题非常棘手的主要原因是现象1,导致你只能一直远程并盯着内存量,当发现开始增长并持续一段时间之后,就要果断做Dump。可是,有可能你正好手头有事或者谁跟你说一件事,这个现象就错过了;又或者做Dump太晚了,导致CPU已经开始大量占用无法启动Dump程序;又或者Dump了半天快结束的时候进程被IIS自动Kill掉了;又或者Dump的太早什么都没有Dump出来。

经过很长时间的联系,终于熟练的抓到了一个Dump,结果发现实际内存占用量非常少,大约只有300M左右,但是内存提交量却达到了2G。最后实在没辙了翻查GCHeap,结果找到少量极其巨大的string对象,比如20MB。这些string对象指向了一个位置:日志。原来在系统中有一个数据库表的字段,是用来存放最后一次某些特地操作的。按照设计,这个字段应该只保留最后一次,但实际情况是该字段不知道为何被改成一直累加。于是乎,一些频繁访问的用户就会累积出这种巨大日志记录。只有当这些用户频繁访问的时候,才会造成系统崩溃。这很好的解释了前面所说的5个现象,尤其是阵发性。

也许有人会问了,那GC不应该回收么?对,GC会回收,但是GC回收会有一个副作用,就是CPU占用会变高,导致系统变得很慢。此外,还有一个无法在32位操作系统中很不好解决的问题:由于.NET设计上将大于85kb的对象放在一个特殊的Large Object Heap(LOH),而这个Heap和普通的GCHeap有一个明显的不同,就是GC发生时并不会进行对象拷贝(也就是碎片整理)。最后,连续的内存地址将会全部碎片化,此时如果需要分配大对象的时候,将会再也找不到任何连续地址用于存放此对象。虽然所有空余空间加起来远远超过几百兆,你也可能无法分配一个30兆的内存空间出来。于是才有了明明内存占用很少,却报告内存不足。

你看,这个问题挺技术的吧。可偏偏有那种什么也不懂的人,跑出来很技术的要求增加网络带宽。从这个事情上,我明白到合作的事情,你必须相信你的伙伴在他的领域内是专业的,不要傻了吧唧的在那里指手画脚。如果你不信任,那你就应该立即换人,并同时承担看错人的成本(包括新人和旧人)。否则,指手画脚除了显示出你的不专业,搞坏团队气氛之外,不能带来什么好处。除非,你认为以飞机失事的概率成功解决一件事情能算是成功。

哦,对了,这种事情从来不会停止发生。比如说上面说的那个销售老大技术真是完全不懂,否则根据我在通告中说明的“内存不足”异常,可以在带宽倍增无效之后,继续要求给服务器增加1TB的内存,然后再增加100个CPU和600个网卡。最后实在不行了,还可以赖.NET不如Java先进,要求更换至Java平台然后使用Oracle数据库。至于操作系统嘛,可以换成EXASOL EXACluster OS……

Linux撞鬼集4之IFS

前阵子自建CDN,因为有攻击导致某地点某IP不可用。在屏蔽攻击后重新需要给用户重新分配IP地址,但是分配之后一直都没有成功。通过查看配置文件,我们确实配置成新的IP地址了,并且确实已经“成功”的重新启动bind服务。但是用户持续报告不可访问,我们直接找ip地址发现实际上仍然是旧的。

一开始我们怀疑可能是不是TTL错误,或者什么别的原因导致用户访问的DNS服务器没有刷新新的解析记录。经过一阵紧张的nslookup和dig之后,还是没有能成功找出问题所在。最后修改named.conf当中的日志级别为debug,重新启动之后发现实际上有一个区域的解析记录并没有成功加载。日志大概意思如下:

unknown type "xxx.xxx.xxx" for record NS (大概这意思吧)

打开配置仔细一看,原来该区域下面应该有一个NS解析记录,原本应该是:

            NS       xxx.xxx.xxx

结果变成了:

NS xxx.xxx.xxx

这一条解析的意思是,当前要解析的域下面的域名服务器是xxx.xxx.xxx。但是由于前面的空格去掉了,结果按照空格划分,第一个表示二级域名的字段从空变成了NS,而记录类型则从NS变成了"xxx.xxx.xxx",而第三个表示解析结果的字段则从"xxx.xxx.xxx"变成了空。于是这一个域名解析记录加载失败,这也导致了后面更改了的解析记录实际上从未被正确加载。之所以域名服务器仍然返回旧的IP,是因为过期时间比较长,在得不到我们的正确解析时,其它的DNS服务器将沿用原来已经有的域名解析结果。

那么,这个空格为什么消失了呢?如果是某个人手动修改的,其手动意图又是啥?经过调查之后发现,原来有人写了一个修改解析地址的脚本,正是这个脚本造成的问题。而之前就已经发现有此问题,解决的办法是通过运行脚本之后手动添加空格来解决,而这一次却忘了。

具体造成问题的脚本命令是read,这个命令十分神奇,会自动根据IFS来分割字段,然后将字段重新组合成一个字符串。用C#来描述,就是:
return string.Join(IFS[0], Console.ReadLine().Split(IFS, StringSplitOptions.RemoveEmptyEntries));

解决的办法很简单,就是将脚本改写成:

while IFS=""; read LINE
do
#  something
done < afile

关于IFS在while上面赋值,我想了一下,原因应该是这样的:
如果在独立一行修改,则导致从此之后的运行环境发生改变,会影响并改变后续所有命令的默认行为。而如果在while上面赋值,则因为是folk一个子进程,并且在子进程中运行修改的缘故,并不会导致外部(当前脚本)环境的变化,也不会改变后续命令的默认行为。

这种环境变量影响程序行为的问题,在Windows算比较难以注意到的。一般也就是PATH之类的变量会导致能否找到可执行程序,但很少会影响程序本身正常行为的。对于read来说,默认按照IFS分割并重组输入,确实让初学者难以注意到。

一句话

人生就是一砣砣屎等着让你吃,你要么找别人替你,要么就在吃下一口之前赶紧大喘气。

都掉钱眼里了,就别跟我谈什么理想梦想的。

不要将自己的无能怪罪到别人头上,尤其别人是在帮你的时候。

在说别人没有XX的时候,最好先看看自己有没有。(XX=卵、智商、情商、能力……)

话不要随便乱说,因为朋友的敌人的敌人,可能同时是你的敌人和你朋友的朋友。

沟通是倾听,是从别人的视角看问题。

凡是使用外部归因法的,都是懦夫、侏儒、智障。

有问题的永远是我自己,别人永远都是没有问题的,这样才能做好事情,会做人。

2012年3月29日星期四

技术宅分析养老金

据说是郎咸平的文,至于是不是,我没有考究,我关心的是里面数字的计算:
继续工作20年,退休活命20年,当前平均工资4000,退休后保持生活质量需要243万,实际养老金只有37万。

首先说我的结论,那就是赶紧移民吧。且不管里面别的地方是否正确,但是有那么几点是一定正确的:

  1. 公务员退休保障是普通人的好几倍,已经形成公务员阶级和屁民阶级。未来某个点上面肯定要达到势不两立的地步;
  2. 最低收入者需要交的保险比例比寻常人高也是不争的事实(北京貌似按照1600的最低基数交,就算你只拿1200的工资);
  3. 各类社保亏空是不争的事实,未来一定会通过增发货币进行账面名义值弥补,但一定会引起持续的高通胀,乃至超高通胀(乃至引发爆点);
  4. 各种不明不白是一定的:比如你移民,公司交的那部分就没了;跨地区也是被截留一部分;
  5. 公积金账户年利率似乎只有0.36%这种级别,至少不是一年定期的利率。可是大家都听说过社保基金入市吧?那么用我们的钱赚出来的利润怎么就被侵吞了呢?如果亏了呢,算谁的过错,最后又是谁来买单?(答案:没人负责,我们通过货币增发补充账面亏空-〉通货膨胀-〉货币实际价值下降-〉生活质量下降的路径来买单。)

实际上我一直认为,如果这些钱如果发到每一个人的手上,全部存1年定期,也比放在政府账户要有更高的保障。如果政府真心为我们好,那就不要交什么社保费,直接动用税收给我们发钱就好了。当然,如果是每个人一个独立账户,清清楚楚,那也可以考虑。

之所以现在大面上大家都没有反应,那是因为传统文化中的“国〉家〉个人”的部分作怪,个人权利意识从未真正觉醒。尤其对于底层的群众来说,经常不自觉认同可以为了大家的利益牺牲个人利益这种扭曲的观念,只有当被强拆的时候才会发现周围的人是那么的冷漠。如果放在西方,这种社保制度早就上街抗议了,因为个人利益受到了极大的损害,而且不明不白。

扯远了,咱还是扯数字。上面那几个数字在其假设上面是正确的,计算方式如下:
令G = 4000, k = 1.03, 则有
G20 = G*(k^20) = 7224.45 (20年后的月工资)

因为每年增加3%这个数字比较小,可以认为近似线性增加,于是20年工资总和为(略小于):
T20 = (G+G20) / 2 * 20(年) * 12 (月) = 1,346,933.39

令p = 8%+20% = 28%,则有实际保险名义值 J20 = T20 * 28% = 377,131.35,约38万。

20年后至40年后这段时间,如果每个月都需要现今的4000元,则此期间名义货币值总需求量是T20_40 = (G40+G20) / 2 * 20 * 12,其中
G40 = G * (k^40) = 13048.15,因此
T20_40 = 2,432,711.53,约243万。

从计算结果看基本吻合,但是实际上这里面有几个比较大的漏洞:

  1. 大部分人将工作38年,而不是20年;
  2. 现在开始工作的人,其人均寿命是68岁;(注意,2012年的人均寿命是指预期2012年出生的这群人的平均寿命,而不是这一年死亡人口的平均寿命。)


也就是说,今年刚工作的本科毕业小鬼,将会干38年的活,然后活8年。于是:
G38 = G*(k^38) = 12,299.13
G46 = G*(k^46) = 15,580.17
T38 = (G+G38) / 2 * 38 * 12 = 4,451,375.98
J38 = T38 * 28% = 1,246,385.27,约124万。
T38_46 = (G38+G46) / 2 * 8 * 12 = 1,338,206.82,约134万。
也就是说,账面缺口大约10万。于是差距已经小了很多,此外考虑实际账户资金是存在一定的利率的,比如存1年定期,这10万的差距基本上是可以找回来的。

呃,如果这样的话,为什么还要考虑移民呢?因为:
如果你不幸寿命没有达到60岁,那么……
目前有一大批的退休人口,实际上在过去是1分钱的保费都没有交。或者更准确的讲,是那些计划经济时代的老一辈,政府就没有专门的账户和预算来应对养老的事情,基本上算是比糊涂账。这部分的内容才是目前账面亏空的根本原因;
还是那句话,不清不楚,随便侵吞个人权益。于是你贡献的时间越长,损失可能就越惨重。比如说,未来如果出一个规定,说必须回原籍才能拿养老保险……

当然,如果你维权,就会遇到一个强大的政府,完全不按照法律行事。比如我就听过这样的对话:
屁民:按规定应该可以办的。
领导:(一拍桌子)规定!我打开这个抽屉里面的文件有一个规定,打开另一个抽屉里面的文件又有另外一个规定,我想要什么样的规定还不简单!

我国立法形式基本上就是形式,完全无视宪法,宪法也只能代表装精神一下。比如没有宪法法庭,也没有违宪救济途径。所以你既可以说有法有天,也可以说无法无天。


几个简单常用的Linux命令——日志分析篇


同事说能否分享一下linux日志分析的心得,尽管我也只是初学小菜,不过既然问到了,那就献丑了。

linux里面有那么几个常用的命令,在大部分日志分析场景里面都会用到:

cut  按照特定分割符分成若干列,取出其中的某一列。
     -d" " 表示用空格作为分割符默认好像是逗号;
     -f8   表示取出第8列。

sort 按照特定的分隔符分成若干列,按照某一列进行排序。
     -d" " 表示用空格(默认应该就是空格),-f1,2表示用第1、2列输出;
     -g    表示按照数字进行排序;
     -r    表示逆序;(默认从小到大);
     -k8   表示根据第8个字段进行排序;(默认只根据第1列排序,可以通过设置IFS来指定分隔符)
     -k1,3 表示根据第1至3个字段之间的内容进行排序;
     -u    表示相同的元素只输出一次。

uniq 如果下一行重复则消除。
     -c    表示统计出现次数(重复一次计数为2)。

head 输出最开始的几行。
     -n8   表示输出头8行。(无参数时默认10行)

tail 输出最末尾的几行。
     -n8   表示输出尾8行。(无参数时默认10行)

wc   输出指定文字出现的次数。
     -l    表示输出出现该文字的行数而不是次数。

grep 输出包含指定文字的若干行。
     -i    表示大小写不敏感;
     -e    表示使用正则表达式;(等同于egrep命令)
     -v    表示输出不等于制定文字的若干行;
     -m8   表示指定文件中的输出前8个;
     -a    表示如果是二进制文件,则仅对比里面是文字的部分;(比如要找squid缓存文件当中记录的,与该文件对应的uri时就特别有用);
     -r    表示递归搜索所有文件夹中的指定文件;
     -H    在每一行输出中均包括文件名;(搜索单个文件时默认不输出)
     -h    在每一行输出中均不包含文件名;(搜索多个文件时默认输出)
     -l    仅输出有命中的文件名;
     -L    仅输出没有命中的文件名;
     -B8   输出命中该行之前的8行;
     -A8   输出命中该行之后的8行;
     -c    仅输出命中次数。

上面这个命令组合通常就可以进行大部分的日志分析工作,下面举例说明。

我们假设日志名称为squid.log,格式如下所示(有点像Squid,但实际上不是真实的日志格式,只是为了便于说明):
- - [2012/1/1 04:23:44] 223.33.54.123 "GET /pages/head.htm HTTP/1.1" {www.fix.com|http://www.google.com/search} 200 - - TCP_HIT:NONE "Mozilla/4.0"

例1 查看最后若干个访问"/pages/head.htm"的IP都来自哪里:
grep -i "/pages/head.htm" squid.log | tail -n20 | cut -d" " -f5
注意:

  1. 日期和时间当中有一个空格,因此是两个字段,而不会因为方括号而认为是一个字段。同样的,单引号双引号也不会影响以空格为分割符的分割;
  2. 如果文件非常大导致明显非常慢,可以尝试用tail作为开头进一步限制,如:

tail -n1000 squid.log | grep -i "/pages/head.htm" | tail -n20 | cut -d" " -f5



例2 统计最后若干个访问"/pages/head.htm"的IP的次数:
grep -i "/pages/head.htm" squid.log | tail -n1000 | cut -d" " -f5 | sort | uniq -c | sort -g
备注:这里的技巧是,先要sort,然后再通过uniq -c来统计次数,然后再通过sort来进行排序。这是因为对于uniq命令来说,之比较上一行和下一行,而不会考虑交错的情况,例如对于文件tmp如下:
A
A
B
B
A
A
C

用uniq -c tmp来检查,输出如下:
2 A
2 B
2 A
1 C

而实际上我们希望的输出类似如下:
1 B
2 C
4 A

于是只能够通过sort先将相同的内容放到一起,然后再用uniq来统计。sort命令当中的-u参数并不会统计处数量,于是必须使用uniq命令。同时uniq输出的顺序是原始文本出现的顺序,而不是根据数量进行排序的,因此最后还需要用sort进行排序。


例3 统计最后若干个访问"/pages/head.htm"的IP中,出现次数最多的前三名以及其次数:
grep -i "/pages/head.htm" squid.log | tail -n1000 | cut -d" " -f5 | sort | uniq -c | sort -gr | head -n3



例4 统计后端返回状态为503的访问次数:
cut -d" " -f10 "/pages/head.htm" squid.log | grep 503 | wc -l


例5 统计后端返回状态为200,且访问"/pages/head.htm"的HIT和MISS的情况(日志中TCP_HIT:NONE的那一列)
这一个需求比较复杂,我们需要动用上面没有介绍到的另一个命令:awk

awk  对每一行按照指定分隔符进行分割,并根据指定的条件输出指定的内容。
     -F" " 表示输出以空格作为分割符。

完整命令格式如下:

awk [条件] ['指令'] [待分析文件]
之所以指令是可选的,是因为参数中还有一个可以指定指令文件的参数;而待分析文件可选,是因为可以通过管道提供待分析文件。

其中的指令部分比较复杂,完整的这里就不介绍了,请自行谷歌之。这里介绍非常简单的几个常用部分。
$1       表示第一个字段
$1>2     表示第一个字段是数字且大于数字2
$1>"2"   表示第一个字段大于字符2,也就是说"12"<"2"
$1==$2   表示第一个字段等于第二个字段
&&       表示与
||       表示或
a { b }  表示如果前面的a部分匹配,则执行b部分
print    表示输出整行
print $1 表示输出第一个字段
;        表示两个指令之间的分割

于是
$1==200 { print; }
就是说第一列等于200就输出整行。

对于本示例,将需要使用如下命令:
awk -F" " '$10==200 { print; }' squid.log | grep -i "/pages/head.htm" | cut -d" " -f13 | sort | uniq -c | sort -g


例5 统计UserAgent的情况,但前面的访问路径处可能包含不定数量的空格,比如某些访问没有发出HTTP/1.1,于是只有"GET /pages/head.htm"。


这个问题很容易想到:啊,如果能够忽略双引号中的空格多好啊!嗯,这种思路应该也是可以的,但是比起下面这种思路来说,有的时候真的太复杂了。
cut -d\" -f4 squid.log | sort | uniq -c | sort -g

“……靠,还能用双引号做分割符,咋就没想到呢?”好吧,再提示一下:每一个命令可以考虑使用不同的分割符,而不需要每一次都用同一个分割符。

2012年3月28日星期三

Linux撞鬼集3之$()内嵌加管道(尚未解决)

由于自己比较菜,写linux脚本的时候总遇到一些很撞鬼的事情,比如说:
$(echo "echo hello")

这个的执行结果是:
hello

但是执行下面这个命令:
$(echo "echo hello | more")

结果你以为仍然一样,结果却是:
hello | more

嗯?为啥是这样的呢?抱歉,我不知道为什么。从我目前的认知来说,我以为$(xxx)的意思是执行xxx命令,并且再次执行该命令的输出。用第一个例子来描述,那就是:
echo "echo hello"

将会输出:
echo hello

而执行 echo hello 的结果,将会是:
hello

也就正好可以解释第一个例子的执行结果。可是到了第二个例子,我们推测
echo "echo hello | more"

的执行结果是:
echo hello | more

而上述语句的执行结果应该是
hello

这是因为该输出只有一行,more不会起任何影响。但是实际上第二个例子的结果却等同于:
echo "hello | more"

嗯,这就奇怪了,bash怎么会如此识别呢?如果说是自动根据IFS来添加双引号/单引号,那也应该是等价于下面这样的命令才对。
echo "hello" | "more"

好吧,我假定$()操作符将会根据输出结果按照IFS分割成两列,第一列作为命令,剩下的都作为参数,这样倒是能解释结果。可是,我要如何写才能得到等价于hello | more的执行结果呢,或者说达到这一个目的呢?

……呃,这个,非常抱歉,google这个玩意儿对于学习linux脚本编程天然鄙视。比如搜索“$()”,结果是木有啊木有,更不要说搜索“$() |”这样的玩意儿了……(泪奔ing)

P.S.:
$(echo "echo hello" | more)这个是可以的,但是这两者其实并不等价。比如说有一个有3行文字的文件a,执行cat a | head -n 1,现实的是一行。但是执行$(echo "cat a" | head -n1),却把所有文字都显示出来了。为啥?不知道,GOOGLE不告诉我……

2012年3月18日星期日

出逃

我们说投资要跟着巴菲特,就是因为这些人通常见多识广,见解可能更加正确。照这么说,只要有可能,学习他们的生活方式也是应该的。因为通常来说,他们的生活方式可以让他们比我们活的更有质量、更加长寿,后代更有可能培养得更好。

今天看到一个新闻,说,可投资净资产1000万以上的中国人“14%的人已经移民海外,另有46%的富人正在计划或者办理移民的过程中。等到后者也完成移民手续,那么,每5个有钱的中国人中,将有3个是外国国籍。”而“在2万名拥有可投资资产超过1亿元的中国富人中,27%的人已经移民,另有47%的人正在考虑移民。超级富人高达74%的移民比例,真是一个惊人的世界纪录,恐怕连俄罗斯或者动荡不安的中东国家也望尘莫及。”

呃,这个估计跟法73条这类的问题有莫大的关系。尽管越往高处的人,这类感受越深刻。但也不代表像我们这样的人没有感受,比如我就去拿AU的PR了(老早的事情了)。但当时考虑到一些私人问题,并没有立即成行。这么几年下来,几乎都已经快不怎么注意与此有关的事情了。除了偶尔获知现在申请已经比当年难上许多,审批的速度也不比当年神速的时候,会暗自庆幸一把。

但是最近抽筋了解了一下情况,发现原来说小孩的PR申请最多2个月就下来了,现在变成了14个月。原来2周能下来的牛奶金,现在有人居然4周都没有下来。由此可见近期到达AU的人数真的变多不少。至于这是否华人的功劳,那就不清楚了。但可以大胆猜想,申请小孩PR的人这么多,说明去那边的人真的多了很多,估计AU的房价大概就跌不下来了至少。甚至如果说涨价了,那也没有什么好奇怪的。

原本AU就已经是发达国家中生活成本非常高的国家之一,现在这么搞法想想头皮有点发麻。但一想到国内吃喝玩乐坐火车呼吸空气皆不安全(尤其是对小孩来说),再加上如果在网上不小心表达了这种不满,还不知道会不会被消失家人却不知道,就觉得还是这一坨狗屎更恶心人。于是走人的想法还是那么的坚决,尽管很不情愿。

是啊,如果能够有免于恐惧的自由,不用害怕吃错东西、喝错水、看错书、上错学、吸错一口空气以及说错哪句话,为什么要拖家带口、离别父母、长途跋涉、艰难开拓呢?谁不愿意好好的呆在温暖的窝里舒适的生长呢?可现在,我居然是看着许多没有赶上趟的、或者不具备移民资格的同学,感叹道:可惜啊,没能离开祖国!

---

Windows服务器撞鬼集


老说Linux的坏话貌似有点不太好,咱搞IT的也要讲究平衡。说心里话,Linux虽然有很多不爽,但爽的时候也有,此时就会挺痛恨Windows里的一些问题。

Windows服务器里面的东西通常都比较傻瓜和简单,但是这也就造就了如下的一些问题:
1、出问题的时候,日志不一定有,或者有了也不够清晰,或者无法调整至更清晰的日志输出级别。相对应的,Linux里面较成熟的应用都很注重这类事情;
2、不开源,导致除了问题要查找非常的困难。如果遇到了文档齐全的还好,要是不齐全或者文档里面没有描述,那就可能需要你通过付费服务来解决了;(这又延伸出来另一个问题:比如说,开Ticket让微软来解决,那我的代码也有只是产权不便提供。可以想象,他们调试起来也会很困难,乃至盲人摸象。)
3、要做到傻瓜,就一定有额外的开销,甚至从设计上就有不同的考虑。除了可能导致占用更多的资源,还有可能会导致某些操作的速度急剧下降。

今天我们就遇到了一个例子,正好三个问题都沾点边。这个问题其实一直都存在:用户说访问好慢啊!只是原来不那么明显,再加上系统结构复杂,CDN、交换机、负载均衡、缓存、Web、数据库、存储,分别需要考察CPU、内存、带宽、硬盘IO等项目,也确实不容易。之前也找到一些有嫌疑的地方,但处理之后却没有太大的改善。今天又找到了另一个嫌犯,貌似它才是元凶:Web上面缓存目录中文件过多,导致读取该目录的文件时,速度非常的慢。

在Web上面做缓存,是为了避免每次都动态生成一些内容,减少后端DB的各种压力和Web端的CPU占用率。理论上来说,由于前端已经有缓存,后端的磁盘IO应当不是问题。比如假设每秒钟有100个链接需要通过缓存文件返回,每个文件平均大小10KB,大概也就1M的数据量。加上可能有很多文件是已经在内存中有缓存的,因此实际的磁盘读取量并不会那么大。

按道理来说这种级别的随机读取应该不是大问题,对吧?但是当一个目录中存在大量的文件时,就可能悲剧了:比如说这个目录下有10000个文件,平均每个文件的文件名为50个字符。这时候整个目录光是文件名的数据量就达到了:10000*50*2(Unicode嘛)=1MB。实际上一个文件名至少还需要对应上该文件所在的位置等信息(具体有哪些没去考究),而实际读取的数量也并非全部的数据。由于NTFS文件系统中记录大目录中的文件名所采用的是B-树的形式存储的,检索一个文件所需要读取的信息量也没有那么大,而实际读取数量取决于你要查找的文件所在的层次。后者则取决于这个目录中文件的数量,一次分配的Extend会有多大,文件增删的情况,以及恢复平衡的策略。(啥是Extent见这里

这个B-树的层次会是只有2层,还是有多层,目前还没有找到确切的说明。一次分配的Extent会有多少个簇,也还没有找到确切说明,但貌似我看到的情况好像是1个。同时如果一个子树节点包含太多孙节点,检索效率也会明显下降,因此不可能很大。

为了便于理解和计算,假定一个文件会占用100个字节,而B-数的层次是多层,每次分配的Extent均为2个簇。因为MFT节点大小为1K,一个簇4K,我们可以得到:(大约)

  • 根节点大约能保存5个文件;
  • 每一个(层)子树可以保存8K/100 = 80个文件;

在树平衡的情况下,10000个文件保存的情况大致如下所示:
根:5=5
第一层:400=5*80
第二层:3600=5*80*80
第三层:5995<5*80*80*80=2,560,000

也就是说,比较糟糕的情况下,打开一个文件需要访问到第三层,也就是要访问1+8+8+8=25K的数据。对于平均状况而言,可能需要访问13K。如果我们套用之前的数据计算,每秒100个文件,文件数据大小10K,再加上这13K的目录数据访问量,每秒磁盘读取数量就达到了2.3M每秒了。

呃,好像也不是很大啊。但是当我们用ProcessMonitor来监视的时候,就发现了这样的一个记录:
ReadFile C:\XXX目录\YYY目录 SUCCESS
Offset: 6,664,192, Length: 4,096, I/O Flags: Non-cached, Paging I/O, Synchronous Paging I/O

请注意,这个操作居然是Non-cached的!也就是说,这种操作很可能每次都要从磁盘中读取出来。我们知道,对磁盘的随机访问性能实际上是很低的。一块普通的7200转SATA盘,随机IO能达到1MBps基本上就已经到顶了。即便是1万转的4块盘做RAID10,随机IO能够达到10MBps已经很难了,一般也就维持在5-6MBps之间。按照上面的计算,使用后一种配置的服务器,每秒也就只能够撑大概700个文件的随机访问。(呃,如果你只有那么700个文件的话,那肯定不止这个数,因为文件会被缓存进来,甚至7000个都不成什么问题。但如果你有几十万个文件,并在这堆文件中来回随机访问的话……)

从上面的分析可以看出来,要提升性能,除了提升硬件性能外,还需要注意:
减少一个目录中文件的数量;
减少文件目录的层次;
缩短文件名的长度。


呃,等等,还有一个问题没解决:为什么是Non-cached的呢?是否因为这个原因导致了系统性能不佳呢?目前说实话还没有找到资料,也许是.NET框架的设计原因,也可能是操作系统的设计原因。

这,就是Windows系的一个大问题:很封闭。如果是在Linux世界,大不了直接看各个部分的代码,只要你能看得懂,也愿意看。反正只要花时间,那也一定是可以解决的。但是在Windows世界那就未必了,比如我之前遇到的一个问题,最后就不了了之了,我也无能为力。而在Linux世界里面,squid有个行为和我们想象的不一样,看代码发现有个地方的判断和想象的不一样。对于后者,我们就可以讨论,到底是改让后端用另一种方式输出呢,还是修改Squid。换做Windows系统,大多时候只能干着急,别无他法。比如说我们就遇到StateServer不时地报告连接中断的问题,但就是没有什么特别好的方法进行排查,也没有特别详细的文档去说明这一现象,最后仍然是不了了之。

也许有人说,NTFS资料还是挺多的啊。可是很多时候这些资料是不准确的,比如上面说到的保存目录的方法是B-数,在很多很多的地方都说是B+树,包括微软官方的页面维基百科的页面等等。但是实际上请仔细看微软官方页面上提到的图二,在看看关于B+树B-树在维基百科上的定义,你就会发现NTFS采用的显然是B-树。这个错误估计是某个半桶水的无证临时工,在编辑网页资料的时候将工程师写的"B-tree",私自修改成了"B+tree"造成的。也有可能是画图美工嫌工程师给的图不好看,随手给改成现在看到的图二,结果从B+树变成B-树的形态了。事实是如何的呢?因为没有代码可看,实在不清楚。而MFT内部结构据称也是非公开的,没有官方资料。当年曾经想自己写一个后台不停修复硬盘碎片服务的代码,其中有一步叫做查找碎片,Windows有API可以做但是很慢。我所看的那个帖子中也提到,如果直接通过MFT记录来查找碎片的话,会快很多,可惜这部分内容不公开,网上现有资料无法保证完全准确。

如果你在Windows中遇到的问题跟可能与你无关的部分有关,后续的工作通常很难开展。.NET类库算是一个例外,这也是我为啥这么喜欢这个平台的原因之一。

2012年3月14日星期三

法73条

国人真冷漠,或者说麻木。

基本上大部分人对于刑诉法73条的意义不甚了解,甚至压根就不关心这些新闻。不知道是从什么时候开始有的这样的毛病——事不关己,高高挂起。就第二代最高领导人也曾经有过不按照宪法办事的过去,最后就被同样的方式吞噬。甚至还有段子说,当时被批斗得受不了时,手拿宪法愤怒的呐喊。不管这是否是真的,最后宪法成为一块谁都往上蹭的擦手布,是毋庸置疑的。

蹭就算了,关键还没有任何尊严,随便一部什么法律既可以无视它的存在,也可以消费它的价值观。就比如刑诉法修正案吧,公安自己就可以决定指定地点监视居住6个月,明明不尊重人权,却非要挂到脸上。

这个国家的代表们也挺有趣的。这个有趣我是这么理解的,就是傻了吧唧自杀还帮别人订好自制血馒头。怎么说呢,这是一种恐怖平衡。意思就是,谁也别乱动,到时候说不定你也一样会消失。这跟目前国内企业的窘况是一样的:要都按照规矩来,所有税费交期了,一定陪本没法做生意。但实际上一般睁一只眼闭一只眼,看你肥了敲打两下。关键还不在这,在于你不听话了,或者我就是缺钱花了,你只能投降缴枪全部拱手相让。否则,随便找点证据你就是违法经营,偷税漏税。所以中国的企业都怪怪的,就像这些代表一样。代表们身负着各种原罪,大部分代表都害怕某个代表良心发现突然要搞变革,那时候身上遮羞布一旦退去,清算起来死翘翘。所以只好现在先搞好恐怖平衡,大家都别乱动,乱动的话随便给你安个贪污受贿、危害国家安全的罪名。管你最后罪名成不成立,先指定居所监视居住6个月再说。居住的时候也有人陪:不许动啊,6个月只能平躺双手不离创办,只要一动给你来个不见外伤的大刑。6个月过去放你出去,你敢说内里发生什么,再来一个危害国家安全的帽子,继续吧。

新浪微博上面有人痴痴地在想:实名制怕的不是我们,如果实名了我们还照样说,该害怕的是他们。我认为,这是不可能的事情,因为有恶法在哪里等着呢。3/14通过刑诉法修正案,我认为至少有一小部分原因是为了3/16日微博实名制铺路的。看吧,到时候不噤声的蚂蚱还能怎么蹦达,律师都捞不到你——该法说了,如果涉及危害国家安全的,那律师会见是要法院批准的。

作为对比,关塔那摩不在美国本土,对待的也不是本国籍人。如果敢对美国自己人,哪怕是一个恐怖分子,那不知道得要多沸腾才行了。

哎,也难怪,投票同意该法的代表的比例,貌似和参加微博实名制的百姓的比例类似。由此可见,智商之低下,见识之肤浅,不可救药也。


2012年3月13日星期二

初学配置安装Linux撞鬼集3 - 莫名其妙的squid

知道为什么Linux的维护/开发人员值钱吗?因为Linux世界真的有太多不可思议的事情了:比如说相对于可以调整的参数及其导致的莫名其妙的问题,文档和记录实在是太少了。这里就拿squid来说说吧。

我们使用的Squid服务器之前跑得好好的,突然有一天出现了严重的问题:磁盘空间满了,上去一查,发现在/app/squid/var/logs这个目录里面全是core.12345这样的dump文件,而且几乎是每秒钟都要产生这样的dump。通过gdb打开之后发现,几乎每一个dump都出现了类似如下的情况:

Core was generated by `(squid)'.
Program terminated with signal 6, Aborted.
#0 0x000000373aa30265 in raise () from /lib64/libc.so.6
(gdb) where
#0 0x000000373aa30265 in raise () from /lib64/libc.so.6
#1 0x000000373aa31d10 in abort () from /lib64/libc.so.6
#2 0x00000000004812d1 in fatal_dump ()
#3 0x000000000049c7f9 in xcalloc ()
#4 0x000000000047cc8b in storeKeyDup ()
#5 0x0000000000475b4a in storeHashInsert ()
#6 0x000000000048f280 in storeAufsDirAddDiskRestore ()
#7 0x000000000049092e in storeAufsDirRebuildFromSwapLog ()
#8 0x00000000004375d4 in eventRun ()
#9 0x000000000045da06 in main ()

上面标黄的#3这一行就是问题所在,在其下面的堆栈情况则并不一定。如果你直接搜索xcalloc和squid,几乎是找不到任何线索的。当然,你仔细看网上那些英文帖子的话呢,也会看到有人提示你使用free看看内存是否充足,使用df看看磁盘是否满了。可是你一看,就会发现其实很正常。在Google上搜索这两个关键词,几乎都会指向这样一个Bug,这里面的堆栈和我遇到的问题极度相似。可问题是,人家使用的是3.1系列,而我使用的是2.7系列。而这个BUG的讨论中有人说了,3.1有问题但2.7没有。而且该Bug的描述说的是,如果有人POST了一个很大的文件,超出了内存容量,则可能会出现此错误。但显然,因为这种问题导致频繁当机的可能性太低了,不应该是这个原因。到了这里,似乎又没有任何头绪了。

实际上,如果仔细看第一个帖子,里面还有人贴出了大量的信息,什么Dependencies.txt、Disassembly.txt啊等等。关键的地方其实都不在这些链接里面,而是5楼的回复
I'm seeing this too; I'm attaching the /var/log/squid-deb-proxy/cache.log, which seems to indicate that squid is attempting to allocate approximately 10PB of memory for swap. This, obviously, fails; hence the abort. 
也就是说,在这个cache.log里面会有线索。Linux先进的地方就在于日志真的很多,dump也很多。但问题也很多,尤其是莫名其妙的问题。那么在这个日志文件里面又说了什么呢?在我这里,这个日志大致如下:
FATAL: xcalloc: Unable to allocate 1 blocks of 4112 bytes!
Squid Cache (Version 2.7.STABLE9): Terminated abnormally.
CPU Usage: 23.655 seconds = 11.627 user + 12.028 sys
Maximum Resident Size: 4665232 KB
Page faults with physical i/o: 0
Memory usage for squid via mallinfo():
        total space in arena:  1020340 KB
        Ordinary blocks:       1020328 KB     38 blks
        Small blocks:               0 KB      0 blks
        Holding blocks:        166348 KB      8 blks
        Free Small blocks:          0 KB
        Free Ordinary blocks:      11 KB
        Total in use:          1186676 KB 100%
显然,我这里的情况和BUG所导致的症状很不一样。前者每次都是分配一个很小的内存——最低只有24字节,最大也不过几KB;而后者则是分配4035364077 blocks of 1 bytes。这就进一步增加了此问题非彼问题的几率了,可还是不知道问题在哪对不?好在这次又有了另一个新的关键词:FATAL: xcalloc: Unable to allocate 1 blocks of bytes

这个问题一搜索,就发现有不少人报告这个问题,除了上面提到的BUG之外,也有像我这种分配一点点内存即报错的(B贴)。当然,也有中文贴,只不过中文贴貌似真没几个人进来讨论,就更不要抱给点营养的希望了。

除了报Bug之外,还有一些文档说这个问题“通常”跟以下两个问题有关:
  1. 这个机器的磁盘交换空间用完了;或者
  2. 这个进程的数据段大小已经达到最大限制了。
对于这两个问题,第一个应该用ps -m或者cat /proc/mem查看。(如果你想知道具体交换文件在哪里,可以用cat /proc/swaps或者swapon -s来查看,坑太多了,这里插播一下。)第二个问题可以通过ulimit -a和ulimit -aH来查看,其中第一个似乎是查看目前的配置,第二个似乎是硬件本身以及kernel编译时的限制。

我执行第一个命令之后,发现空余的交换空间足以将整个进程都交换进去,而正在使用的交换空间则小得可怜(也就100兆),于是第一个问题可以排除。而第二个命令则发现data seg size均设置为unlimited,这个问题也可以排除了。于是这个文档又没有解决问题。

(插播一下,你需要通过运行uname -a来看看你的系统是否64位。如果是32位的系统,也可能会有2G左右的内存大小的限制。)

最后,还是B贴的最后一个说法给出了本问题的终极答案:编译时去掉dlmalloc参数就能解决问题了。而且在这里,你还得到了另一个“有用的”忠告:不要使用你不知道你是否需要的配置参数。这真是一个有用的忠告啊!后面再说这个问题。

真TMD神奇了,这可是从repo下载下来的源代码的默认编译参数啊!那么dlmalloc又是一个什么玩意儿呢?上面那个讨论中说道:
这个参数是为了让那些malloc内存分配函数实现得很糟糕的操作系统所准备的。要知道你是否正好走了这种狗屎运,(在关掉这个参数的情况下,编者注)你会看到Squid不停的增长,但已分配的内存没有变化,而空余内存则一直在增长。
我估计说的是内存地址空间增加,但实际分配的数量很少。相应的,因为内存地址增加导致总虚拟内存增加,而空余内存则因为总虚拟内存-实际分配内存的计算,导致空余内存不停增加的假象。

如果你想知道具体什么是dlmalloc,这篇文章会告诉你这是一个叫做Doug Lea的同志,在1987开始写的内存分配函数。由于该邮件是1996年从德语翻译过来的,所以估计在那时候已经基本稳定了。换句话说,这是一个古董。而这个古董级的文件可能根本就无法分配超过2G的内存,甚至在前面那个B贴中说无法超过1G,而我的实际情况也是似乎无法超过1.2G。可是这个问题在很多地方都没有提及,尤其是中文的编译参数详解说明

看到了吧,Linux里面可以动的东西很多,坑也就很多。大家一定要注意了,LINUX下面的默认编译安装方式,有可能会要了你的亲命。为了避免出现像我这里描述的问题,去掉默认参数也许都不是稳妥的。因为有可能某些参数一去掉,这种隐藏的Bug是没有了,但性能就急剧下降,甚至你无法证明你的系统必须要有某些参数才能够工作。于是,保险的办法应该是了解都有哪些编译参数,每一个参数是干什么的,你的系统有什么限制,你需要什么样的优化。这才是刚才那句忠告的终极含义!

换句话说,你需要搞清楚什么事内存分配,malloc和dlmalloc的关系,还要英语过硬知道怎么搜索。这,就是一个linux系统应用维护人员所需要具备的基本素质,可不是Windows人员拷贝安装就可以搞定的。这还不包含安装完毕之后的各种配置呢!




2012年3月9日星期五

踏术而上

对于技术人员来说,学习是必须的。有不少人.NET的同学都是沿着提问(CSDN)->博客(博客园)这条道路学习的,但有一天你可能会遇到瓶颈:为什么感觉掌握了不少东西,但具体到开发或者工作的时候,总是觉得不能得心应手,总是不知道从何开始施展,又或者总是觉得做出来的东西是不太对劲呢?

如果你有这样的体会,也许可以听听我不见得正确的看法。我认为这基本上属于国内教育方式的遗毒。国内教育方式总爱填鸭式,比拼谁记得的东西多,很少引导和启发每个个体去独立的思考问题。这种方式所教育出来的同学,对于解决封闭问题都能得心应手,但面对开放性问题就会无从下手。

最近我遇到的例子是这样的,说:系统突然从某天开始,每天都出现各种卡、慢、错的情况。这个问题我一开始并没有介入,而是试图让下面的人自行尝试面对,最后发现结果不理想。处理的方式基本上停留在听说别的部门告知机房正遭受攻击,然后就反馈给用户说机房受到攻击,全然不管攻击是否真的影响到我们,或者是否真的是因为这样导致我们系统的不稳定。

在提醒了问题可能不是这样之后,处理的方式又转为随机式的试错,比如尝试机房流量转移,甚至提出建议说是否可以增加前段服务器。但当我仔细去了解之后发现,基本上没有任何调查,也就没有任何证据能表明这是前段服务器的问题,更无法证明增加了前段服务器就能解决问题。于是,我说明了先找证据再分析的重要性,并明确不要在假设的基础上做推理。比如有如下推理:加入HaProxy都扛不住了,那么后端早就扛不住了,现在后端没看到报警,所以前端一定没有问题。理论上这应该是正确的,但实际上HaProxy因为配置给后端服务的并发连接数量很小,导致大量连接过来的时候HaProxy认为后端已经没有服务器有能力进行处理,于是报告了503错误。

在此之后,还有同学抱怨没有给出解决方案,所以不知道怎么做。其实,如果你想坐比较高的位置,那么你必须拥有“独立思考解决从未遇到过的问题”的能力,也就是解决开放性问题的能力。也许有人会说:我不会Linux啊,总得有人来带一下吧。我必须说明,除了给你几个建议,告诉你要自己想之外,剩下事情别人帮不了你。如果你希望通过别人手把手教你处理几次问题来获得成长,那我可以说你不过获得了一些经验值,下次出现全新的问题时,你依旧是小白一只。

这种处理开放性问题的诀窍,在于思考关键点并动手检验这一步骤,而不是学习使用某种工具。很多人喜欢问一些关于语言、设计模式、算法、HaProxy的配置方法等等的问题,这些知识是必须要具备的。前述的那些名词,是一些工具,或者说是“道法术器”当中的“器”。不学无术的意思,就是你不学习就不知道有关如何使用该工具的只是。如果你随便大概的糊弄一下,断然是不行的。但同时你也需要清醒的认识到,这样的知识再多,也摆脱不了蓝领的定位。要想进步,成为高人、神级的人,你需要“懂法”、“得道”。

术,是指具体的操作办法。以武术为例,拳法套路可以说是术,并不能实战,真正实战的时候并不是按套路来的。而张无忌学习太极之后“全都忘了”,就是得道了。而对于码农来说,术就是如何在工作当中熟练使用设计模式。请注意,是熟练而不是正确。我经常遇到的一个典型是滥用,或者说是不知道何时要用,为何要用,这是因为使用者并没有去思考背后的道法。

比如说做一个很简单的工具:这个工具会自动监控IIS进程,如果发现长时间CPU很高,就会调用一个程序来重启相应的进程池。重启的过程包括三个步骤:1、执行一个命令找到PID对应的进程池;2、执行重启进程池命令;3、根据命令执行的结果记录日志。由于步骤2可能会比较耗时,甚至可能因不明原因死机。考虑到持续监测的需要,异步执行是很必要的。有人会大致设计成:

ManualResetEvent exitEvent = new ManualResetEvent(false);
void Restart(int w3pProcessId)
{
   string poolName = GetPoolName(w3pProcessId);
   Task task = new Task(RunRestartCommand, poolName);
   task.Start();
   if (exitEvent.Wait(10000))
   {
      LogTimeout(poolName, w3pProcessId);
   }
   else
   {
      LogSuccess(poolName, w3pProcessId);
   }
}

// 其它函数省略

你看,多豪华啊:通过Task来实现异步,内部通过ManualResetEvent放出信号让外部继续执行后续操作,同时通过Wait(timeout)来保证不会死机。这里每一个“术”都看着很眼花缭乱,但从简单就是美这个“道”来说,并不优美。比如可以这么写:

void Restart(int w3pProcessId)
{
   string poolName = GetPoolName(w3pProcessId);
   Process restartCommandProcess = CreateAndRunProcess(poolName);
   if (restartCommandProcess.WaitForExit(10000))
   {
      LogTimeout(poolName, w3pProcessId);
   }
   else
   {
      LogSuccess(poolName, w3pProcessId);
   }
}

通过建立一个Thread来调用上述函数,是否显得更简单呢?实际上第一个例子还有另一个错误:实际上Restart函数还是阻塞性的,只不过控制在10秒以内,对于持续监控而言,仍然是不稳定的因素。

回过头来讲,Task虽说是异步的一种,但并非用于解决这类的问题的。Task实际上是为了让各个异步步骤能够更方便的串起来,包括各种取消功能。用在这里,显然是“好钢没用在刀刃上”,或者说是错误的。

当你学习完术之后,一定要思考背后的道法,或者说“为什么”。否则,你就很容易像第一个例子中那样,堆砌各种花拳绣腿。术完全可以学,而道法则需要会悟。对于现成的案例,自然也可以说出些道理法则。但是越到高处,你会发现有越来越多的未知问题,同时已经遇到并且成功解决的人会越来越少。换句话说,有大量的问题是不可能有说明书告诉你背后的门道。和“器”之“术”不一样,后者发明“器”的人,必然会提供“术”。比如推出Squid的团队,会给你一个说明书。定义“设计模式”的人,也会告诉你这是什么,怎么用。但只有当你可以发现并理解背后的问题和道理,你才可能发明Squid,发现设计模式的时候。

为什么到了看写博客这个阶段之后,通常会有一个瓶颈?那是因为大多数人并没有明白上面的奥妙,仍然在弄术。关于这一点,看看博客园的文章便可知一二。比如摘抄标题如下:

ASP.NET伪静态 UrlRewrite(Url重写) 实现和配置

这类的文章在博客园中占比当是大头,基本没有什么道法。少量的如敏捷立会两三事,标题算是“法”的范畴,但内容却只有问题和结论,没有中间思考过程。真正讲解道法的文章,少之又少。如果认同,却又不知如何提升,那就需要做下面两件事情:
1、寻找一些讲道理法则的博客,比如

2012年3月6日星期二

初学配置安装Linux撞鬼集2


什么?0 == true?
习惯了各种C系编程,很自然的认为0表示false,1(或者其它非零非空值)表示true。于是当你看到一个脚本如下所示:

#!/bin/sh

function ret0 { return 0; }

if ret0; then
    echo "HELLO! The condition is TRUE!"
else
    echo "HELLO! The condition is FALSE!"
fi

这时候,如果你继续使用C系列语言(乃至绝大多数常见语言比如js)的思维来思考的话,你会认为输出应该是

HELLO! The condition is FALSE!

可是当你运行这段脚本之后,你会发现结果居然是TRUE……当然了,自圆其说还是可以的:C/ASM的标准中,返回0表示没错,其它值表示有问题。既然经常要判断是否正常,用0表示true也是可以理解的。这个让我想起了在飞机上和印度人聊天,当他点头的时候我都不知道到底算是Yes呢还是No,我看他自己也搞不清楚,经常是先点头后摇头,或者先摇头后点头。

总结:
Linux/Unix的Bash尽管是用C写的,那也不代表其脚本语言就遵循了这些语言的逻辑标准。

P.S.:
Q:为什么成功通常是0呢?
A:因为出错的原因多种多样啊,比如1表示文件不存在,2表示目录不存在,3表示你太罗嗦了……

Q:可是,这还是不能解释为什么选择了0这个值,而不是其它值,比如1来表示成功啊。
A:因为你必须写某个判断语句来判断之前的操作/调用是否成功,比如说:
if (ret == 0) printf ("success");
0这个数字比其它的数字都好记,但这还不是最重要的。最重要的是,判断是否为零,在汇编当中比判断任何其它数字都要简单和快速(相对于机器而言),至少在x86系列肯定是这样的。比如:
; if ax == 0 goto label
xor ax, ax  ; 当然了,这里的ax就毁了。但通常后边不会再用,要用就先备份好了。
jz label

; if ax == 1 goto label
cmp ax, 1
jz label

前者最短只有2个字节,后者最短要5个字节。早期的CPU中(比如386),时间周期也会有非常巨大的差别。前者第一句话可能1个时钟周期就完事了,后者可能得要4个时钟周期。啥?看不懂?复习汇编去。

(待续)

2012年3月5日星期一

霍格沃茨魔法学校

记得年轻的时候(初三?),我曾经在一台586上面安装过某个版本的Linux。当时确实曾经真心想学,因为那时候正迷各种C++。可是后来在上面跑XWin,发现慢得不可理喻,比Win95是慢多了,于是觉得业界有些东西真有吹嘘的成分。图形界面慢得不行,命令行又和DOS有那么些差别,再加上互联网不比今时今日之发达,很多东西真的很难查。尤其是中文资料,相对今天而言真是挺少的 这些困难对于我来说,那是一种痛苦,对于寻找有趣事情的我来说,学习Linux自然成了优先级很低的事情了。

后来,兴趣彻底转移到了编译原理,再往后还延伸到一些中间语言虚拟机的皮毛,结果就堕落到了MS的怀抱,尤其是.NET。直到某天,由于机缘巧合,一个大系统中需要用Linux系的不少东西来进行配合,比如HAProxy和Squid,于是稍有接触。后来,负责这两个系统搭建维护的同学离队,加之和Linux有关的一个小Team被另一个部门借走,这件事情自然就回到了我的视野附近。换句话说,不得不认真对待了。

本来呢,我也没有想认真对待的。但是当兄弟们对某些紧急的事情处理不过来的时候,就不得不加入到一线战斗中。比如说安装、配置HAProxy,写一些脚本分析其中报告的日志等。在这段时间里面,我开始从舒适安逸的MS风格开发,转移到千头万绪的细枝末节当中。感觉就像麻瓜因为某种错乱进入到霍格沃茨魔法学校里面一样,发现开个门都要念个Alohomora(阿拉霍洞开),不由得一头雾水、晕头转向。

好在,我的启蒙之路是从DOS开始的,命令行和批处理脚本也没有少接触,甚至汇编、内存驻留程序啥的也搞过,算不上是个麻瓜。再加上英语经过长期磨练,不算精通那也足够应付检索资料了,以及今时今日互联网之发达,寻找解决方案相对想当年已经容易很多了。

即便如此,我这个在微软这个麻瓜世界呆久了的人,一时半会儿在霍格沃茨魔法学校还不甚适应。常见的症状是不知道咒语怎么念,以及念着念着搞错了,还有就是各种迷路。比如说吧,配置HAProxy,其实就比较烦人。不像非常成熟的产品,直接yum install就能基本完事。而是需要wget / tar / make / make install 一步步下来,中间乱七八糟要注意的地方还不少。

首先说wget,对于麻瓜来说,下载通常都会进入到“我的文档\下载”这个目录(或者类似),但是wget不是的,你的命令行当时在哪个目录,就会保存在哪个目录。我第一次装mono的时候没有注意,就搞到了root目录里面去……

接着tar这边有一堆的参数“-zxvf”,每个是干什么的得要仔细看帮助才知道。其实照着网上的麻瓜和巫师写的各种安装命令行,只要是用到tar的命令,基本上参数都是“-zxvf”。换句话说你照着念基本上没错,可是对于不少麻瓜来讲,这真的就只是一种必须照念的鸟语。

如果说tar那堆参数通常错不了,接下来的make就头大了,TARGET是什么,CPU是什么,ARCH是什么,你都要照顾。通常这几个还算好,基本都差不多。但接下来就有一堆的隐藏参数USE_XXX,这个就真是不知道哪儿找去了。比如说有一个USE_POLL,代表什么目前我还没有找到一个很官方的文档说明,貌似是USE_POLL=1表示编译的时候包含POLL功能。但是,当我看到有文档说USE_POLL是代表禁用POLL,我就晕菜了。此外,如果要打开EPOLL,是否USE_EPOLL=1呢?貌似也找不到文档。在查找这些资料的过程当中,又发现一个叫做USE_LINUX_TPROXY=1的选项,作用是让backend端能够查看客户端真实的IP地址。而实用这个功能,要么你的内核是2.6.28以上,要么就要下载特定的代码,重新编译内核。这些各种隐藏的参数,很可能要仔细阅读每一分文档,乃至阅读Makefile来解读之。同时,这些选项要让如何才能生效,可能需要你对Linux系统本身有一定的了解,包括如何编译内核。也许编译内核啥的也是一个很简单的咒语如Diffindo (四分五裂),但不出问题还好,一旦出些非常莫名其妙的问题,你可能就要很深入的了解整个操作系统,乃至某一行内核代码大概如何发挥作用才好。(水好深啊……霍格沃茨魔法学校太危险,麻瓜们还是回伦敦去吧。)

--- 后记开始 ---

其实仔细研究Makefile,发现里面的脚本已经能够根据当前的系统状况,判断当前适合使用的参数了。可以说,网上大部分说应该添加TARGET=linux26 CPU=generic ARCH=x86_64 USE_EPOLL=1等的,都属于多此一举。只有你有很明确的不希望使用某种默认功能,或者你的系统被你自己定制得不是默认情况时,才需要进行调整。
哦,对了,我还看到网上有人说TARGET要设置成linux86的呢,真是欢乐。幸好我稍微grep了一下Makefile,觉得不对劲。麻瓜们,不要照着网上贴出来的咒语直接念,一不小心就会念成傻瓜的。

--- 后记结束 ---

后面make install的时候,一不小心没有指定PREFIX,结果就装到了/usr/local里面去了。很奇怪的是,网上的各种介绍说,在make的时候指定PREFIX就能在后续的安装中安装到指定的地方,但我的情况并非如此。至于为什么,反正现在正确了,我也不想再回过头来重复这一痛苦的排查问题过程。

在这之后,还有各种配置阶段,这个还好,基本上有文档。虽说有文档,但那选项之多,应该怎么样才是比较好的配置却完全不知道。估计大部分同学都是要在网上一顿乱搜,再经过实战的检验,然后试错试错试错……

配置还算顺利的了,如何让HAProxy变成自动启动的,居然也要自己动手丰衣足食。Linux的自启动通常就是在/etc/rc.d/rc.local里面添加一段启动脚本,而启动的脚本也是需要自己编写的,比如这篇,或者这篇。嗯,为啥这两篇里面的脚本几乎完全不一样呢?Linux世界就是这样的啦,你要习惯。好比如果有一百个哈利波特,那伏地魔就得有一百个死法。其中前者的脚本目前我这里还是不能完全正确运行,原因是我启动了多个进程,而该脚本stop的时候,使用了functions里面的相关功能来从pid文件获取当前正在运行进程的pid。但是奇怪的是,HAProxy输出的pid文件是每个进程一行的,而不是在一行内用空格分隔不同的pid。这就导致了该脚本永远只停止第一个HAProxy进程,而不包括其他进程。(麻瓜们,有没有看着像天书一样的感觉?)

--- 后记开始 ---
Linux有个最大的好处,就是几乎所有东西都能够看到、改得了。包括服务的启动脚本啥的,通常都是一段脚本。比如第一个链接中的脚本,只要在stop里面killproc $prog修改成这么几句话就可以了:


    local tmpfile="/tmp/haproxy.pid"
    if [ -f "$lockfile" ] ; then
        cat "$lockfile" | tr "\\n" "$IFS" > "$tmpfile"
    else
        tmpfile="$lockfile"
    fi
    killproc -p "$tmpfile" $prog

--- 后记结束 ---



看到这里,你就会发现霍格沃茨魔法学校确实不太适合麻瓜。没错,Linux程序之间的协作和配合,比之Win系的确实容易不少,至少命令行如此。这也造就了Linux程序有很多默认的协定,比如说参数就有协定:如果是一个杠开头,后面的每一个字符都是一个参数(-zxvf就是这样),如果是两个杠开头,后面就是一个单词组成的参数(--help便是最常见的,几乎每一个程序都支持,几乎……)。这些基本的协定不仅限于命令行参数的规范,我就不一一表述了。

Linux世界之于Windows,开源这一点不同导致了程序设计思想有很大的不同。在Linux世界,似乎能够各种配置调整到每一个毛孔的东西,才便于大行其道。因为具体每个地方的应用场景肯定有不一样的地方,具体需求或多或少肯定有这么些个差异。在Linux世界里面更倾向于修改程序(包括配置),来适应需求,而不是苟且一下让自己的需求适应程序的能力。也正是因为这一点,就导致在霍格沃茨魔法学校这片地儿你需要学习大量的咒语。

换句话说,在Linux世界混,你需要知道的就比Windows上面多太多了,否则就算是安装个程序都这种运维的工作都很难做好。除此之外,你还需要有很强的解决未知问题的能力,或者说独立思考能力。




初学配置安装Linux撞鬼集

对于刚踏入Linux的同学,肯定会遇到不少“撞鬼”的事情。所谓撞鬼,就是说:“邪门了,怎么就是不行呢?”为了让大家不要重蹈覆辙,这里记录一些我遇到过的事情。

为什么不能往特定目录记录日志?

这个问题的来由是这样的:某天我需要在内网一台已经配置好的Linux服务器上面,配置一整套与线上正式环境完全相同的测试环境。其中有一个步骤就是安装HAProxy,安装的过程基本还算顺利。配置并运行后发现,服务倒是起来了,但日志却没有记录到指定的文件当中。

需要说明的是,HAProxy这里选用了syslogd的方式,在/etc/sysconfig/syslog里面开了-r -x参数,然后在 /etc/syslog.conf 里面添加了一行“local3.* /mydir/logs/haproxy.conf”,同时修改好haproxy的配置"log 127.0.0.1 local3 info"。本以为这么配置一下就大吉了,但是实际上却发现该目录下面没有该日志文件,这就诡异了。由于一开始配置错误,选择了local7,于是日志记录到了/var/log/boot.log里面去了。所以很明显,该问题不是因为HAProxy或者syslogd配置错误导致。通过使用ls -l以及getfacl来查看各个目录和文件的情况,发现测试环境和正式环境是完全一致的。那么,到底撞什么鬼了?

经过一番查找,发现了这么一篇FAQ,发现原来:

  • /etc/syslog.conf的配置里面不应该使用空格而是"tab",来区分前面的local3.*和后面的目录;
  • syslogd不会自己创建文件,要你自己先创建文件(比如用touch /mydir/logs/haproxy.log)。

我第一个念头就是“真狗屎”,你说这叫什么事啊,tab才行空格就不行,还要自行先创建文件。好吧,为了安全,还是自己动手丰衣足食好一点。由于凑巧使用的是tab而不是空格,第一个问题我没有遇到。而第二个问题,我确实没有创建该文件,只好老老实实touch了一下。可是touch完了,这个文件中还是没有任何的日志,哪怕我狂刷浏览器。这就奇了怪了!

经过咨询周围了解linux的同学,还有当初架设线上服务器的前同学,最后终于发现……他们也不知道为什么。非常沮丧的我只好“胡乱瞎试”:

  • 既然我自己创建的目录里面不能记录日志,但是/var/log可以记录,那肯定是这两个目录的某种差异,甚至最可能的就是权限差异造成的;
  • 既然/var/log可以记录,那肯定可以在这个目录当中创建新的log;
  • 既然可以配置,那干脆把所有日志都记到某个文件当中好了。

于是配置/etc/syslog.conf,添加一行“*.* /var/log/debug2.log”,并touch之,然后service syslog restart。这时候通过more /var/log/debug2.log就会发现,里面有那么一行:
某年某月的某一天 时分秒 机器名字 setroubleshoot: SELinux 正在阻止访问带有默认标签 default_t 的文件。 For complete SELinux messages. run sealert -l 某个GUID

好吧,我手贱,语言选了中文,懒得改回去了。英文版本的应该是“…… SELinux is preventing access to files with the default label, default_t. ……”。

上述信息的含义,是安全上下文环境设置不正确导致的,通过使用命令 ls -Z /mydir/logs可以看到该目录下面的上下文设置信息,如下所示:
-rw-r--r--  root root root_u:object_r:default_t      haproxy.log

根据某处搜索的结果,发现setfattr -x security.selinux可以解决这个问题,很是高兴。但结果一运行,就会告知“没有足够的权限”。NND,我可是用的root啊!经询问才发现,原来是开启了SELinux的缘故。于是有人提供了另一个方案,就是使用setup将selinux给禁用了。

禁用这个方法肯定可以,但显然不是最好的办法。所谓SELinux,就是为了提高系统安全性的东西。禁止它,一定是比较糟糕的实施方式。那应该怎么办呢?

嗯,很简单,参照/var/log这个目录,及其内部文件的响应设置即可(使用命令ls -Z查看)。具体一点,就是执行:
chcon -u system_u -t var_log_t 文件或目录路径
或者
chcon -u system_u -t var_log_t -R 目录路径

其中后者会将该目录下所有的子目录和文件都设置成相同的安全参数,经过这样的设置,日志的记录就成功了。

总结:

  • 配置啥的都对了,也有可能因为各种安全性的原因而无法正常运行;
  • 由于Linux的设计思路是“没有错误”就不输出,但实际上有些时候因为配置的问题,一些错误信息也是不输出的,导致你总觉得正常,但偏偏就不正常。此时一定要想办法让其尽可能输出详细的信息,例如将所有系统/应用的日志都输出到某个日志文件当中。

(待续)

2012年3月3日星期六

早点去上课

今儿个去上课,老师说了,羊膜破水有两种情况:哗啦一下就下来,或者是一点点漏出,后者并不少见。而这里面还有一种叫做羊膜早破的情况,也就是没到足月的时候就已经破裂,原因也有多种,比如感染、臀位等。大部分情况下,在破水之后24小时之内就会开始产程。但是极个别情况下,会超过48小时仍然没有任何先兆。听起来似乎没什么,但实际上这种情况是很危险的,因为可以引发一些非常严重的并发症:比如感染、脐带脱垂。这些并发症,尤其是后者(发生率0.5%),随时可能会导致胎儿死亡。

生活当中确实听说了这样的案例,当时虽然觉得很危险,但也没有意识到居然可能会危险到极严重的程度。据说羊水少于300毫升,就已经是羊水过少,非常不利于顺产。当时我听到这种案例的时候,就直觉一定没有参加课程。一问之后,果然是没参加。忙固然是一方面,但另一方面我认为再忙,其成就也不可能比稳稳当当的制造一个新生命还要大,不是吗?

还有一个例子,现在周围认识的人当中,我们会听说有许多生个7斤、8斤乃至9斤以上的小孩。但实际上超过7斤的叫做巨大儿,有啥不好呢?没啥不好,除了可能很难顺产。但顺产还是剖腹则有非常大的差异,剖腹对于母乳喂养不利,产后恢复也比较慢。严重的情况还包括母亲面对更高可能性的大出血,同时新生儿可能因为肺部羊水没排干净而导致后续严重并发症。这些并发症包括湿肺、肺炎等,搞不好会要命。而剖腹产的婴儿,相对顺产的婴儿,在感觉统合神经等方面会差很多。所谓感觉统合就是比如平衡感,包括以后学写字也会有困难,比如字很难看。此外,巨大儿的母亲通常较胖,在各项激素水平上较容易有异常,包括妊娠糖尿病等。

今天的80后,可能会时不时看到一些欧美肥皂剧当中,夫妻共同参加孕前培训班联系呼吸节奏等画面。原本以为接触到这类信息的我们这辈人,应当比老一辈有更多的求知欲,或者说会强烈的希望参加这种培训班。然而实际上据我的了解,周围参加这种课程的仍不是普遍现象。甚至连那种5块钱一节课的课程都没有参加,基本上算是“无证驾驶”吧。当你连最基本的知识都没有时,就可能在不知不觉中最后步入剖宫产的必然结局中。

为了你和你宝宝的健康,准爸爸准妈妈们,赶紧参加个什么课程吧!

P.S.:如果你参加了课程,就会知道其实月子并没有那么可怕,请月嫂不定有多大作用,除了因为觉得花钱了所以一定有效果这种自我催眠效果之外。

2012年3月2日星期五

情商是什么

情商是什么这个问题,在我第一次听说的时候就觉得很困惑。没错,字面上很好理解,就是处理和各种“情”有关的能力高低,包括但不限于“感情”、“心情”和“人情”,或者说是交际能力高低的一个方面。俗点讲,那就是你会不会看颜色,会不会来事儿,会不会搞关系。也有人会把拍马屁也算进来,我对此有所保留,但里面的“见什么人说什么话”,确实也是情商的一部分。那既然都能说的那么清楚了,还有什么不明白的地方呢?

上面那些只是一个定义,可是关于怎么才算是有情商,或者更准确点讲,我要怎么做、做什么才能表现的情商高,这个问题不解决,仍然无法具体落实“情商”这个概念。从小,我所经常接触的家庭成员的智商都很高,但说到情商,基本上都属于平均水平以下。这么说吧,没有那个是经商的,当官的。这两个职业智商要多高不一定,但是情商必然需要很高才行。比如说,你不能随意对着对方情绪爆发大吼一通,全然不管你的身份、对方身份、当时场景、要解决的问题等等一系列现实。真要这么做,好点的就是砸了生意丢了官,糟糕点的遇上个黑白通吃的说不定不是丢了小命那么简单,可能是生不如死。于是乎一直以来,“情商”这两个字对我来说,都是一个很让我敬畏的词,直到最近。

最近这一年来,突然如同开了窍般,大约对这些个问题有了一些初步的了解。究其原因,有这么两点:1、学了点知识;2、有个机会练习了一下。

今天,咱们只聊聊学习知识这件事情。我在这方面的知识,主要来自于所看的1个故事、2个节目,以及听到2句话。

先说故事吧,这个故事是在某本书当中看到的,大意如下:
住在北京的银行经理琳达,最近她老公找到一个上海的高薪工作,已经去上海一个月了。她现在独自带着3岁的小孩,觉得这样的分居环境对家庭不好,于是想在上海找一个银行工作,这样就好和老公团聚。琳达去上海考察了一下,发现有4家银行附近有幼儿园,上下班正好可以方便接送小孩。于是他就向这四家银行投简历。简历的自荐信可以有这么两种想法:

方式一
我老公最近来上海工作了,所以我也想来上海。同时我考察了一下,发现你们附近有幼儿园,比较方便,于是我就给你们投简历了。

方式二
我在银行业工作N年,对于XX业务非常熟悉。之前在XX银行任职经理的时候,每年为银行带来N万元的存贷款。现在考虑到职业发展,想谋求一份更好的职位,希望能与贵行合作,为您带来更多的客户和收益……

不用说,方式二显然靠谱多了。这个显然的答案后面,有一个不那么显然的道理:人都是谈利益的,和自己的利益有关的,一定是非常感兴趣;和自己利益无关的,那多数是不会得到你需要的积极响应的。其实这个道理有很多书都提过,比如明朝那些事里面的王明阳,就是充分通过利益来调动各人,来达到自己的目的,或者说实现自己的利益。但很多时候,我们很容易犯的错误,就是拿自己的利益说事,来乞求别人完成自己的事情。关于这个部分,还可以看《人性的弱点》中的激发他人的强烈需求这一节。(如果有时间的话,人性的弱点通读一遍是很必要的。)

这个知识,我和别人分享之后,也得到明显有效地反馈。故事是这样的:岳父给儿媳做饭,但儿媳有时候想出去吃。有一次,她说:爸,你做了我的饭吗?如果没做就算了。岳父就很不高兴,觉得自己辛辛苦苦做饭,你还觉得“没做就算了”,是觉得我懒呢,还是做的东西不好吃。后来第二次,她说:爸,你这几天挺辛苦的,今天就别给我做饭了,休息一下,我外边吃就行了。岳父就很高兴,觉得儿媳妇考虑了他的感受,也表扬了他的功劳。其实事情是一样的事情,只是你说的时候是根据谁的利益来表达的问题。这,就是情商的第一部分。

这个知识和故事,其实我早就听说过了,但是实践起来,却非常的困难。简单的说,那就是事发的时候说错话、做错事,等后来平静之后再想,就发现自己又做得不对了。这个问题直到我看了两个电视节目之后,才彻底搞清楚并获得改善。

第一个节目时国家地理杂志频道的《Dog Whisperer》,中文直译《狗语者》,但如果你想看中文版本的话,需要搜索《报告狗班长》。很奇怪吧,情商怎么就跟“狗”扯上关系了呢?这里请容我先给大家简单介绍一下该节目,然后说几个节目中片段。

这个节目的内容,基本上都是某个养狗的主人,他家的狗出现各种问题,诸如狂吠、咬人等,然后请一个叫做Caeser Millan的人来帮忙解决。节目通常都是如下的流程:狗主人说我家小白就喜欢对着门狂吠,怎么叫她都不听。然后Caeser出现,和主人交谈一下说说细节是什么。然后和有问题的狗见面并模拟事发场景,比如找个人敲门。接下来5分钟内,Caeser很简单的执行了几个动作,这个狗就再也不对着门叫了。没错,基本上就是5分钟。这些狗主人还包括各种明星,各种宠物协会会长,甚至这些狗还参加了各种训狗班,什么握手之类的都会,但就是改不了那些不良习惯。

在这个节目中,Caeser每次都会强调,需要训练的不是狗,而是狗主人。他的理论大概是这样的:
狗这种动物很简单,心里状态只有两种,要么认为自己是群体的领导,要么认为自己是追随者。而面对问题和压力的时候,只有4种状态:战斗、防御、逃避以及服从。只有当狗处于服从的追随者时,才不会有任何问题。反之,这个狗就一定会有这样那样的问题。而狗之所以会处于其它状态,那一定是主人本身不是一个“冷静、自信的领导者”。比如说,有的人很胆小害怕,于是他的狗就会变成领导者,甚至给他喂东西的时候都可能咬主人。又比如说,有的人很强势,是一个领导者,但当狗有某种不良行为的时候就很暴躁的呼喝,于是他的狗可能就会显得焦虑和兴奋,甚至最后状态就从防御变为战斗。之所以狗主人不能解决狗的不良习惯,那是主人的问题;之所以Caeser一来就迅速解决问题,是因为Caeser的状态处于一个冷静、自信的领导者状态,狗就很快能进入服从的状态。

我原本只是因为喜欢狗,才看这个节目,并没有往情商这个问题去思考。可是,突然有一天我发现人也是如此的。人脑拥有强大的逻辑思考部分,但底下有一个更古老更固执的动物脑。这个动物脑管感情,基本上第一时间的反应均出于此。比如有人说你的不对,你蹭的一下就火大骂人,通常就是这个部分的大脑在发挥作用。远古智人在面对危险,比如一头狮子的时候,就会迅速的作出反应,包括肾上腺素的分泌,肌肉的紧张,心脏速率的增加等,并且迅速决定是战斗还是逃跑,还是别的什么解决方案。现代人很少会面对一头狮子,但是很显然这部分的技能并没有退化。在我们面对一些问题,或者别人的一些言语时,这种机能也会发挥作用。于是才会有很多为了一些小事吵个面红耳赤的例子,最后还伤了感情。

在日常生活中我还观察到,当一个人在这种不平静的状态下,不仅理性思维不发挥作用无法正常思考,甚至会表现的非常固执。如果一个人大部分时间都很感性,通常也会是一个比较固执的人,以至于很容易表达“我也知道那样是不对的,但是我就是……”。一个这样的人当领导,其团队如同狗语者里面的问题宠物狗一般,也会出现极不正常的表现。例如,一个暴躁的领导,其手下一定会表现焦虑,或者沮丧。反之,一个气定神闲的领导,下面的人也会按部就班的处理问题。

当我想明白这个问题之后,就发现其实无论你是否有下属,当你需要和别人沟通的时候,对方就是你的宠物。当你的状态不对时,对方的状态也不会对。而如果你要让别人能正常的和你沟通,你就要当领导,当一个平静的、自信的领导。甚至当对方有一些不良的行为时,你也需要保持一种平静和自信的状态,引导对方回归正常的状态。即便这么做不能达成结果,也不要让自己进入其它状态,因为那些状态也不可能让事情变得更好,而只会更糟糕。从另一方面讲,如果我观测到对方出现不正常状态,例如不说话、发脾气、吊儿郎当,我首先需要特别注意的地方,是自己什么状态不对。其次,就算思考后发现自己并没有状态不对,那也会明确注意不能让自己进入错误的状态。这么做的好处有两个:

1、你永远会先于“情绪失控”之前,首先注意到征兆,并且让理智引导自己。而不是等到情绪失控了,爆发了,伤害已经造成了,才回过神来理性的总结,下一次又同样的再爆发一次;
2、你会很自信的认为自己应当是领导者,而不是沮丧的或者暴躁的阿猫阿狗,更容易让自己处于一种稳定的状态中。(潜台词:嗯,我要冷静,我要做冷静的领导。)

意识到沟通对方的不良行为,很有可能是你的不良行为的投射所造成的,并且时刻让自己的情绪受理智的控制,出于冷静、自信的领导者状态,也是情商的一部分。

另一个节目叫做《谁在说》,北京青年台每晚19:45开播,雷打不动。这个是一个很“鸡婆”的节目,讲的家庭纠纷,比如妻子怀疑老公有外遇吵架啊,各种争家产啊,婆媳矛盾啊。但是跟另一个叫做《金牌大调解》的节目不一样,这个节目里面的主持人嘉宾都比较有料,通过分析、拆解和解决个人的实际心理、沟通、逻辑问题,来化解矛盾。收看这个节目原来就是想通过看这种家庭琐事的热闹,来消除一天的疲惫。收看该节目的另一个价值,是可以看到各种错误的案例,然后照照镜子看自己是否也犯同样的错误。最后还可以听听专家的意见,看这样的事情应该怎样解决。当然了,该节目最大的价值,是告诉你解决生活中、家庭中各种矛盾解决的智慧。如果没有转换到其余生活领域中的水平,对于今后组建一个正常、幸福的家庭也是很有好处的。在这个节目中,有许多很典型的状况,从中可以看出不少门道,而这些门道则可以延伸至生活的其它领域。

比如说:通常无理取闹的,或者不占理的人,都喜欢在观察室里面不停的说“他胡说”、“没这样的事”、“根本就不是这样的”;问题较多的一方,总是喜欢说别人的不对和错误,却从来不提自己的问题;难以沟通的人,基本上都是自己像机关枪一样不停地扫射,却根本听不到别人在说什么。最常见的例子莫过于主持人说:“我理解你,你确实是挺冤的。但是呢,别人这么做是不是跟你的表达也有关系呢?”但是对方却继续说:“不是,我跟你讲,还有一次他还骂我妈……”也就是说,听不进别人的话,或者说无法从别人的角度去思考,来找自己身上的原因,通常就是祸根。一个人失去了自省的能力,会变得很轴很固执很不讲理很不可理喻,也就是说没有理智可言。良好的沟通,一定是从别人的角度出发考虑对方利益,或者他看到的世界开始的。否则说的越多,问题越大。

另一种常见的情况是,永远认为自己是正确的,别人是错误的,这个人恰恰就造成问题的、犯最严重错误的那个人。这在生活中也很多见,比如我认识或者听说的离婚案例,至少有一方是极度自信,认为所有问题都是别人的错,自己都是对的。甚至双方都认为自己是对的,在这种情况下,那是完全不可救药的。人有一个很奇怪的“自锁”能力,即一旦认定是什么样的,就会习惯性的找证据来印证这一判断是对的。可是这种先下结论再找证据的做法,通常很容易曲解某些现象,自己就理解成“嗯,我之前的判断就是对的”。越是这么认为,这种找证据的欲望和错误理解现实世界的能力就越强大,最后就陷于一种错误的逻辑中不可自拔。其实家庭纠纷的开始,甚至到结束都是些鸡毛蒜皮不值一提的事情。比如我知道的一个案例中,某方认为对方不懂得关心体贴人,于是就把对方很大力的关门,看电视很晚,一声大力的咳嗽,都当作不照顾别人希望维持宁静的生活环境的一种表现,甚至认为故意为之。其实对方根本就没有意识到这是一个错误的事情,甚至可能根本只是因为他的默认力气就是比常人大些而已。当自己认为自己是正确的时候,一定要不停地质疑自己会犯错误。

上面这些,也是情商的一部分。

最后,用两句具有一定启发性的话,来结束今天的话题:

一个好的领导,是让下属在明知道那是一坨屎的情况下,仍然很高兴地吃掉它。

事情有你的一面,我的一面,还有事实真相的一面。