2012年5月20日星期日

风干的豹尸

昨日重又翻看了半日的海明威的短篇集,忽而想起了杰克.伦敦,一个和海明威有些类似的作家。两人终身示人以硬汉形象,最终都因不为世人知的原因自行了断了自己的生命。
为何自杀?论者纷纭,探幽者众。不过据自己观察,海明威是把答案放到了《老人与海》,《乞力马扎罗的雪》,还有另外一篇记斗牛士之死的短篇等作品中。杰克.伦敦则在半自传《马丁.伊登》里回答。
雄奇者自绝于世俗,或可言以激烈的方式超脱于俗世。
附记:
摘抄《乞力马扎罗的雪》的开头如下,一个非常出色的开头,尚还没有像《双城记》的开头那样被用至滥俗:
"乞力马扎罗是一座海拔19710英尺的长年积雪的高山,据说它是非洲最高的一座山。马基人称西高峰为'鄂阿奇-鄂阿伊',意为上帝的庙殿。在西高峰的近旁,有一具已经风干冻僵的豹子尸体。豹子到这样高寒的地方来寻找什么,没有人作过解释。"

2012年5月19日星期六

“活剥”《诗经》一句

前两天不知怎么突然想起“既见君子,云胡不喜”一句,默诵数遍,倒是有些意味。
默诵既多,兼被当天Twitter的“反动”信息浸染了一次,遂活剥生吞成“既为愤青,云胡不忿”。看起来似乎颇符一般愤青心态,兼志吾心。
最后,仍然想不起为何无由来想起《诗经》中的这一句话,颇觉奇怪。
——附注:昨天特意Google了下,发现知道此句当是从《神雕》一书中得来,而非《诗经》原文。想起此句之因仍是模糊难定。

2012年5月1日星期二

一种代替dynamic_cast的模版RTTI实现

在arch框架中,存在这样一种场景:一个IO Channel上顺序注册了一组handler,分别处理不同类型的事件消息,则当某个事件消息到达时,需要依次判断handler是否能处理该类型的消息。(参考自Jboss-Netty的设计)。这类操作是在网络服务是进行,执行次数最多,因此需要考虑一个高效的实现。
最初的实现是模仿Netty中Java的实现,用dynamic_cast判断(Java中是InstanceOf判断),但用dynamic_cast总觉得不太优雅,性能可能也存在问题(参考附注)。
最近有些空闲时间,重新思考了下这个问题,用了另外一种方法代替dynamic_cast实现
1. 注册handler接口方法用模版方法代替,将该携带类型信息的handler注册到一个和该类型相关的模版类中,如红色框部分。当然,在handler析构时需要去注册    image
image
2. 和该类型相关的模版类用静态set保存注册信息;
image
3. 判断事件类型与handler相关联信息,则可调用该模版类的静态方法。时间复杂度为set的实现决定。这里用tr1::unordered_set保存指针,时间复杂度为O(1)。 从理论上应当比用dynamic_cast快的多。(测试发现新方案较dynamic_cast实现快60%以上)
附注:
1. Google的编程规范强烈建议不要在产品代码中使用dynamic_cast。
2. 据一份评测报告显示,一次dynamic_cast可能消耗数十微秒,这个评测待验证。数十微秒显然太慢了。

2012年4月22日星期日

仲宣楼头,千丈白发,怅盘桓而不能去

 读杜诗《短歌行赠王朗司直》有感

2012年4月7日星期六

大量短连接的Server中的惊群问题

最近继续完善自己的多进程非阻塞网络服务框架arch,对于所谓的惊群(Thundering herd)的问题,查看了在这个问题上nginx源码以及其它一些非开源代码,公司内部代码中的处理方式,有以下的思考:

惊群问题(Thundering herd problem),wiki上的解释简单说是“大量处理进程/CPU阻塞在一个调用上,由于某个触发条件导致都被唤醒,但只有一个进程能继续处理,其它均失败”。惊群现象会浪费较多的CPU。这种现象可以出现在很多场景中,以下讨论的具体场景为处理大量短连接的Server中的惊群。

这种场景以Web Server为典型,一般有多个进程/线程同时监听80端口(poll非阻塞IO方式),则当有一个客户端连接上来时,所有的等待者均被唤醒,继而都调用accept处理,但只有一个能成功,其它均失败。

目前较多的实现是基本不考虑惊群问题,如检查的第一个公司内部实现代码,lighthttpd。Redis因为是单进程单线程的实现,故没有考虑此问题。

Nginx是用进程锁的方式解决该问题,即确保在某一个时刻,只有一个进程监听端口处理接收的连接;后续通过比较复杂的手段在多个进程间切换监听工作达到负载均衡的效果,仍然用到进程锁。

 

Nginx的实现对于我来说过复杂了。我在arch中用了一个较为简单方式解决这个问题。

arch是一个多进程的框架实现,故这里先讨论多进程模型下的一种较简单的解决方法:

1. 一个主控进程(Manager)监听指定端口,将FD加入到非阻塞IO集合中,用类poll方式监听(select/poll/epoll)

2. 启动N(N=CPU个数)个工作IO进程(Worker),建立Worker和Manager之间的通信连接(Linux下只能用UnixDomainSocket, 某些Unix可用Stream管道)

3. 以上是进程初始化时的工作,当一个连接发生时,Manager接受此链接,马上将该连接的文件描述符发送到一个Worker进程(同时关闭文件描述符),马上返回到自己的等待状态等待处理下一个连接;(这里的选择Worker进程即是复杂均衡的判断逻辑,一般来说轮询选择就可以达到较好的平衡状态)

4. Worker进程接收到文件描述符后,加入到自己的非阻塞IO集合,读取数据处理业务逻辑

同Nginx的实现一样,arch也避免了惊群现象,不同之处在于arch用到了进程间文件描述符传送,避免了复杂的锁实现;而Nginx用了进程锁,避免了文件描述符传送的系统调用;后续会比较一下两者性能区别。

 

多线程模型下较为简单,只需要采用类似Nginx的锁方式,结合arch的manager+worker模型:

1. 启动Manager线程,监听定端口,将其加入到非阻塞IO集合中,用类poll方式监听(select/poll/epoll)

2. 启动N(N=CPU个数)个工作IO线程(Worker)

3. 当一个连接发生时,Manager接受此链接,马上将该连接的文件描述符交给一个Worker线程马上返回到自己的等待状态处理下一个连接;(这里的选择Worker进程即是复杂均衡的判断逻辑,一般来说轮询选择就可以达到较好的平衡状态)

4. Worker线程接收到文件描述符后,加入到自己的非阻塞IO集合,读取数据处理业务逻辑。

不过,对于编写C/C++的Server程序来说,多线程应当是尽量避免的。这倒是题外话了。

2012年2月4日星期六

畏友

苏浚《鸡鸣偶记》: "道义相砥,过失相规,畏友也;缓急可共,死生可托,密友也;甘言如饴,游戏征逐,昵友也;利则相攘,患则相倾,贼友也。"

以之观“方韩之战”,某些“公知”以及“队友”的言论明显只能归为”昵友”之列,不过倒还未堕落至“贼友”之属。唯有立场,罔顾是非,这一类人还侈谈“德先生”,若变革后的中国是这一类人当道,危险。

最近颇对某些“公知”渐起警惕之心,以上志之。

《古今笑》一则

读《古今笑》,谬误部第五中开篇第一则颇为有趣,说的是乡下人经常以讹传讹,将寺庙,神像之原名附会为一些可笑的名称。如谐音“杜拾遗”变为“杜十姨”,“伍子胥”称为“伍髭须”;“文宣王”因为年代久远只余一“王”以及“宣”字中的“一”字,故被附近的和尚附会为“一字王佛”。及乡下人认为“伍髭须”没有老婆,而将“杜十姨”移到“伍髭须”相配,更是令人绝倒。

另外从这则里也得到一个典故:吴王夫差在伍子胥被迫自杀后,以鸱夷盛伍子胥尸沉之江中,故称伍子胥为“鸱夷子”。《史记·越王勾践世家》也记载范蠡”变姓名,自谓 鴟夷子皮”,可能与伍子胥有一些关系。