小武哥的博客 http://www.wuzesheng.com 左手程序右手诗 Sat, 23 Apr 2016 10:23:16 +0000 zh-CN hourly 1 https://wordpress.org/?v=4.9.3 《创业维艰》(Hard thing about hard things)读书笔记 http://www.wuzesheng.com/?p=3152 http://www.wuzesheng.com/?p=3152#respond Sat, 23 Apr 2016 10:23:16 +0000 http://www.wuzesheng.com/?p=3152 创业维艰害怕并不代表没有勇气,真正的行动才是最重要的。一个人究竟是英雄还是懦夫,由行动决定。

无论你是谁,你的一生都需要两类朋友。第一类是当你遇到好事时,你可以打电话与之分享喜悦的朋友。他的喜悦不是那种蒙着羡慕、嫉妒面纱的虚假喜悦,而是发自内心的真诚喜悦。他会比好事发生在他自己身上更高兴。第二类是当你身陷困境时,你可以打电话与之分担、向其倾诉的朋友。

如果我们不能公平、公正地对待那些即将离开公司的人,那些留下的人就永远不会再信任我了。

我继续前进,追求唯一的完美方向,不能因为恐惧而迷失方向。

每当大公司打算实施某一计划时,该计划总会落到某个人身上,而此人却极有可能延误整个计划。如果此人是工程师,他也许会因为等待上面的决策而踌躇不前;如果此人是管理者,他也许会因为自己无权做出关键性的购买决定而犹犹豫豫。这些看似微不足道的踌躇和犹豫很可能会造成致命的延误。

研发出好产品是创新者的职责,而不是客户的任务。客户只知道根据对现有产品的体验来判断自己想要什么。创新者虽然可以考虑到所有可能的因素,却往往要做出和自己所了解的事实相悖的举动。因此,创新是知识、技能和勇气的结合体。有时,只有创新者才有勇气忽略那些事实数据。

对待确定或不确定的问题,我们有几种不同的方法,比如数学上的微积分方法和统计学方法。在一个确定的世界里,微积分方法占主导地位。通过这种方法,你可以对具体事物进行精确计算。向月球发射火箭时,你必须精确计算出它的飞行轨迹。这和你反复发射火箭,然后一步一步理出头绪的做法完全不同。是将火箭发射到月球?还是发射到木星?它会不会在太空中迷失方向?20世纪90年代,很多公司的行事风格都是只管发射,不管登陆。但在未来那个不确定的世界里,占主导地位的却是可能性和统计学方法,世界会因此具有意义。钟形曲线和随机漫步界定了未来的面貌。标准的教学观点是,高中应该取消微积分课程,代之以统计学课程,因为统计学课程非常重要和实用。一直以来,都有一股强大的思想转变潮流,即统计学思维方式将会推动未来的发展。

创业公司的CEO不应该计算成功的概率。创建公司时,你必须坚信,任何问题都有一个解决办法。而你的任务就是找出解决办法,无论这一概率是十分之九,还是千分之一,你的任务始终不变。

实话实说就是建立信任的关键。

只要有足够多的眼睛,就可让所有问题浮出水面。

健康的企业文化就像过去的路由信息协议:好事不出门,坏事传千里。

只有经营过公司,你才会体验到那种巨大的心理压力,才不会过于乐观。作为CEO,你要勇敢面对压力,直面恐惧,实话实说。

一旦决定裁员,那么必须尽快执行。如果走漏消息,就会横生枝节,麻烦不断。

话是说给那些留下来的人听的。这些人会非常关心你对待他们同事的方式。你裁掉的员工之中,有很多人都和留下来的人关系亲密,因此,你一定要给予他们足够的尊重。毕竟,公司还要向前发展,因此你必须把握尺度,不要过度表达歉意。

招聘高管时,看重的不是对方的长处,而是对方没有弱点。

所谓合适,是指这个人曾有令其他规模相当的公司迅速壮大的成功经验。

解聘面谈关键点:
1. 原因要清楚。你对解聘原因已经艰难地思考过很长时间,因此不要含糊其词或避重就轻,必须让他们清楚你要解聘他们的原因;
2. 说话要果断。不要给讨论留下悬念。这不是一次业绩考核,而是一次解雇行为。要使用诸如“我已经决定”而不是“我认为”这样的措辞;
3. 确定解雇补偿金区分方案。一旦高管听到解雇消息,其关注的重心就会从公司及其相关问题转移到自己和家人身上。因此,你要为解雇补偿金区分方案提供具体细节。

向公司宣布解聘高管消息的正确顺序是:1.该高管的直接下属,因为他们所受影响最大;2.其他高管,因为他们需要就此事回答一些问题;3.公司其余员工。

与其将所有的心思用来哀叹自己的痛苦,还不如努力去寻找一种看似不可能的出路,令自己摆脱目前的困境。不要花时间去懊悔过去,要将所有的时间花在自己可以做的事情上,因为说到底,没人会在意,只要好好经营公司就行了。

我们要依次管理好人、产品和利润。”话虽简单,但意义深远。三者之中,管理好人是最难的,管不好人,其他两项就无从谈起。管理好人意味着公司应该提供一个良好的工作环境,但事实上,大多数工作场所远远称不上良好。当组织规模扩大时,重要工作可能被人忽视,最勤奋的工人可能被最出色的政客所遮蔽,各类繁文缛节可能会扼杀创造力,让一切变得毫无乐趣。

《格鲁夫给经理人的第一课》(High Output Management)书中写道:“大多数管理者似乎都觉得,培训员工这件事应该让其他人来做,但我却坚信,管理者应该亲自去做这件事。”

好的产品经理极其了解市场、产品、生产线和竞争情况,凭借自己丰富的经验和充分的自信开展管理工作。好的产品经理是产品的CEO。他们勇于承担全部责任,以产品的成功与否来衡量自己。他们必须确保产品、时间,以及所需要的一切正确无误。好的产品经理对周围形势十分清楚(公司、营收资金、竞争等),为了获得成功,他们主动制订并执行计划(从不推辞)。差的产品经理总有一大堆借口,如资金不足、项目经理无能、微软研发这项产品的工程师比我们多10倍、我劳累过度了、没人给我指示等。我们的CEO从不会找这些借口,产品经理也不应该拿这些当借口。

帮助新人尽快融入公司:
第一,促使他们积极创造。每月、每周,甚至每天给他们制定目标,确保他们做出相应的贡献;
第二,确保他们明白自己的职责所在;
第三,把他们放入集体。

著名励志大师托尼·罗宾斯所说:“如果不知道自己想要什么,你实现愿望的可能性就微乎其微。”

招聘的陷阱:第一,凭外表和感觉聘人。第二,挑选与众不同的人才。第三,看重的是应聘者身上没有弱点,而不是其长处。

金无足赤,人无完人。因此,招聘时要看重应聘者的长处,而不是其身上没有弱点。

想知道自己需要什么样的人才,最好的方法是在该职位上亲自体验一番。

你心里要清楚自己对加入公司的人有什么期许。

有时候,企业领导者只需言简意赅地表明态度,不一定非得提出具体的解决办法。

多年的管理经验告诉我,主管可以通过努力提高他的业务能力和工作表现,可一旦失去人心,那就回天乏力了。

最能激发员工积极性的做法莫过于让他们怀揣使命感去工作,让他们相信这份崇高的使命值得他们把个人抱负暂放一边。

个别员工私心重也许没什么太大危害,但如果你把公司的重要部门交给那些野心家来打理,公司就会危机重重。

一个团队内部无论哪个层面出现了滥竽充数的人,他们都会像蛀虫一样影响其他成员,最终使得能力出众的人也渐趋平庸。

只有聪明还远远不够。出色的员工同样还要能吃得了苦,担得住事,并且善于和团队成员和睦共处。

能否在适当的时候引入经验丰富的人,往往是决定公司成败的关键。

聘请资深人士加盟新创业的公司,有点儿像运动员为提高比赛成绩服用兴奋剂。如果使用得当,你有可能刷新纪录;如果使用不当,你就会一败涂地。

从4个方面科学合理地衡量主管们是否出色地完成了工作:
1. 参照标准检验结果。一旦设定了高标准,你只需参照标准来检验主管们的工作成效;
2. 管理能力。即便该主管出色地完成了岗位目标,他也未必能带出一支实力雄厚的富有凝聚力的团队。所以,就算他能完成任务,对其管理能力的考查也必不可少;
3. 创新能力。主管们极有可能为实现眼前目标而放弃长远打算;
4. 合作能力。合作也许是形势所需,而非心甘情愿。但是作为主管,你必须要善于沟通,能有效地与其他人协调。因此,也要从这个层面对他们进行评估。

CEO的工作就是明确奋斗目标,然后让全公司齐心协力地朝着这个目标前进。推行适宜的企业文化有助于你在一些重要的领域取得长足的进展。

想让人们交流想法,最好的办法是把他们交给同一个上司,让他们待在同一个部门。

你必须时常对手下管理人员的工作表现进行评估,这是你身为CEO的分内之事。

在一定程度上,管理能力是后天掌握的一种技能,而不是先天具备的禀赋。

每个季度开展一次针对管理人员的全方位评估。

专注于目标,然后力求实现,不再为过去曾有或者将来可能会有的错误而担心、懊恼,这也许是我从企业家的角色中获得的最大领悟。

对CEO来说,最理想的态度是既要雷厉风行,又要保持理性。他应该果断出手,快刀斩乱麻,避免让自己被负罪感所奴役。如果能甩掉情绪对自己的困扰,对问题的重要性做出理智的判断,员工以及他本人都会以良好的状态投入工作。

智慧与勇气。我的CEO生涯告诉我,在面临那些至关紧要的问题时,老天考验的是我的勇气,而不是我的智商。

与靠自己的判断力做出的决定相比,在多数人影响下做出的决定能为你带来更好的社会评价。

你每做一次艰难而正确的决定,勇气就会增加一分。相反,你每做一次轻松却错误的决定,怯懦就会多出一分。作为CEO,你的公司是勇往直前还是畏首畏尾,完全取决于你的选择。

管理公司所必需的两项核心技能:第一,目标明确,知道自己该做什么。第二,能带动全公司去实现这个目标。

领导才能是那些可以衡量一个领导者基本素质的因素:有多少人愿意追随他,有哪些人愿意追随他,追随他的人都属于什么层次。

真正出色的领导者会营造一种以员工为中心的工作氛围。这样的氛围往往会造就奇迹:众多员工凭借一种主人翁精神,一心一意地为公司做贡献。

“三明治反馈法”的基本概念是,如果你在一开始先表扬(第一片面包),人们会更容易接受你的反馈。接下来给出那些令他们不快的信息(批评),最后再提醒他们你有多看中他们的优点(第二片面包)。“三明治反馈法”还可以使你的反馈对事不对人,因为你在一开始就表达了对他的肯定,而这是一个非常关键的反馈信息。

发展蓝图不同于使命宣言,不是三言两语就能概括的。它就像一个故事,只要有必要,你可以一直讲下去,但是这个故事必须得有吸引力。一家没有故事的公司往往会缺乏战略性的发展规划。

伟大的决策往往来自那些集智慧、理性和勇气于一身的精英式CEO。

确保员工队伍的品质是管理公司的关键。

问责制:努力程度,承诺,结果。努力程度这是比较容易考量的一个因素。要成为世界一流的大公司,一流的工作态度必不可少。假如有人在工作中敷衍了事,不尽最大努力,那就必须要受到处罚。承诺许多经营得当的公司都会有这样一些管理宗旨:勇于承诺,兑现承诺。诚然,如果你参与了某项任务但又没有按要求完成,你会让每个人都大失所望,而这种失望情绪是极具传染性的。要求人们对承诺负责任,这是确保工作顺利完成的一个重要因素。兑现承诺的难度系数有高有低,因此问责的程度也会有相应变化。写一份市场宣传资料或是发一封电子邮件与完成一个软件项目绝不是一码事。如果谁完不成前面这项任务,你必须严肃处理。而后者则可能涉及计算机科学中的根本性问题,情况要复杂得多,因此你必须审慎对待结果根据结果确定问责程度是一个比较复杂的问题。

在高科技产业中,你很难未卜先知。平庸与杰出之间的差距往往就源于你的态度,源于你是否放手让员工大胆创新,不折不扣地实行问责制。责任固然重要,但也并不是唯一的重点。

没有一流的团队,就无法创建一流的公司。

高标准要求员工。在雇用某个人时,你并不了解他的全部。虽然这会让你觉得有些尴尬,但是为适应市场需求和激烈竞争,调整并提高你对他的要求是完全合乎情理的。
学会平衡。在一开始花费大量的时间来指导一个副手很正常。但是,如果你发现自己还和聘请或提拔这个副手之前一样忙碌,那就说明他的工作不称职。
你是CEO,不负责培养人才。我从CEO生涯中总结出的最令我沮丧的教训就是,我不可以做副手们的辅导员。他们所在的工作岗位要求他们基本上能独当一面。和我以前做总经理时不一样的是,我现在没有时间去从头培养一个副手。对于公司中的其他岗位来说,手把手地传授经验是可行且必要的,但对于管理层人员却并非如此。如果你的副手总是需要太多的指导和培养,他就是不合格的。

在旧岗位上实现的辉煌不一定能直接转换为新岗位上的成就。事实上,人们往往难以胜任新工作,原因就是他们延续了过去的工作方式,没能及时调整到新的状态中来。

我们走在同一条路上,穿的却是各自的鞋。我们住在同一栋楼里,看到的却是不同的风景。

最好的企业家只选择最好的风投公司进行合作。

]]>
http://www.wuzesheng.com/?feed=rss2&p=3152 0
聊聊创业早期的人才招聘 http://www.wuzesheng.com/?p=3143 http://www.wuzesheng.com/?p=3143#respond Sun, 03 Apr 2016 13:00:17 +0000 http://www.wuzesheng.com/?p=3143 创业是一件很不容易的事,各方面都不容易,招人尤其不容易,组建一个好的团队,是创业成功的基石。棒米从成立到现在,快一年半的时间,团队成员从最开始的两个人,到了现在的二十多个人,这期间有三位同事离职,一位是被我们主动Fire掉的,另外两位是因为各种原因,主动离职的。公司在招聘的岗位有不少,这一年多的时间也面了很多人,在这个过程中,自己也反思了很多,今天这篇文章就把这一年多在招聘过程中的一些心得体会做个记录。希望能够抛砖引玉。
1

从熟人下手
创业公司,刚开始招到的最靠谱的成员,大多都是创始人自己所熟悉的朋友、同学等。因为早期的创业公司,跟已经成熟的大公司比,各方面都没有优势,这时候要招到靠谱的人,就只能从熟人下手。你跟熟人讲情怀、讲梦想,因为有你们多年的信任基础做背书,你只需要让他能够认可你所做的事就够了。如果你去找陌生人,首先要解决的就是信任问题,人与人之间最难的就是建立信任,越聪明、越牛的人之间建立起信任越不容易。我们早期的成员,大都是我和我合伙人以前的同学或者朋友,大部分也都比较靠谱,到现在大都成了能够独档一面的骨干。
关于熟人招聘,还有一些坑需要注意,这也是我们所亲身经历的。对于以前没有一起经历过事情,交往还不够深入的熟人,要格外小心。人与人之间相处,没一起经历过事情,很难把对方看得比较清楚,而且往往比较容易被刚开始比较好的印象造成错觉。结果在一起共事了之后,发现对方不是以前认为的那样,而且处理不好,还容易影响朋友关系。所以,对于熟人招聘,如果不是特别熟、知根知底的人,还是走正常的招聘流程,多让不熟悉他的团队成员来面试考查,这样会更加客观、稳妥一些。

独档一面的第一位
“麻雀虽小,五脏俱全”,有这句话来形容创业公司,再恰当不过了。创业公司,虽说人不多,但是各种岗位都需要有人才行。前期基本是一个人一个岗位,或者一个人兼多个岗位,因此前期成员的招聘都非常重要。岗位第一位成员的天花板,基本上决定了该岗位的高度。我们一直坚持着招最靠谱的人,做最靠谱的事的原则,大部分情况下我们坚持的都很好,所以前期的团队成员大部分也都非常OK。但还是有一些做得不好,不尽如人意的地方。我们之前招过一位产品经理,她的教育背景、工作背景都很不错,面试过程我们都一致认为她人很聪明、思路很清楚。但是,她的缺点是她以前是从事管理咨询工作的,完全没有互联网产品的经验。当时我们面试下来,我们觉得只要人足够聪明,学习能力OK,肯定没问题的。但是,到后来我们发现我们错了。产品经理本身是一个对行业敏感性、创新性和积极主动性要求非常高的岗位,我们这位同学她对于比较明确、有规则的事情(比如日常的项目管理之类)都没有问题,学习和上手都很快,但是致命的问题是对于产品的想法太少,到后来产品经理基本就成项目经理了。到后来这位同学主动离职了。这个事情对我的触动很大,我深刻的体会到,对于创业公司而言,岗位第一人的相关行业从业经验是多么的重要。因为对于创业公司而言,时间成本和试错的机会成本太高了。

非创始人熟知领域的岗位招聘
我和我的合伙人,一个是技术背景,一个是销售背景。对于技术类的招聘,我能够清楚的知道怎么样去衡量一个人的技术水平,以及候选人是否是我们想要的人,这个是我所擅长的。对于销售类的招聘,我的合伙人可以把握的比较好,但目前我们这方面招的还不多,所以问题也不大。但是,还有很多岗位,是在我们以前所熟知的领域之外的,比如运营、市场、产品等等。招聘这些岗位,最容易出现的问题就是,我们能够从基本面去判断这个人怎么样,但是对于候选人的专业性,我们很难做比较准确的判断。这块目前我们也没有解决的特别好,但是因为面了比较多,我自己得出的结论就是,在面试这类候选人之前,一定要通过各种渠道弄清楚,对应的岗位需要什么样的专业技能,如何在面试过程中考查这些技能,这个也是我们努力做到的。另外,候选人的基本面也是非常重要的一个因素,包括教育背景,以往的工作背景。

项目进度与招聘Bar之间的平衡
去年底,我们iOS这边由于之前谈好的一个比较Senior的同学因为家庭的原因,不打算过来了,直接给我们造成的后果就是iOS在很长一段时间内没人做。12月份我们要做正式的公测了,发了公测邀请后,通过收集回来的用户报名信息发现80%以上的用户是iOS。这下我们傻眼了。虽说我们一直在招,也面了不少,但是真正好的太少了,几乎没有。那段时间真是压力巨大,睡觉都不安稳。后来面了一个,感觉可以做,但是肯定是没有达到之前我们一直坚持的Bar,肯定是不能独档一面的。当时犹豫了一下,最终在项目进度和Bar之间做了平衡,招了这位同学进来。进来之后,那段时间短期的问题是得到了解决,但是后来这位同学因为觉得压力大,也离职了。回过头来反思这个事情,很难说当时我的决定是正确的、还是错误的。但是,要我再选择一次,我一定会坚持我们的基本原则,尽最大的努力招最好的人,把事情做到最好。因为在过去的几个月里,iOS这边后续的进展和中间做的一些特性的质量,都很难达到我们的预期,这期间我自己也投入了很多时间,一些核心的模块的逻辑我亲自做。这段时间真是心力交瘁。

专业的人做专业的事
前面我也提到了,创业前期很可能是一个人身兼数岗。我们的团队也不例外。我们的新媒体运营岗位刚开始一个同学做得不太好,被我们Fire掉了。后来这个事情分别由其它岗位的同学兼任过一段时间,这段时间我们输出的内容也好,包括我们对相关热点事件的报道也好,做得都很一般,我们一直都在找问题到底出在哪里。再到后来,我们招了有比较成功的运营过新媒体的同学进来,入职没多久,输出的内容和策划的活动,都做得很棒。还有一些例子,我们店铺运营的同学,以前没有推广的经验,所以我们店铺这边的推广,钱花了不少,但是效果比较一般。还有很多例子。从这些例子,我们得出的结论是,一定要找专业的人做专业的事情。很多人对于自己不熟悉的领域的学习成本都是很高的,这对于创业公司而言是不合适的。可能等公司成熟了之后,可以让一些没有相关专业经验的人做一些尝试,但是创业早期,一定要术业有专攻,尽最大可能让每个人发挥自己的优势,只有这样,才能高效的把事情做出来、做好。

以上是最近的一些思考,希望能够有机会跟同行朋友多多交流。
——————————————————————————————–
最后打个广告,我们现在有大量的岗位在招聘,欢迎有梦想、爱学习的大牛加入我们团队,详情看这里:棒米招聘

]]>
http://www.wuzesheng.com/?feed=rss2&p=3143 0
再出发 http://www.wuzesheng.com/?p=3121 http://www.wuzesheng.com/?p=3121#respond Sun, 06 Mar 2016 13:32:16 +0000 http://www.wuzesheng.com/?p=3121 今天无意间打开博客,发现上一篇博文是近两年之前的事了,已是太久没有写了。自从决定创业,开始走上创业之路,整天忙于各种事务,像以前那样静下心来细细思考、总结的时间越来越少了。一直以来,都觉得博客对于我而言,是一个很不错的记录心境、梳理思绪的地方。是时候该好好理理了,希望今天这篇博文能起个好头,让自己每隔一段时间,能静下心来好好深思一番,既是记录创业路上的心路历程,也是为自己更好的走下一步理清楚思路。

过往在小米的三年,对于我的职业生涯而言,是到目前为止,是最为难忘三年。不单是因为经历了小米从40亿美金到450亿美金的快速成长,更为重要的是,跟一个中国IT行业最为优秀的技术团队(没有之一)一起,把小米的基础架构(云计算、云存储)平台,从无到有做起来,并且支持了小米如此快速的业务发展。对于一个技术人员来讲,这是莫大的幸福。

离开小米,创办棒米(www.bongmi.com),在我的职业生涯里是最为重要的历程碑事件。去年11月初,从北京只身回到阔别多年的杭州,个人角色的转变、职责的变化,也让我感受到了很多促不及防的压力。以前在腾讯、小米这样的大公司的工作经历,确实给我在早期组建、管理棒米团队的过程中,带来了非常大的帮助,但是这些还是远远不够,每天都会面临很多新的问题。一直坚信一个理念,遇到问题,第一时间解决问题是关键,办法总会比问题多。所以,即使每天会遇到各种各样的问题,依然保持着一颗乐观的心,坚信一切都会越来越好。这些个是支撑我一周工作七天,每天工作12个小时以上的动力。

创业,对于任何一个人来讲,都不是一条容易的路,既然选择了,就得承受更多,就得付出更多的努力。今天这篇文章叫《再出发》,一是想,以此为起点,把以前写博客的习惯再培养起来,培养一个更好的反思与梳理、总结的习惯;二是想,棒米对于我来说,是一个全新的起点,以此出发,做一件真正能够创造一定社会价值的事情。在做棒米的过程中,肯定会遇到各种各样的坎坷,通过反思与梳理,把这些坎坷能够记录下来,能够让自己有更加深圳的理解,同时也希望能够对后来者有所启发。

愿今天是个好的开始。

]]>
http://www.wuzesheng.com/?feed=rss2&p=3121 0
Reed Solomon Erasure Codes http://www.wuzesheng.com/?p=2902 http://www.wuzesheng.com/?p=2902#respond Sat, 12 Jul 2014 04:24:55 +0000 http://www.wuzesheng.com/?p=2902 [latexpage]
上一篇文章《Finite Field Arithmetic》介绍了有限域上的运算,理解有限域上的运算,是理解erasure编码的基础。今天这篇文章就来介绍一下erasure编码。在分布式存储系统中,通常会通过多副本的方式来保证数据的可靠性,但是多副本带来的成本问题也是显而易见的。在类HDFS这样的系统中,通常数据都会保留三副本,三副本可以容有两副本故障的场景,但同时成本也是一副本的三倍。如何在保证同等的数据可靠性的前提下,减少副本数,降低成本,是分布式存储系统中很重要的一个课题。Erasure编码正是用来解决这个问题的,它能将副本降到1.x的同时,保证同等的数据可靠性保证。本文会以最常用的Reed Solomon erasure编码为例来介绍。

基本思想

erasure-codes
如上图所示,我们总共有$n$块盘,其中$k$块用来存放数据,m块用来存储erasure编码($k+m=n$),在上面的$n$块盘中,任意坏$m$块,都可以通过erasure编码将其余的恢复出来。也就是说,通常$k+m$的erasure编码,能容$m$块盘故障的场景,这时候的存储成本是$1+m/k$, 通常$m

Reed Solomon算法

Reed Solomon算法的核心思想包括三个部分:
1. 利用范德蒙德(Vandermonde)矩阵,通过数据块计算编码块
2. 利用高斯(Gaussian)消元法, 恢复损坏的数据块
3. 为了方便计算机处理,所有运算都是在伽罗华域Galios Field)$GF(2^w)$的基础上进行的

下面是Reed Solomon算法的具体步骤($k+m$的Reed Solomon, 处理的字长为$w $):
1. 选择一个合适的字长$w$, 保证$2^w>k+m$
2. 构造$GF(2^w)$上的对数表$gflog$和反对数表$gfilog$
3. 取$m*k $的Vandermonde矩阵F, 其中$F_{i,j}=j^{i-1}$, $(1 < = i <= m, 1 =< j <= k)$ 4. 用F去按字(w)乘数据块就会得到对应的编码块$FD=C$ 5. 如果$e$($e<=m$)块数据损坏,可以通过下面的方式恢复: $A=\begin{bmatrix}I\\F\end{bmatrix} $, $E=\begin{bmatrix}D\\C\end{bmatrix}$ 我们知道$AD=E $, 在$A $和$E $中消去坏块对应的行,得到$A^' $, $E^' $, 满足$A^{'}D=E^'$ 通过高斯消元法,便可以解出$D $; 另外如果是编码块中有坏块,可以直接通过$AD=E$算出对应的坏块。 关于Gaussian消元法: 读者朋友可以回忆一下小时候学的n元一次的方程组的解法,它所用的方法其实就是高斯消元法。由n个独立的方程组成的n元一次方程组,有确定的唯一解。在erasure编码中,$k$个数据块相当于是$k $个元,我们有$k+m$个独立的一次方程,这$k+m$个方程中,取任意$k$个方程组成方程组,我们都可以通过高斯消元法解出$k$个元,这便是erasure编码中真正的秘密。 关于Vandermonde矩阵: 为了保证"n元一次方程组"有解,我们需要保证每个方程都是"独立的", 而用Vandermonde矩阵用来做为方程组的系数,便可以保证这一点。这就是erasure编码中为什么用Vandermonde矩阵的原因。下面是Vandermonde矩阵的定义: \[ V=\begin{bmatrix} 1 & 1 & 1 & \cdots & 1 \\ x_0 & x_1 & x_2 & \cdots & x_{n-1} \\ x_{0}^2 & x_{1}^2 & x_{2}^2 & \cdots & x_{n-1}^2 \\ \vdots & \vdots & \vdots & \vdots & \vdots \\ x_{0}^{n-1} & x_{1}^{n-1} & x_{2}^{n-1} & \cdots & x_{n-1}^{n-1} \end{bmatrix} \] 在erasure编码的具体应用到分布式存储系统中时,上面的$x_i $都是在$GF(2^w)$中,所有的运算都是在$GF(2^w)$上做的。

Cauchy Reed Solomon算法

Cauchy Reed Solomon跟普通的Reed Solomon的最大的区别就是把Vandermonde矩阵换成了Cauchy(柯西)矩阵。通过《Finite Field Arithmetic》我们知道,在$GF(2^w)$中的运算需要查对数表和反对数表来进行,并且随着$w$的增大,查表的开销也会增大。在Cauchy Reed Solomon中,通过使用Cauchy矩阵,可以将$GF(2^w)$上的四则运算做如下转化: 加法->异或,乘法->与,我们知道$GF(2^w)$上的减法都可以转化为加法,除法都可以转化为乘法。因此,通过Cauchy Reed Solomon,可以将$GF(2^w)$上的四则运算都转化为“异或”或者“与”运算,这对于计算机来说,更为友好,速度更快。
Cauchy Reed Solomon算法的过程跟普通的Reed Solomon算法的过程基本是类似的,关于如何构造Cauchy矩阵,以及如何把四则运算转化为位运算,感兴趣的同学可以参考[2], 这里不再赘述。

Jerasure and Java-Erasure

再回到算法本身的具体实现上,[3]有各种实现的对比,感兴趣的同学可以参考。总体来说,目前用得比较广泛的是James Plank实现的Jerasure, Jerasure本身是由纯C语言实现了,为了应用到Java语言实现的系统中(HDFS),前段时间封装了一个wrapper library Java-erasure, 该库的用法和接口都非常简单,具体参考github上的README。

参考资料

1. A Tutorial on Reed-Solomon Coding for Fault-Tolerance in RAID-like Systems
2. Optimizing Cauchy Reed-Solomon Codes for Fault-Tolerant Storage Applications
3. Tutorial: Erasure Coding for Storage Applications

————————————-华丽丽的分隔线——————————————————————————————-
下面是广告时间: 本站开通了微信公众号,小伙伴们可以在微信搜索公众号”5GBlog“,或者扫描下面的二维码,关注本站的公众号,第一时间接收本站的最新文章。
qrcode_for_gh_837a2630b93a_258

]]>
http://www.wuzesheng.com/?feed=rss2&p=2902 0
Finite Field Arithmetic http://www.wuzesheng.com/?p=2789 http://www.wuzesheng.com/?p=2789#respond Sat, 28 Jun 2014 05:53:18 +0000 http://www.wuzesheng.com/?p=2789 [latexpage]
在分布式存储系统中,通常通过多副本的方式来保证数据的可靠性。Erasure编码,是分布式系统中用来在保证跟多副本同等可靠性的前提下,减少副本、节约成本的标准做法。有限域(Finite Field)上的四则运算是Erasure编码在分布式存储系统中应用的基础。今天这篇文章介绍一下有限域(Finite Field)及其上面的运算, 后续文章再介绍Erasure编码。

什么是有限域(Finite Field)

大小为$n$的集合,在其上的任何四则运算(加、减、乘、除)的结果仍在这个集合中,表示为$GF(n)$

有限域(Finite Field)有什么样的性质

对于有限域GF(n)中的元素A(0除外), 均存在:
1. $A+B=0$, $B$称之为$A$的加逆, $-A$(Addition inverse, 可以跟相反数据类比)
2. $A*B=1$, $B$称之为$B$的乘逆, $A^{-1}$(Multiplication inverse, 可以跟倒数类比)
由上面的性质可以推论,在有限域上的减法都可以转化为加法,除法都可以转化为乘法

$GF(n)$($n$为素数)有限域上的运算法则

对于$GF(n)$, 如果$n$是素数,可以这样来定义$GF(n)$,及其上的四则运算:
$GF(n) = {0, 1, 2, \dots, n-1}$
加: $A + B = (A + B) \mod n$
减: $A – B = (A + (-B)) \mod n$
乘: $A * B = (A * B) \mod n$
除: $A / B = (A * B^{-1}) \mod n$


下面举几个$GF(3)$的例子:
\[
\begin{center}
\begin{tabular}{|l|l|l|l||l|l|l|l|}
\hline
\multicolumn{4}{|c||}{Addition} & \multicolumn{4}{|c|}{Multiplication} \\
\hline
Add & 0 & 1 & 2 & Multiply & 0 & 1 & 2 \\
\hline
0 & 0 & 1 & 2 & 0 & 0 & 0 & 0 \\
\hline
1 & 1 & 2 & 0 & 1 & 0 & 1 & 2 \\
\hline
2 & 2 & 0 & 1 & 2 & 0 & 2 & 1 \\
\hline
\end{tabular}
\end{center}
\]
下面是减法和除法的例子:
减法: $1 – 2 = 1 + (-2) = 1 + 1 = 2$
除法: $1 / 2 = 1 * 2^{-1} = 1 * 2 = 2$

对于$GF(n)$, 如果$n$不是素数, 用上述取模的方式来定义四则运算就行不通了(比如在$GF(4)$上$3/2$无解)

$GF(2^w)$有限域上的四则运算法则

1. 对于$GF(2^w)$上的加减法,我们可以这么定义:
加法: $A + B = A \oplus B$
减法: $A – B = A + (-B) = A \oplus (-B) = A \oplus B$
从上面的定义,很容易看出,加、减的结果任在$GF(2^w)$中,满足之前的假设

2. 对于$GF(2^w)$的乘法和除法,为了使其结果任在$GF(2^w)$中,我们需要定义一种规则来生成$GF(2^w)$, 然后在其上做运算:

  • 以$GF(2) = {0, 1}$为初始集合
  • 拿集合中的最后一个元素,乘以$x$
  • 如果所得的结果的度大于等于$w$, 则把该结果模上本源多项式(Primitive Polynomial), 所得结果就是下一个元素
  • 重复(2)(3)两步,直到集合中有$2^w$个元素

下面来构建$GF(2^2)$, 即$GF(4)$, $GF(4)$的本源多项式为$q(x)=x^2+x+1$:

  • 初始状态: $GF(4) = {0, 1}$
  • 下一个元素: $1 * x = x$, 度为1, 小于$w$, $GF(4) = {0, 1, x}$
  • 下一个元素: $x * x = x^2$, 度为2, 大于等于$w$, $x^2 \mod q(x) = x + 1$, $GF(4) = {0, 1, x, x + 1} $
  • 此时,$GF(4)$中已经有4个元素了,结束构造

下面来看乘法: (度数大于等于2的需要mod $q(x)$)
\[
\begin{center}
\begin{tabular}{|l|l|l|l|l|}
\hline
\multicolumn{5}{|c|}{Multiplication} \\
\hline
Multiply & 0 & 1 & x & x + 1 \\
\hline
0 & 0 & 0 & 0 & 0 \\
\hline
1 & 0 & 1 & x & x+1 \\
\hline
x & 0 & x & x+1 & 1 \\
\hline
x+1 & 0 & x+1 & 1 & x \\
\hline
\end{tabular}
\end{center}
\]
做如下映射: 0->0, 1->$x^0$, x->$x^1$
然后,依次取每个多项式的系数, 得到如下表:
\[
\begin{center}
\begin{tabular}{|l|l|l|l|l|}
\hline
\multicolumn{5}{|c|}{Multiplication} \\
\hline
Multiply & 00 & 01 & 10 & 11 \\
\hline
00 & 00 & 00 & 00 & 00 \\
\hline
01 & 00 & 01 & 10 & 11 \\
\hline
10 & 00 & 10 & 11 & 01 \\
\hline
11 & 00 & 11 & 01 & 10 \\
\hline
\end{tabular}
\end{center}
\]
再将上述表,转化为十进制:
\[
\begin{center}
\begin{tabular}{|l|l|l|l|l|}
\hline
\multicolumn{5}{|c|}{Multiplication} \\
\hline
Multiply & 0 & 1 & 2 & 3 \\
\hline
0 & 0 & 0 & 0 & 0 \\
\hline
1 & 0 & 1 & 2 & 3 \\
\hline
2 & 0 & 2 & 3 & 1 \\
\hline
3 & 0 & 3 & 1 & 2 \\
\hline
\end{tabular}
\end{center}
\]
下面我们来看几个GF(4)上乘、除法的例子:
乘法: $2 * 3 = 1$
除法: $2 / 3 = 2 * 3^{-1} = 2 * 2 = 3 $

从上面的例子中,我们可以总结出$GF(2^w)$上的乘法法则(除法可以转化为乘法):

  • 将要乘的两个数先转化为$w$位的二进制
  • 将二进制分别都转化为对应的系数基于$GF(2)$的多项式
  • 将两个多项式相乘,相乘的结果如果度大于等于$w$, 将结果mod本源多项式$q(x)$
  • 将上面得到的多项式再转化为对应的二进制
  • 将上面得到的二进制再转化为对应的十进制,所得就是计算结果

下面我们来看一个$GF(16)$上的例子,$GF(16)$上的$q(x)=x^4+x+1$:
\[
\begin{split}
\begin{aligned}
5 * 6 &\\
& = 0101 * 0110 \\
& = (x^2 + 1)*(x^2 + x) \\
& = x^4 + x^3 + x^2 + x \\
& = (x^4 + x^3 + x^2 + x) \mod q(x) \\
& = (x^4 + x^3 + x^2 + x) \mod (x^4 + x + 1) \\
& = x^3 + x^2 + 1 \\
& = 1101 \\
& = 13 \\
\end{aligned}
\end{split}
\]
对于除法: $A / B = A * B^{-1}$,我们可以通过查表得到$B^{-1}$,然后按上面同样的方法来计算

在实际的应用为,用上面的方法来计算还是比较麻烦的,尤其对于计算机程序来说。我们知道对数可以将乘法转化为加法,将除法转法为减法:
乘: $log(A*B) = log(A) + log(B)$
除: $log(A/B) = log(A) – log(B)$
对于上面的式子,我们在两边同时取2的次幂运算:
乘: $2^{log(A*B)} = 2^{log(A) + log(B)}$ => $A*B = 2^{log(A) + log(B)}$
除: $2^{log(A/B)} = 2^{log(A) – log(B)}$ => $A/B = 2^{log^(A) – log^(B)}$
由上面可以推出,我们构造出$GF(2^w)$上的对数表$gflog[i]$的反对数表$gfilog[i]$, 就可以将乘法转化为加法,将除法转化为减法。这里如果$gflog[i] = b$, 那么$2^b = i$; 如果$gfilog[i] = b$, 那么$2^i = b$

如下表所示是GF(16)上的对数表和反对数表:
\[
\begin{left}
\begin{tabular}{|l|l|l|l|l|l|l|l|l|l|l|l|l|l|l|l|l|}
\hline
i & 0 & 1 & 2 & 3 & 4 & 5 & 6 & 7 & 8 & 9 & 10 & 11 & 12 & 13 & 14 & 15 \\
\hline
gflog[i] & – & 0 & 1 & 4 & 2 & 8 & 5 & 10 & 3 & 14 & 9 & 7 & 6 & 13 & 11 & 12 \\
\hline
gfilog[i] & 1 & 2 & 4 & 8 & 3 & 6 & 12 & 11 & 5 & 10 & 7 & 14 & 15 & 13 & 9 & – \\
\hline
\end{tabular}
\end{left}
\]
(注意: 本图中最后一列没显示出来,可以右键在新窗口中打开图片查看)
根据上表,我们来看几个乘除法的例子:
乘: $5 * 6 = gfilog[gflog[5] + gflog[6]] = gfilog[8 + 5] = gfilog[13] = 13$ (跟上面用多项式计算出来的结果一致)
除: $5 / 6 = gfilog[gflog[5] – gflog[6]] = gfilog[8 – 5] = gfilog[3] = 8$

关于对数和反对数表的生成,[3]中有给出具体的C代码,感兴趣的朋友可以查阅[3]。关于本源多项式,感兴趣的朋友可以查阅[2]。关于有限域上的运算中用到的多项式的乘除,感兴趣的朋友可以参考[4]。关于有限域具体是怎么应用到Erasure编码中的,然后又怎么具体应用到分布式存储系统中的,后续会写单独的文章来介绍。

参考资料

1. Finite Field
2. Primitive Polinomial
3. A Tutorial on Reed-Solomon Coding for Fault-Tolerance in RAID-like Systems
4. Finite Field Arithmetic

]]>
http://www.wuzesheng.com/?feed=rss2&p=2789 0
我所理解的Paxos http://www.wuzesheng.com/?p=2724 http://www.wuzesheng.com/?p=2724#respond Sun, 01 Jun 2014 03:21:48 +0000 http://www.wuzesheng.com/?p=2724 Paxos是前段时间刚获得图灵奖的大神Leslie Lamport所提出的,是用来解决分布式系统中的一致性问题的算法。该算法对于分布式系统的重要性,在这里不再赘言。了解过Paxos的朋友应该都知道,要完全理解Paxos不是一件容易的事。本文是笔者在学习Paxos时,用来帮助自己更好的理解Paxos所梳理的一遍文章,希望能够通过通俗易懂的方式,把Paxos理解清楚。

Paxos要解决的问题

我们知道,Paxos要解决的问题,是分布式系统中的一致性问题。到底什么是“分布式系统中的一致性问题”呢?在分布式系统中,为了保证数据的高可用,通常,我们会将数据保留多个副本(replica),这些副本会放置在不同的物理的机器上。为了对用户提供正确的读/写/删/改等语义,我们需要保证这些放置在不同物理机器上的副本是一致,这就是Paxos所要解决的问题。
Paxos-Scenario
如上图所示,Client会将数据写到三个不同的Server上,如何保证写在这三个不同Server上的数据是一致的,这便是Paxos要解决的问题。
对上述问题进一步抽象,我们假设Replica A的初始状态是S0, 然后用户一次操作称为一个OP, 那么任意时刻,Replica A的状态Sn,都是在S0的基础,执行一系列操作{OP1, OP2, …, OPn}的结果,即:

Sn = S0 + {OP1, OP2, ..., OPn}

在此基础上,要保证多个Replica A/B/C一致,我们需要A/B/C有相同的初始状态S0, 然后{OP1, OP2, …, OPn}按相同的顺序执行,那么最终得到的A/B/C的状态Sn就都是一致的。在这里S0一致是比较好做的,重点是保证{OP1, OP2, …, OPn}按相同的顺序执行,这正是Paxos所要解决的问题。具体到把Paxos应用到实际的系统中,我们需要在系统中执行多轮的Paxos算法,每一轮Paxos用来保证每个OP在每个Replica上被正确执行,多轮Paxos执行的顺序保证多个OP执行的顺序。

Paxos算法模型

Paxos-Model
上图是Paxos所抽象出的模型,为了简化说明,省略了Server1和Server3中的一些细节,这里拿Server2为例来进行说明。如上图中所示,每个Server中都抽象出了Proposer, Acceptor和Learner三个角色。对应到第一部分中Paxos要解决的问题,每个OP被抽象成一个Proposal,Proposer用来发起Proposal, Acceptor用来决策是否接受Proposal, Learner用来获取各Acceptor决策的结果。与之对应,Paxos算法也分为三个过程:Prepare(准备发起Proposal, 图中绿线), Accept(发起Proposal并协商接受, 图中绿线), Learn(学习获取接受的Proposal, 图中蓝线)。

Paxos算法

结合上面的图,我们先来看一下Paxos算法,先对算法本身有一个直观的认识,然后再结合后文来进一步理解,Paxos算法分为下面三个阶段:
1. Prepare阶段:

  • Proposer向大多数Acceptor发起自己要发起Proposal(epochNo, value)的Prepare请求
  • Acceptor收到Prepare请求,如果epochNo比已经接受的小的,直接拒绝; 如果epochNo比已经接受的大,保证不再接受比该epochNo小的请求,且将已经接受的epochNo最大的Proposal返回给Proposer

2. Accept阶段:

  • Proposer收到大多数Acceptor的Prepare应答后,如果已经有被接受的Proposal,就从中选出epochNo最大的Proposal, 发起对该Proposal的Accept请求。如果没有已经接受的Proposal, 就自己提出一个Proposal, 发起Accept请求。
  • Acceptor收到Accept请求后,如果该Proposal的epochNo比它最后一次应答的Prepare请求的epochNo要小,那么要拒绝该请求;否则接受该请求。

3. Learn阶段:

  • 当各个Acceptor达到一致之后,需要将达到一致的结果通知给所有的Learner.

简化模型1

Paxos-Simple-Model1
在Paxos算法中,每个Proposer都会发向所有的Acceptor发起Proposal, 上图是一个Proposer向所有Acceptor发起Proposal的过程。我们知道,在分布式系统的环境中,经常会有各种故障,比如网络异常,物理机器故障等。如果Paxos要求所有的Acceptor都时时刻刻都一致,那么只要有一个Accepor故障的话,整个系统将无法正常运转。因此,Paxos这里弱化了一致的语义,这里的一致是指大多数(majority)机器一致,也就是说,一个Proposal如果被半数以上的Acceptor接受,我们就认为该Proposal被接受了

简化模型2

Paxos-Simple-Model2
上图是Paxos中,各Proposer向各Acceptor发起proposal的过程。可以看出,每个Proposer都会向指定的某个Acceptor发起Proposal, 也就是说,每个Acceptor都会被收到多个Proposal。如何保证每个Acceptor最终接受的值是确定的,且保证大多数Acceptor的接受的值是一样的,这是我们面临的问题。

先看这样一个场景,Proposer1/2/3同时向Acceptor1发起Proposal, 多个Proposal在同一个Acceptor不能并行处理,因此要保证这些Proposal在Acceptor1上能被正确处理,需要一把”锁”, 用它来保证每次只处理一个Proposal。在Paxos算法中,采用了一个单独调增的整数epochNo来充当”锁”的角色。每个Proposal都会有一个epochNo,也就是说每个Proposal是(epochNo, value)这样一个元组。

对于如何保证每个Acceptor最终的值是确定的,Paxos是这么做的: 对于某个指定的Acceptor, 如果它之前没接受任何Proposal, 那么它将接受它所收到的第一个Proposal; 如果它之前已经接受了某个Proposal, 后续将会把自己已经接受的Proposal告知给发起Proposal的其它Proposer,请它们帮忙用该值跟其它Acceptor达到一致。

对于如何保证大多接受的值是一样的,我们来看这样几个场景:
1. Proposer1发起的Proposal收到了一半以上的Acceptor接受的应答: 假设Acceptor1/2接受了Proposer1的Proposal, 如果后续其它Proposer发起的Proposal要保证被一半以上的Acceptor接受,那么这些Acceptor里至少包含Accetor1或2中的一个,它们会协助完成让其它Acceptor接受该Proposal的任务。
2. Proposer1发起的Proposal没收到一半以上的Acceptor接受的应答: 假设只有Acceptor1接受了请求,这时候如果Acceptor1在其它Proposer要达成一致的大多数中, 那么它的值也将会在别的Proposer的协助下,让这大多数达到一致;如果Acceptor1不在其它Proposer要达成一致的大多数中,那么其它大多数会自己达成一致。

总结

关于Paxos,有几个比较重要的核心点,需要进一步强调:

  • 1. epochNo, 在Paxos中充当了“抢占式锁”的角色,非常重要
  • 2. 新(大)的epochNo到了之后,旧(小)的就不再生效,它所有的请求都会被拒绝
  • 3. 新(epochNo大)的Proposal, 要认可旧值,帮助促成旧值达到一致

另外,Paxos因为会有多个Proposer发起Proposal, 新的epochNo能抢占旧的,这样就会有活锁(Liveness)问题,通常的解决方案是选Leader, 由Leader来发起Proposal,Leader会维护一个Lease, Lease过期的话,需要选举新的Leader。Leader是保证和加速Paxos进度的重要手段,通常在具体的工程实践中,都会选Leader。

参考资料

1. Paxos Made Simple
2. Paxos Made Live

]]>
http://www.wuzesheng.com/?feed=rss2&p=2724 0
Linux System and Performance Monitoring http://www.wuzesheng.com/?p=2665 http://www.wuzesheng.com/?p=2665#comments Sun, 06 Apr 2014 02:16:08 +0000 http://www.wuzesheng.com/?p=2665 写在前面:本文是对OSCon09的《Linux System and Performance Monitoring》一文的学习笔记,主要内容是总结了其中的要点,以及加上了笔者自己的一些理解。通过总结,一方面是为了加深笔者自己的理解,另一方面也是希望能对有需要的朋友有所帮助。

做为一名服务器开发工程师,经常会有分析系统性能,解决系统性能瓶颈的需求。通常我们所说的性能问题,不外乎就是CPU/Memory/IO/Network这四个方面,这四个方面每个都有各自独特之处,同时也都是相互关联的。下面就分别从这四个方面展开进行介绍。

CPU

基本概念

1. 内核调度的优先级:在Linux系统中,内核scheduler调度资源包括两种:threads(Process是由threads组成)和interrupt,这些被调度的资源是有特定的优先级的,以下从高到底:

  • Interrupts: Interrupt被设备用来通知内核相关的事件,优先级是最高的
  • Kernel(System) Processes:所有的系统进程都是以仅次于Interrupt的优先级被调度的
  • User Processes: 所有的应用程序都是run在用户态空间,以最低的优先级被内核调度

2. 上下文切换(Context Switch): 线程在运行过程中,CPU时间片用完,或者是被更高优先级的的资源抢占了CPU,该线程都会被放到一个等待队列,等待下一次被调度,这样的一次过程称为一次上下文切换。另外,在用户程序调用系统调用(System call)的时候,也会发生上下文切换(这个也有叫Mode Switch的, 确实跟前面两种情况有所区别)。

3. 运行队列(Run Queue)和负载(Load):

  • Run Queue: 在Linux系统中,每个CPU维护着一个run queue, 里面放着等待被执行的threads, run queue越大,在里面的线程等的时间越长
  • Load: Linux系统提供了1/5/15分钱的load, load是的值指的是当前running的threads数加上run queue中等待被执行的threads数

4. CPU利用率(Utilization):

  • User Time(us): CPU在用户空间运行线程所花的时间的百分比
  • System Time(sy): CPU执行内核线程和中断(interrupt)所花的时间的百分比
  • Wait IO(wa): 所有进程因为等待IO完成而被阻塞,导致CPU idle所花的时间的百分比
  • Idle(id): CPU完全idle的时间的百分比
常用工具

1. vmstat: vmstat是linux下非常强大的工具,它的结果包含了比较全面的CPU和Memory相关的指标,这里只介绍CPU相关的.
下图是vmstat使用时的一个截图:
vmstat-use
下图是vmstat CPU相关指标的介绍:
vmstat-cpu

2. top: top也是linux下比较常用的工具,它除了可以看系统整体的CPU/Memory使用情况,还可以单独看某个进程的每个线程的情况:
下图是top看整体情况(top打开之后,按数字键’1′, 可以展开每个cpu的情况):
top-use
下图是top -p $pid -H看指定进程的(top打开之后,按字母键’c’,可以查看进程的参数详情):
top-use2
3. mpstat:mpstat是一个专门用来查看CPU使用情况的工具,通常用’mpstat -P ALL 2’来查看每个CPU的情况。这里最后面的数字’2’表示的是采样周期,Linux下有很多命令支持这样的参数,像sar/iostat/vmstat/mpstat等,很多资料是给的例子是用1,但在实践的过程中发现,用1的结果常常波动比较大,不稳定,所以在具体的实践中,笔者推荐用2或者更大一些的值,获得的结果相对稳定一些。

经验之谈

关于CPU,有下面一些经验可供参考:

  • Run Queue: 每个run queue最好不要超过3个threads在等待,转换到load, 就是load的值最好不要超过3倍的cpu核数,1倍核数是比较理想的状态,2-3倍是比较饱和的状态,再高就会影响系统正常运行了。
  • CPU Utilization: 推荐的比例是 us 60-70%, sy 30-35%, id 0-5%, 简单可以记us:sy=70:30, 这个是比较合适的比例,如果sy超过30,就会影响系统的正常运行
  • Context Switch: 上下文切换跟cpu利用率是直接相关的,如果cpu利用率符合上面说的比例,那么比较高的context switch是可以接受的

Memory

基本概念

1. Memory Pages: Linux系统中内存是以页(Page)为基本来存取的,默认的页大小是4096Bytes, Linux下内存页可以分为下面几种类型:

  • Unreclaimable – locked, kernel, reserved pages
  • Swappable – anonymous memory pages
  • Syncable – pages backed by a disk file
  • Discardable – static pages, discarded pages

2. kswapd: kswapd是用来保证系统有足够多的free memory的Linux daemon。它监控了内核的pages_high和pages_low这两个值,如果free memory的值低于pages_low, 它就会开始扫描内存并尝试free一些内存页,每次32个页,它会重复这个过程,一直到free memory的值达到pages_high这个值。kswapd在free内存页时,主要有下面几种情况:

  • 如果内存页没有被修改,它会直接放到free list
  • 如果内存页被修改了,而且该内存页是Syncable的,把该内存页的内容写回磁盘,然后把该内存页放到free list
  • 如果内存页被修改了,页且该内存页是Swappable(Anonymous)的,把该内存页写入到swap device, 然后把该内存页放到free list

3. pdflush: pdflush是用来把内存页同步到对应的磁盘文件的Linux daemon. 比如说,一个文件在内存中被修改了,那么pdflush会把它写到磁盘上。当内存页中有10%的dirty页的话,pdflush就开始向文件系统同步这些dirty页。这个阈值可以通过vm.dirty_background_ratio这个内核参数来配置,缺省是10%.

常用工具

1. vmstat: 前面提到的vmstat同样也是用来查看内存使用情况的利器, 下图是vmstat中内存相关指标的介绍:
vmstat-mem
2. top: top同样可以用来看内存的使用情况,包括系统整体的情况,也有单独每个进程的:
下图是系统整体的,其中total是总量,used/buffer/free跟vmstat的cache/buff/free是对应的:
top-mem
下图是单个进程的,其中virt是该进程使用的虚拟内存的大小(memory + disk pages),res是使用的物理内存的大小(only memory pages):
top-per-proc

经验之谈

关于memory, 有下面一些经验之谈:

  • 比较低的free memory大小,表明系统有效地使用了内存;除非是在大量、持续的写swap device
  • 如果系统在持续读、写swap device, 表明系统内存不够了

IO

基本概念

1. Page fault: 当应用程序要访问的数据不在正在使用的memory中的时候,就会发生page fault, 具体有下面两种类型的page faults:

  • Minor(MnPF): 数据在物理内存中,但在Fault发生的时候,还没在MMU(Memory Management Unit)登记,此时发生的Fault为Minor Page Fault.
  • Major(MPF): 数据不在物理内存中,需要从磁盘加载,此时发生的Fault为Major Page Fault.

2. File Buffer Cache: 它是系统发生IO时,系统与磁盘之间的Cache, 主要目的就是最大化MnPF, 最小化MPF。前文中vmstat/top的截图中,buff对应的就是它的大小。
3. Page Type: 前文中从回收的角度对memory page进行了分类,从IO的角度可以分为下面几类:

  • Read Pages:系统从磁盘加载的只读的Page, 这些Page会一直在内存中驻留,一直到系统内存紧张,内核才会将这些Page加入到free list,另做它用
  • Dirty Pages:系统从磁盘加载的Page, 并且做了修改。这些Page会被pdflush同步到磁盘。当系统内存紧张时,kswapd会将这些Page写入到磁盘
  • Anonymous Pages: 不属于某个进程的Page, 不能同步到磁盘。当系统内存紧张时,kswapd会将其swap到swap device, 以此来释放内存

4. 磁盘IOPS计算:

IOPS = 1000 / {((1 / (RPM / 60)) * 1000 / 2)[rotation] + 3[seek] + 2[latency]} 

解释一下上面的公式:

  • rotation: 磁盘旋转时间,1/(RPM/60)是每转一圈所用的秒,*1000是转化为毫秒,/2是平均情况下,需要转半圈
  • seek: 寻道时间,3ms
  • latency: 数据传输时间,2ms
  • 最后用1000/(rotation + seek + latency)就是磁盘的IOPS,正常用的10000RPM的磁盘,算下来约是125 IOPS
常用工具

1. iostat: iostat是用来查看系统IO情况的利器, 下面介绍该工具:
iosat -k -d -x 2的结果:
iostat-2
上图中,比较常用的r/w这两列分别是每秒的读/写请求数,也即IOPS;rkB/wkB是每秒读写的数据量,也即throughput; 最后三列分别是io wait时间,io请求serve的时间,也即io利用率。

经验之谈

关于IO,有下面一些经验之谈:

  • iowait正常情况下应该是0,如果持续非0的话,就说明对应的io设备overloaded了
  • 根据你的磁盘转数,计算它所能承受的IOPS,以此来判断当前的iops是否正常
  • 顺序读和随机读有一定的差异,这个也是要考虑的因素
  • 如果要监控磁盘的话,可以考虑监控持续一段时间的iowait和svctm, 如果这两个值持续比较大的话,对应的磁盘设备很大可能有问题
  • 监控swap和file system分区,确认虚拟内存和fs IO之间没有竞争

Network

基本概念

1. 带宽: 当下比较常用的带宽用100Mbps, 1000Mbps, 10000Mbps,分别对应于我们平时提的百兆网、千兆网和万兆网。通常,我们在说带宽的时候,单位用的是bit, 但是在实际应用的时候,我们用的单位大多是Byte, 因此,上述三种网对应的Byte带宽分别约是12.5MBps, 125MBps, 1250MBps。

常用工具

1. sar: sar是一个全面的工具,这里介绍用它来看系统网络情况:
sar-2
2. iptraf: 查看指定设备实时的throughput
下图是iptraf -d em1的结果:
iptraf

经验之谈

关于Network, 有下面一些经验之谈:

  • 关于网络,最重要的就是查看的是网络带宽的使用是否符合预期,注意(bit/Byte)转换,另外也要注意上下行是独立的
]]>
http://www.wuzesheng.com/?feed=rss2&p=2665 1
OAuth in One Picture http://www.wuzesheng.com/?p=2645 http://www.wuzesheng.com/?p=2645#respond Sun, 12 Jan 2014 03:42:46 +0000 http://www.wuzesheng.com/?p=2645 OAuth

近年来,OAuth在各种开放平台的引领下变得非常流行,上图是OAuth协议认证的全过程,图本身已经比较详细,这里不再赘述。

从上图中可以看出,OAuth协议中有三个角色: User, Consumer, ServiceProvide, OAuth协议要解决的问题是,Consumer代表User去访问ServiceProvider提供的User的数据,如何保证User数据安全性的问题。传统的做法是是User把自己的用户名、密码提供给Consumer,然后Consumer代表User去向ServiceProvider请求User的数据,这样的做法的缺陷是显而易见的,Consumer拿了User的用户名、密码,就可以对用户的数据做任意操作,同时也有泄漏User的用户名、密码的风险。OAuth很好的解决了上面的问题,保证了对User数据访问的安全性的前提下,也很好的保证了User的用户名、密码的安全性。

关于对上图的理解,可以假设这样一个场景,用户想通过第三方App(比如Fuubo客户端),来收发自己的新浪微博,这里用户就是上图中的User, 第三方App是Consumer, 新浪微博是ServiceProvider。在这样场景下,结合初次授权时的情景,应该就比较好理解了。

如果想详细了解上图中每个过程中具体的细节,可以参考下面的两篇文章,或者直接参考相关的RFC:
1.OAuth介绍 – 使用场景
2.OAuth介绍 – 协议解析

]]>
http://www.wuzesheng.com/?feed=rss2&p=2645 0
ZooKeeper原理及使用 http://www.wuzesheng.com/?p=2609 http://www.wuzesheng.com/?p=2609#comments Sun, 15 Dec 2013 14:27:54 +0000 http://www.wuzesheng.com/?p=2609 ZooKeeper是Hadoop Ecosystem中非常重要的组件,它的主要功能是为分布式系统提供一致性协调(Coordination)服务,与之对应的Google的类似服务叫Chubby。今天这篇文章分为三个部分来介绍ZooKeeper,第一部分介绍ZooKeeper的基本原理,第二部分介绍ZooKeeper提供的Client API的使用,第三部分介绍一些ZooKeeper典型的应用场景。

ZooKeeper基本原理

1. 数据模型
zookeeper-tree
如上图所示,ZooKeeper数据模型的结构与Unix文件系统很类似,整体上可以看作是一棵树,每个节点称做一个ZNode。每个ZNode都可以通过其路径唯一标识,比如上图中第三层的第一个ZNode, 它的路径是/app1/c1。在每个ZNode上可存储少量数据(默认是1M, 可以通过配置修改, 通常不建议在ZNode上存储大量的数据),这个特性非常有用,在后面的典型应用场景中会介绍到。另外,每个ZNode上还存储了其Acl信息,这里需要注意,虽说ZNode的树形结构跟Unix文件系统很类似,但是其Acl与Unix文件系统是完全不同的,每个ZNode的Acl的独立的,子结点不会继承父结点的,关于ZooKeeper中的Acl可以参考之前写过的一篇文章《说说Zookeeper中的ACL》。

2.重要概念
2.1 ZNode
前文已介绍了ZNode, ZNode根据其本身的特性,可以分为下面两类:

  • Regular ZNode: 常规型ZNode, 用户需要显式的创建、删除
  • Ephemeral ZNode: 临时型ZNode, 用户创建它之后,可以显式的删除,也可以在创建它的Session结束后,由ZooKeeper Server自动删除

ZNode还有一个Sequential的特性,如果创建的时候指定的话,该ZNode的名字后面会自动Append一个不断增加的SequenceNo。

2.2 Session
Client与ZooKeeper之间的通信,需要创建一个Session,这个Session会有一个超时时间。因为ZooKeeper集群会把Client的Session信息持久化,所以在Session没超时之前,Client与ZooKeeper Server的连接可以在各个ZooKeeper Server之间透明地移动。

在实际的应用中,如果Client与Server之间的通信足够频繁,Session的维护就不需要其它额外的消息了。否则,ZooKeeper Client会每t/3 ms发一次心跳给Server,如果Client 2t/3 ms没收到来自Server的心跳回应,就会换到一个新的ZooKeeper Server上。这里t是用户配置的Session的超时时间。

2.3 Watcher
ZooKeeper支持一种Watch操作,Client可以在某个ZNode上设置一个Watcher,来Watch该ZNode上的变化。如果该ZNode上有相应的变化,就会触发这个Watcher,把相应的事件通知给设置Watcher的Client。需要注意的是,ZooKeeper中的Watcher是一次性的,即触发一次就会被取消,如果想继续Watch的话,需要客户端重新设置Watcher。这个跟epoll里的oneshot模式有点类似。

3. ZooKeeper特性
3.1 读、写(更新)模式
在ZooKeeper集群中,读可以从任意一个ZooKeeper Server读,这一点是保证ZooKeeper比较好的读性能的关键;写的请求会先Forwarder到Leader,然后由Leader来通过ZooKeeper中的原子广播协议,将请求广播给所有的Follower,Leader收到一半以上的写成功的Ack后,就认为该写成功了,就会将该写进行持久化,并告诉客户端写成功了。

3.2 WAL和Snapshot
和大多数分布式系统一样,ZooKeeper也有WAL(Write-Ahead-Log),对于每一个更新操作,ZooKeeper都会先写WAL, 然后再对内存中的数据做更新,然后向Client通知更新结果。另外,ZooKeeper还会定期将内存中的目录树进行Snapshot,落地到磁盘上,这个跟HDFS中的FSImage是比较类似的。这么做的主要目的,一当然是数据的持久化,二是加快重启之后的恢复速度,如果全部通过Replay WAL的形式恢复的话,会比较慢。

3.3 FIFO
对于每一个ZooKeeper客户端而言,所有的操作都是遵循FIFO顺序的,这一特性是由下面两个基本特性来保证的:一是ZooKeeper Client与Server之间的网络通信是基于TCP,TCP保证了Client/Server之间传输包的顺序;二是ZooKeeper Server执行客户端请求也是严格按照FIFO顺序的。

3.4 Linearizability
在ZooKeeper中,所有的更新操作都有严格的偏序关系,更新操作都是串行执行的,这一点是保证ZooKeeper功能正确性的关键。

ZooKeeper Client API

ZooKeeper Client Library提供了丰富直观的API供用户程序使用,下面是一些常用的API:

  • create(path, data, flags): 创建一个ZNode, path是其路径,data是要存储在该ZNode上的数据,flags常用的有: PERSISTEN, PERSISTENT_SEQUENTAIL, EPHEMERAL, EPHEMERAL_SEQUENTAIL
  • delete(path, version): 删除一个ZNode,可以通过version删除指定的版本, 如果version是-1的话,表示删除所有的版本
  • exists(path, watch): 判断指定ZNode是否存在,并设置是否Watch这个ZNode。这里如果要设置Watcher的话,Watcher是在创建ZooKeeper实例时指定的,如果要设置特定的Watcher的话,可以调用另一个重载版本的exists(path, watcher)。以下几个带watch参数的API也都类似
  • getData(path, watch): 读取指定ZNode上的数据,并设置是否watch这个ZNode
  • setData(path, watch): 更新指定ZNode的数据,并设置是否Watch这个ZNode
  • getChildren(path, watch): 获取指定ZNode的所有子ZNode的名字,并设置是否Watch这个ZNode
  • sync(path): 把所有在sync之前的更新操作都进行同步,达到每个请求都在半数以上的ZooKeeper Server上生效。path参数目前没有用
  • setAcl(path, acl): 设置指定ZNode的Acl信息
  • getAcl(path): 获取指定ZNode的Acl信息

ZooKeeper典型应用场景

1. 名字服务(NameService)
分布式应用中,通常需要一套完备的命令机制,既能产生唯一的标识,又方便人识别和记忆。 我们知道,每个ZNode都可以由其路径唯一标识,路径本身也比较简洁直观,另外ZNode上还可以存储少量数据,这些都是实现统一的NameService的基础。下面以在HDFS中实现NameService为例,来说明实现NameService的基本布骤:

  • 目标:通过简单的名字来访问指定的HDFS机群
  • 定义命名规则:这里要做到简洁易记忆。下面是一种可选的方案: [serviceScheme://][zkCluster]-[clusterName],比如hdfs://lgprc-example/表示基于lgprc ZooKeeper集群的用来做example的HDFS集群
  • 配置DNS映射: 将zkCluster的标识lgprc通过DNS解析到对应的ZooKeeper集群的地址
  • 创建ZNode: 在对应的ZooKeeper上创建/NameService/hdfs/lgprc-example结点,将HDFS的配置文件存储于该结点下
  • 用户程序要访问hdfs://lgprc-example/的HDFS集群,首先通过DNS找到lgprc的ZooKeeper机群的地址,然后在ZooKeeper的/NameService/hdfs/lgprc-example结点中读取到HDFS的配置,进而根据得到的配置,得到HDFS的实际访问入口

2. 配置管理(Configuration Management)
在分布式系统中,常会遇到这样的场景: 某个Job的很多个实例在运行,它们在运行时大多数配置项是相同的,如果想要统一改某个配置,一个个实例去改,是比较低效,也是比较容易出错的方式。通过ZooKeeper可以很好的解决这样的问题,下面的基本的步骤:

  • 将公共的配置内容放到ZooKeeper中某个ZNode上,比如/service/common-conf
  • 所有的实例在启动时都会传入ZooKeeper集群的入口地址,并且在运行过程中Watch /service/common-conf这个ZNode
  • 如果集群管理员修改了了common-conf,所有的实例都会被通知到,根据收到的通知更新自己的配置,并继续Watch /service/common-conf

3. 组员管理(Group Membership)
在典型的Master-Slave结构的分布式系统中,Master需要作为“总管”来管理所有的Slave, 当有Slave加入,或者有Slave宕机,Master都需要感知到这个事情,然后作出对应的调整,以便不影响整个集群对外提供服务。以HBase为例,HMaster管理了所有的RegionServer,当有新的RegionServer加入的时候,HMaster需要分配一些Region到该RegionServer上去,让其提供服务;当有RegionServer宕机时,HMaster需要将该RegionServer之前服务的Region都重新分配到当前正在提供服务的其它RegionServer上,以便不影响客户端的正常访问。下面是这种场景下使用ZooKeeper的基本步骤:

  • Master在ZooKeeper上创建/service/slaves结点,并设置对该结点的Watcher
  • 每个Slave在启动成功后,创建唯一标识自己的临时性(Ephemeral)结点/service/slaves/${slave_id},并将自己地址(ip/port)等相关信息写入该结点
  • Master收到有新子结点加入的通知后,做相应的处理
  • 如果有Slave宕机,由于它所对应的结点是临时性结点,在它的Session超时后,ZooKeeper会自动删除该结点
  • Master收到有子结点消失的通知,做相应的处理

4. 简单互斥锁(Simple Lock)
我们知识,在传统的应用程序中,线程、进程的同步,都可以通过操作系统提供的机制来完成。但是在分布式系统中,多个进程之间的同步,操作系统层面就无能为力了。这时候就需要像ZooKeeper这样的分布式的协调(Coordination)服务来协助完成同步,下面是用ZooKeeper实现简单的互斥锁的步骤,这个可以和线程间同步的mutex做类比来理解:

  • 多个进程尝试去在指定的目录下去创建一个临时性(Ephemeral)结点 /locks/my_lock
  • ZooKeeper能保证,只会有一个进程成功创建该结点,创建结点成功的进程就是抢到锁的进程,假设该进程为A
  • 其它进程都对/locks/my_lock进行Watch
  • 当A进程不再需要锁,可以显式删除/locks/my_lock释放锁;或者是A进程宕机后Session超时,ZooKeeper系统自动删除/locks/my_lock结点释放锁。此时,其它进程就会收到ZooKeeper的通知,并尝试去创建/locks/my_lock抢锁,如此循环反复

5. 互斥锁(Simple Lock without Herd Effect)
上一节的例子中有一个问题,每次抢锁都会有大量的进程去竞争,会造成羊群效应(Herd Effect),为了解决这个问题,我们可以通过下面的步骤来改进上述过程:

  • 每个进程都在ZooKeeper上创建一个临时的顺序结点(Ephemeral Sequential) /locks/lock_${seq}
  • ${seq}最小的为当前的持锁者(${seq}是ZooKeeper生成的Sequenctial Number)
  • 其它进程都对只watch比它次小的进程对应的结点,比如2 watch 1, 3 watch 2, 以此类推
  • 当前持锁者释放锁后,比它次大的进程就会收到ZooKeeper的通知,它成为新的持锁者,如此循环反复

这里需要补充一点,通常在分布式系统中用ZooKeeper来做Leader Election(选主)就是通过上面的机制来实现的,这里的持锁者就是当前的“主”。

6. 读写锁(Read/Write Lock)
我们知道,读写锁跟互斥锁相比不同的地方是,它分成了读和写两种模式,多个读可以并发执行,但写和读、写都互斥,不能同时执行行。利用ZooKeeper,在上面的基础上,稍做修改也可以实现传统的读写锁的语义,下面是基本的步骤:

  • 每个进程都在ZooKeeper上创建一个临时的顺序结点(Ephemeral Sequential) /locks/lock_${seq}
  • ${seq}最小的一个或多个结点为当前的持锁者,多个是因为多个读可以并发
  • 需要写锁的进程,Watch比它次小的进程对应的结点
  • 需要读锁的进程,Watch比它小的最后一个写进程对应的结点
  • 当前结点释放锁后,所有Watch该结点的进程都会被通知到,他们成为新的持锁者,如此循环反复

7. 屏障(Barrier)
在分布式系统中,屏障是这样一种语义: 客户端需要等待多个进程完成各自的任务,然后才能继续往前进行下一步。下用是用ZooKeeper来实现屏障的基本步骤:

  • Client在ZooKeeper上创建屏障结点/barrier/my_barrier,并启动执行各个任务的进程
  • Client通过exist()来Watch /barrier/my_barrier结点
  • 每个任务进程在完成任务后,去检查是否达到指定的条件,如果没达到就啥也不做,如果达到了就把/barrier/my_barrier结点删除
  • Client收到/barrier/my_barrier被删除的通知,屏障消失,继续下一步任务

8. 双屏障(Double Barrier)
双屏障是这样一种语义: 它可以用来同步一个任务的开始和结束,当有足够多的进程进入屏障后,才开始执行任务;当所有的进程都执行完各自的任务后,屏障才撤销。下面是用ZooKeeper来实现双屏障的基本步骤:

    进入屏障:

  • Client Watch /barrier/ready结点, 通过判断该结点是否存在来决定是否启动任务
  • 每个任务进程进入屏障时创建一个临时结点/barrier/process/${process_id},然后检查进入屏障的结点数是否达到指定的值,如果达到了指定的值,就创建一个/barrier/ready结点,否则继续等待
  • Client收到/barrier/ready创建的通知,就启动任务执行过程
    离开屏障:

  • Client Watch /barrier/process,如果其没有子结点,就可以认为任务执行结束,可以离开屏障
  • 每个任务进程执行任务结束后,都需要删除自己对应的结点/barrier/process/${process_id}
]]>
http://www.wuzesheng.com/?feed=rss2&p=2609 3
From MapReduce To YARN http://www.wuzesheng.com/?p=2585 http://www.wuzesheng.com/?p=2585#comments Tue, 05 Nov 2013 15:39:01 +0000 http://www.wuzesheng.com/?p=2585 Google MapReduce

MapReduce是由Google提出的一种软件架构,用于大规模数据的并行计算。Map和Reduce这两个概念,是从函数式编程语言中借鉴过来的。正如Google MapReduce Paper中所描述,MapReduce是这样一个过程:输入是Key/Value对A,用户指定一个Map函数来处理A,得到一个中间结果Key/Value集合B,再由用户指定的Reduce函数来把B中相同Key的Value归并到一起,计算得到最终的结果集合C,这就是MapReduce的基本原理,可以简单的表达为:
map (k1, v1) -> list (k2, v2)
reduce (k2, list(v2)) -> list (v2)

MapReduce的原理本身比较简单,但开发一套完备、易用性好的MapReduce系统,不是一件容易的事。这里会涉及分布式系统的故障容错、负载均衡等一系列复杂的问题。下面就结合在Google MapReduce Paper所讲的MapReduce的执行流程,来介绍一下MapReduce系统的基本工作原理:
mapreduce
在上图中,我们可以看到,MapReduce系统中有三种角色,一是用于Job调度与管理的Master进程;二是实际执行Map/Reduce Task的Worker进程;三是用户向MapReduce系统提交Job的MapReduce Client。用户提交一个MapReduce Job到Job执行完成,是这样一个过程:

  • 用户通过MapReduce Client将输入数据根据大小切分成M片,并向Master发起提交Job请求;
  • Master收到用户提交Job的请求后,将M(M是输入切片数)个Map Task和R(R是由用户指定的)个Reduce Task分配到空闲的Worker上;
  • 收到Map Task的Worker,把对应于该Task的输入文件读入,并调用用户提供的Map函数对数据进行处理,处理的结果缓存在内存中。Worker会定期把内存中的数据Flush到磁盘上,并且会通过hash(Key)%R的方式,将输出分为R份;
  • 当执行Reduce Task的Worker收到Master的通知,得到了Map Task输出结果的位置后,它就会通过RPC调用把Map Task的输出结果读回来。首先对数据进行排序,然后对相同Key的数据进行归并。这里排序是必要的,因为数据有可能比较大,读入内存的数据只是一小部分,如果不排序的话,没办法将相同Key的数据进行归并及处理;
  • 执行Reduce Task的Worker遍历排序好的中间结果数据,调用用户提供的Reduce函数对数据进行处理,并将处理结果输出到最终的结果文件;
  • 当所有的Map和Reduce Task都执行完成后,Master会通知给用户程序Job执行结束。

Hadoop MapReduce V1

受Google Paper的启发,在Yahoo做搜索引擎的工程师Doug Cutting和Mike Cafarella做出了Yahoo自己的Mapreduce,以及Distributed File System,发展到后来,成了Apache Software Foundation的顶级开源项目,也就是今天大家所熟知的Hadoop, Hadoop系统主要包括两大系统:分布式文件存储系统HDFS,和分布式计算系统MapReduce。HDFS跟Hadoop MapReduce的关系,跟GFS跟Google MapReduce的关系是一样的,它带来的主要好处是数据的高可用,以及MapReduce本身不需要关注数据存放的具体物理位置,很大程度上简化了MapReduce系统的Failover过程。下面我们通过下图来介绍一下Hadoop MapReduce的架构:
MRArch
与Google的MapReduce的结构类似,Hadoop MapReduce也是一种Master/Slave结构。在Hadoop MapReduce系统中,也包含三种不同的角色:一是JobTracker, 与Google MapReduce中的Master对应,负责Job的整体协调与管理;二是TaskTracker,与Google MapReduce中的Worker对应,负责执行具体Map/Reduce Task;三是JobClient,与Google MapReduce中的Client对应,用户通过它来向MapReduce系统提交Job。因为Hadoop MapReduce(V1)与Google MapReduce非常类似,基本的执行流程也是类似的,这里就不再赘述了。

YARN – Hadoop MapReduce V2

我们知道,MapReduce的主要优势在于大规模数据的批量处理(Batch Processing),在很多应用场景下,MapReduce是十分高效的,但是MapReduce并不能解决所有的问题。在现实生活中,还有多种其它的计算场景,与之对应的也出现了不少相应的计算框架,比如流式计算的Storm、迭代计算的Spark、以及图计算的Giraph等。在这样一些场景下,MapReduce就显得没有什么优势了。试想一下,如果用户数据都存储在HDFS中,如果Hadoop本身不支持这些新型的计算框架,那么如果用户要使用这些新型的计算框架,就需要把HDFS中的数据搬到别的存储系统中去,这个开销对于大部分应用来说几乎是不可接受的。因此,在Hadoop上支持除MapReduce之外的其它计算框架是众望所盼的。

以Hadoop MapReduce为例,在传统的MapReduce框架中,会把资源划分为Map Slots和Reduce Slots, 分散在各个TaskTracker中, Map Slots和Reduce Slots是不可相互替代的。这样在实际的执行过程中,就可能会遇到Map Slots用完,而Reduce Slots却空闲,或者Reduce Slots用完,而Map Slots空闲的情况,在这样的情况下,整个Job都无法正常进行。因此,资源的统一调度与管理,资源利用率的提升,对于Hadoop来说也是一个十分迫切的需求。

同样以Hadoop MapReduce为例,我们知道,整个系统是Master/Slave结构,JobTracker是一个中心点,所有用户的Job的提交、后续每个Map/Reduce Task的状态更新及进度管理,都要经过JobTracker, 这样JobTracker要管理的结点是跟所有的Task数正相关的,JobTracker很容易成为整个系统的瓶颈。因此,如何弱化JobTracker这个中心点的功能,减轻其负载,成了提升Hadoop MapReduce系统支持更大规模的集群的关键问题。

基于上面一些主要的考虑点,Hadoop社区开始了下一代MapReduce系统的开发,号称MapReduceV2,也叫YARN(Yet Another Resource Negotiator)。对于YARN和MapReduceV2, 很多初识YARN者会误认为是同一个东东,其实不然。YARN和MapReduceV2完全是两回事,YARN是Hadoop系统的任务与资源管理系统,而MapReduceV2是基于YARN进行重构过的新版的MapReduce计算框架。打个比方,YARN是操作系统,而MapReduceV2刚是装在操作系统上的一个App,这么说就应该比较好理解一些了。接下来,我们来看一下YARN的架构:
YARNArch
从上图我们可以看出,在YARN中,主要包括以下几种角色:

  • ResourceManager: 负责Job的调度、管理,资源的分配、管理的模块。它所管理的单位是Job(更为通用的叫法是Application),不再是具体的Task,因此能比传统的MapReduce支持更大规模的集群;
  • NodeManager:负责执行具体Task的模块。它不再有Slots的概念,取而代之的Container,Task都是通过在NodeManager上启动Container的方式来执行的;
  • ApplicationMaster:由YARN框架提供的一个Libary,每个用户Application(Job)会对应一个ApplicationMaster,在用户提交Application后,ResourceManager会启动该Application对应的ApplicationMaster, 并把ApplicationMaster的地址反馈给客户端,后续客户端直接跟ApplicationMaster进行交互。ApplicatoinMaster负责向ResourceManager申请执行用户Task所需要的资源,以及管理用户Application中每一个Task的运行情况;
  • Container:抽象出来的资源集合的概念,它代表由ResouceManage授予给Application的资源,包括CPU、内存等。最终用户Task都是通过Container来执行的;
  • Client:负责向ResourceManager提交Application(Job)请求,并从ApplicationMaster获取执行结果。

通过上面的介绍,相信大家应该对YARN有了初步的了解,下面我们来看一下在YARN中是如何提交、执行Application(Job)的:

  • 用户通过Client向ResourceManager提交Application,ResourceManager根据用户的请求,分配一个合适的Container,然后在指定的NodeManager上启动该Container运行ApplicationMaster;
  • ApplicationMaster在NodeManager上启动后,向ResourceManager注册自己;
  • ApplicationMaster跟ResourceManager协商、申请用户Task所需要的Container, 申请到后ApplicationMaster把Container描述发给指定的NodeManager,NodeManager启动Container,运行用户Task;
  • 用户Task在Container中执行的过程中,会定期向ApplicationMaster汇报执行的进度、状态等信息;
  • 在执行过程中,Client可以直接跟ApplicationMaster通信,获取整个Application执行的进度、状态等信息;
  • 所有的Task执行结束后,整个Application执行结束,ApplicationMaster会向ResourceManager注销自己,释放资源,并退出运行。
]]>
http://www.wuzesheng.com/?feed=rss2&p=2585 2
Distributed System Prerequisite List http://www.wuzesheng.com/?p=2546 http://www.wuzesheng.com/?p=2546#comments Sun, 27 Oct 2013 13:38:19 +0000 http://www.wuzesheng.com/?p=2546 bigdata写在前面:不知不觉,来帝都已经一年整了,这也意味着从search转向分布式系统真正一年了。当初选择这个方向,也考虑到过会有很多不容易,但是既然决定了、选择了,就要努力去做好。过去的一年,也曾迷惘过,也曾沮丧过,但好在努力坚持了下来。总得来说,对所目前的状态,以及所做的事还是充满了信心。一直以来,都坚信只要是自己喜欢的事情,努力去做了,就一定能做好,这是支撑我不断努力、奋斗的信念。最近一直在考虑一个事情,过去的一年,在工作之余,陆陆续续零零散散看了不少分布式相关的资料,打算把这一块好好梳理一下,希望在接下来一年左右时间,通过系统地、深入地学习,对这个领域的理解能够更加全面、透彻。这也是今天写下这篇文章的一个初衷,一方面是给自己接下来的学习理清一个思路,另一方面,也是希望能够与有同样需求的朋友共勉。

接下的内容按几个大类来列:
1. 文件系统
a. GFS – The Google File System
b. HDFS
1) The Hadoop Distributed File System
2) The Hadoop Distributed File System: Architecture And Design
c. XFS – The Tencent File System

2. 数据库系统
a. BigTable – BigTable: A Distributed Storage System for Structured Data
b. HBase – The Apache HBase Reference Guide
c. Dynamo – Dynamo: Amazon’s Highly Available Key-Value Store
d. Megastore – Megastore: Providing Scalable, Highly Available Storage for Interactive Services
e. Spanner – Spanner: Google’s Globally-Distributed Database
f. Azure – Windows Azure Storage: A Highly Available Cloud Storage Service with Strong Consistency
g. Percolator – Large-scale Incremental Processing Using Distributed Transactions and Notifications

3. 机群/资源管理系统
a. Omega – Omega: Flexible, Scalable Schedulers for Large Compute Clusters
b. Autopilot – Autopilot: Automatic Data Center Management
c. Yarn
1) Architecture of Next Generation Apache Hadoop MapReduce Framework
2) The Next Generation of Apache Hadoop Mapreduce
3) Introducing Apache Hadoop YARN
d. Mesos – A Platform for Fine-Grained Resource Sharing in the Data Center

4. 计算框架:
a. MapReduce – MapReduce: Simplified Data Processing on Large Clusters
b. Storm – Storm: Distributed and Fault-Tolerant Realtime Computaion
c. Spark – Spark: Cluster Computing with Working Sets
d. Impala – Cloudera Impala: Real-Time Querie in Apache Hadoop
e. Dremel – Dremel: Interactive Analysis of Web-Scale Datasets
f. Hive/Stinger
1) Hive: A Warehousing Solution Over a MapReduce Framework
2) Hive: A Petabyte Scale Data Warehouse Using Hadoop
3) The Stinger Initiative: Making Apache Hive 100 Times Faster
4) Stinger, Interactive Query for Apache Hive
g. FlumeJava/Crunch
1) FlumeJava: Easy, Efficient Data-Parellel Pipelines
2) Introducing Crunch: Easy MapReduce Pipelines for Apache Hadoop
h. Tez
1) Apache Hadoop Tez
2) Apache Tez: A New Chapter in Hadoop Data Processing
g. Presto – Presto: Interacting with petabytes of data at Facebook

5. 分布式一致性
a. Paxos – Paxos Made Simple
b. Zookeeper
1) Zookeeper: A Distributed Coordination Service for Distributes Applications
2) Zookeeper: Wait-free Coordination for Internet-scale Systems
c. Chubby – The Chubby Lock Service for Loosely-coupled Distributed Systems
d. Raft – In Search of an Understandable Consensus Algorithm

6. 其它
a. SequenceFile – Sequence File Format
b. SSTable
1) SSTable and Log Structured Storage: LevelDB
2) SSTable Storage Format
c. RCFile – RCFile: A Fast and Space-efficient Data Placement Structure in MapReduce-based Warehouse Systems
d. ORCFile – ORC File Format
e. Parquet – Parquet: Columnar Storage for The People

]]>
http://www.wuzesheng.com/?feed=rss2&p=2546 1
深入浅出Future Pattern http://www.wuzesheng.com/?p=2485 http://www.wuzesheng.com/?p=2485#respond Mon, 24 Jun 2013 15:59:34 +0000 http://www.wuzesheng.com/?p=2485 前几天看hdfs QJM的代码,里面看到一个ListenableFuture,说实话对于Java,目前我还只是通过看代码,遇到没见过的再去查的方式,也着实是没有时间和精力再去通篇研读诸如《thinking in Java》这样的大砖块了,现在这样的方式,目前来说应该是够用了。重点还是放在系统和业务上,语言本身本不应该成为障碍。言归正传,回到ListenableFuture, 在网上看了一下相关的资料,把它的来龙去脉了解了一下,在这里记录一下。

前面提到的ListenableFuture, 是google开源的自己的Java Library Guava(http://code.google.com/p/guava-libraries/)中的一个模块,它本身是继承是Java的Future。严格来讲,Future是一种Design Pattern, 它本身跟语言是没有关系的。最新的C++11中,也加入了Future的支持,不过笔者以前写C++的时候,C++11还没正式发布,加上它本身也是比较新的,主流的编译器支持的本身也不是很好,因此只是知道它的存在,并没有去研究过它是做什么的,怎么来使用的。接下来,我会通过一些Java的Future的例子,来一步步介绍Future及其用法。

简单来讲,Future是这样一种Pattern: 它本身表示‘将来(future)’,你提交一个异步的任务,比如提交到一个threadpool,与此同时拿到一个Future对象,任务的执行是异步的,这时候你可以去做其它的事情,等到异步任务结束的时候,你可通过前面的Future对象拿到异步执行的任务的结果。下面通过一个简单的例子来直观感受一下Future:

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.FutureTask;

public class FutureTest {

  public static void main(String[] args) {
    ExecutorService executor = Executors.newFixedThreadPool(1);

    Future< String> future = executor.submit(new Callable< String>() {
      public String call() {
        return "Hello futue!";
      }
    });

    try {
      Thread.sleep(1000);
      System.out.println(future.get());
    } catch (InterruptedException e) {
      future.cancel(true);
    } catch (ExecutionException e) {
      future.cancel(true);
    } finally {
      executor.shutdown();
    }
  }
}

在上面的程序中,和我注意到第20行的Thread.sleep(1000),这里的原本的含义是在异步任务执行期间,原线程可以做任何事情,不会阻塞。这里简单用了sleep,但要表达的意思是清楚的。

看了上面的例子,细心的朋友总会有这样的疑问,Future要获取异步任务执行的结果,需要通过轮询或者阻塞等待的方式,这样的方式,总显得不太‘完美’,我们知道,比较好的方式,应该是异步执行结束后,自动通知用户异步任务结束了,你可以通过Future来获取执行结果了。这就诞生了google的ListenableFuture,用户可以向它注册一个回调函数和提供一个线程池(可选),当异步任务执行结束后,它会自动在用户提供的线程池里调用用户注册的回调函数,通知用户异步任务执行结束了。当然,如果用户不提供线程池,它会在运行异步任务的工作线程里运行回调函数,这种情况适用于工作线程本身的任务比较轻量级的情景。下面通过几个例子,说明ListenableFuture的具体的用法:

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executors;

import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.ListeningExecutorService;
import com.google.common.util.concurrent.MoreExecutors;

public class ListenableFutureTest {

  public static void main(String[] args) {
    ListeningExecutorService executor = MoreExecutors.listeningDecorator(
        Executors.newFixedThreadPool(1));

    final ListenableFuture< String> future = executor.submit(
        new Callable< String>() {
      public String call() throws Exception {
        return "Hello listenable future";
      }
    });

    future.addListener(new Runnable() {
      public void run() {
        try {
          System.out.println(future.get());
        } catch (InterruptedException e) {
          future.cancel(true);
        } catch (ExecutionException e) {
          future.cancel(true);
        }
      }
    }, Executors.newFixedThreadPool(1));

    System.out.println("exit..");
  }
}

上面的程序中,第35行的System.out.println(“exit..”);可能会先于“Hello listenable future” print到stdout上,这也间接能说明回调函数的执行是在别的线程中的。另外,在上面的例子中,我们发现,用户需要在注册的回调函数中处理InterruptedException和ExecutionException, 显得略为麻烦。这里Guava还提供了另为一种使用方式,接口上来看,更加清晰一些,下面是这种方式的使用的一个例子:

import java.util.concurrent.Callable;
import java.util.concurrent.Executors;

import com.google.common.util.concurrent.FutureCallback;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.ListeningExecutorService;
import com.google.common.util.concurrent.MoreExecutors;

public class ListenableFutureTest2 {

  public static void main(String[] args) {
    ListeningExecutorService executor = MoreExecutors.listeningDecorator(
        Executors.newFixedThreadPool(1));

    final ListenableFuture< String> future = executor.submit(
        new Callable< String>() {
      public String call() throws Exception {
        return "Hello listenable future";
      }
    });

    Futures.addCallback(future, new FutureCallback< String>() {
      public void onSuccess(String result) {
        System.out.println(result);
      }k

      public void onFailure(Throwable t) {
        System.out.println("error: " + t);
      }

    }, Executors.newFixedThreadPool(1));

    System.out.println("exit..");
  }
}

上面几个例子中,都显式的提供了用户线程池,用来执行回调函数。用户也可以不提供线程,或者可以通过 MoreExecutors.sameThreadExecutor()把当前线程传进去,用来执行回函数。

]]>
http://www.wuzesheng.com/?feed=rss2&p=2485 0
HDFS ZKFC手记 http://www.wuzesheng.com/?p=2475 http://www.wuzesheng.com/?p=2475#comments Thu, 13 Jun 2013 00:38:22 +0000 http://www.wuzesheng.com/?p=2475 1.基本原理

zk的基本特性:
(1) 可靠存储小量数据且提供强一致性
(2) ephemeral node, 在创建它的客户端关闭后,可以自动删除
(3) 对于node状态的变化,可以提供异步的通知(watcher)

zk在zkfc中可以提供的功能:
(1) Failure detector: 及时发现出故障的NN,并通知zkfc
(2) Active node locator: 帮助客户端定位哪个是Active的NN
(3) Mutual exclusion of active state: 保证某一时刻只有一个Active的NN

2. 模块

(1) ZKFailoverController(DFSZKFailoverController): 驱动整个ZKFC的运转,通过向HealthMonitor和ActiveStandbyElector注册回调函数的方式,subscribe HealthMonitor和ActiveStandbyElector的事件,并做相应的处理
(2) HealthMonitor: 定期check NN的健康状况,在NN健康状况发生变化时,通过回调函数把变化通知给ZKFailoverController
(3) ActiveStandbyElector: 管理NN在zookeeper上的状态,zookeeper上对应node的结点发生变化时,通过回调函数把变化通知给ZKFailoverController
(4) FailoverController: 提供做graceful failover的相关功能(dfs admin可以通过命令行工具手工发起failover)

3. 系统架构

zkfc-arch
如上图所示,通常情况下Namenode和ZKFC同布署在同一台物理机器上, HealthMonitor, FailoverController, ActiveStandbyElector在同一个JVM进程中(即ZKFC), Namenode是一个单独的JVM进程。如上图所示,ZKFC在整个系统中有几个重要的作用:
(1) Monitor and try to take active lock: 向zookeeper抢锁,抢锁成功的zkfc,指导对应的NN成为active的NN; watch锁对应的znode,当前active NN的状态发生变化导致失锁时,及时抢锁,努力成为active NN
(2) Monitor NN liveness and health: 定期检查对应NN的状态, 当NN状态发生变化时,及时通过ZKFC做相应的处理
(3) Fences other NN when needed: 当前NN要成为active NN时,需要fence其它的NN,不能同时有多个active NN

4. 线程模型

ZKFC的线程模型总体上来讲比较简单的,它主要包括三类线程,一是主线程;一是HealthMonitor线程; 一是zookeeper客户端的线程。它们的主要工作方式是:
(1) 主线程在启动所有的服务后就开始循环等待
(2) HealthMonitor是一个单独的线程,它定期向NN发包,检查NN的健康状况
(3) 当NN的状态发生变化时,HealthMonitor线程会回调ZKFailoverController注册进来的回调函数,通知ZKFailoverController NN的状态发生了变化
(4) ZKFailoverController收到通知后,会调用ActiveStandbyElector的API,来管理在zookeeper上的结点的状态
(5) ActiveStandbyElector会调用zookeeper客户端API监控zookeeper上结点的状态,发生变化时,回调ZKFailoverController的回调函数,通知ZKFailoverController,做出相应的变化

5. 类关系图

zkfc_class

6. 参考资料

(1) https://issues.apache.org/jira/secure/attachment/12521279/zkfc-design.pdf
(2) http://svn.apache.org/viewvc/hadoop/common/trunk/hadoop-hdfs-project/hadoop-hdfs/

]]>
http://www.wuzesheng.com/?feed=rss2&p=2475 6
说说Zookeeper中的ACL http://www.wuzesheng.com/?p=2438 http://www.wuzesheng.com/?p=2438#respond Sun, 02 Jun 2013 12:28:15 +0000 http://www.wuzesheng.com/?p=2438 Access Control在分布式系统中重要性是毋庸置疑的,今天这篇文章来介绍一下Zookeeper中的Access Control(ACL)。

  • 1. 概述
    传统的文件系统中,ACL分为两个维度,一个是属组,一个是权限,子目录/文件默认继承父目录的ACL。而在Zookeeper中,node的ACL是没有继承关系的,是独立控制的。Zookeeper的ACL,可以从三个维度来理解:一是scheme; 二是user; 三是permission,通常表示为scheme:id:permissions, 下面从这三个方面分别来介绍:

    (1)scheme: scheme对应于采用哪种方案来进行权限管理,zookeeper实现了一个pluggable的ACL方案,可以通过扩展scheme,来扩展ACL的机制。zookeeper-3.4.4缺省支持下面几种scheme:

    • world: 它下面只有一个id, 叫anyone, world:anyone代表任何人,zookeeper中对所有人有权限的结点就是属于world:anyone的
    • auth: 它不需要id, 只要是通过authentication的user都有权限(zookeeper支持通过kerberos来进行authencation, 也支持username/password形式的authentication)
    • digest: 它对应的id为username:BASE64(SHA1(password)),它需要先通过username:password形式的authentication
    • ip: 它对应的id为客户机的IP地址,设置的时候可以设置一个ip段,比如ip:192.168.1.0/16, 表示匹配前16个bit的IP段
    • super: 在这种scheme情况下,对应的id拥有超级权限,可以做任何事情(cdrwa)

    另外,zookeeper-3.4.4的代码中还提供了对sasl的支持,不过缺省是没有开启的,需要配置才能启用,具体怎么配置在下文中介绍。

    • sasl: sasl的对应的id,是一个通过sasl authentication用户的id,zookeeper-3.4.4中的sasl authentication是通过kerberos来实现的,也就是说用户只有通过了kerberos认证,才能访问它有权限的node.

    (2)id: id与scheme是紧密相关的,具体的情况在上面介绍scheme的过程都已介绍,这里不再赘述。

    (3)permission: zookeeper目前支持下面一些权限:

    • CREATE(c): 创建权限,可以在在当前node下创建child node
    • DELETE(d): 删除权限,可以删除当前的node
    • READ(r): 读权限,可以获取当前node的数据,可以list当前node所有的child nodes
    • WRITE(w): 写权限,可以向当前node写数据
    • ADMIN(a): 管理权限,可以设置当前node的permission
  • 2. 实现
    如前所述,在zookeeper中提供了一种pluggable的ACL机制。具体来说就是每种scheme对应于一种ACL机制,可以通过扩展scheme来扩展ACL的机制。在具体的实现中,每种scheme对应一种AuthenticationProvider。每种AuthenticationProvider实现了当前机制下authentication的检查,通过了authentication的检查,然后再进行统一的permission检查,如此便实现了ACL。所有的AuthenticationProvider都注册在ProviderRegistry中,新扩展的AuthenticationProvider可以通过配置注册到ProviderRegistry中去。下面是实施检查的具体实现:

    void checkACL(ZooKeeperServer zks, List acl, int perm,
        List ids) throws KeeperException.NoAuthException {
      if (skipACL) {
        return;
      }
      if (acl == null || acl.size() == 0) {
        return;
      }
      for (Id authId : ids) {
        if (authId.getScheme().equals("super")) {
          return;
        }
      }
      for (ACL a : acl) {
        Id id = a.getId();
        if ((a.getPerms() & perm) != 0) {
          if (id.getScheme().equals("world")
              && id.getId().equals("anyone")) {
            return;
          }   
          AuthenticationProvider ap = ProviderRegistry.getProvider(id
              .getScheme());
          if (ap != null) {
            for (Id authId : ids) {
              if (authId.getScheme().equals(id.getScheme())
                  && ap.matches(authId.getId(), id.getId())) {
                return;
              }   
            }   
          }   
        }   
      }
      throw new KeeperException.NoAuthException();
    }
    
  • 3. server配置
    可以通过下面两种方式把新扩展的AuthenticationProvider注册到ProviderRegistry:
    配置文件:在zookeeper的配置文件中,加入authProvider.$n=$classname即可
    JVM参数:启动Zookeeper的时候,通过-Dzookeeper.authProvider.$n=$classname的方式,把AuthenticaitonProvider传入
    在上面的配置中, $n是为了区分不同的provider的一个序号,只要保证不重复即可,没有实际的意义,通常用数字1,2,3等
  • 4. 管理ACL
    可以通过zookeeper client来管理ACL, zookeeper的发行包中提供了一个cli工具zkcli.sh,可以通过它来进行acl管理,下面通过一些例子来说明acl管理的基本方法:
    zkcli
]]>
http://www.wuzesheng.com/?feed=rss2&p=2438 0
Log4j学习笔记 http://www.wuzesheng.com/?p=2407 http://www.wuzesheng.com/?p=2407#comments Sun, 17 Mar 2013 03:21:07 +0000 http://www.wuzesheng.com/?p=2407 用了三四年的C++,转向Java的怀抱,还是有诸多的不适应。C++中不论多复杂的Server,只要有GDB在手,总感觉debug都不是啥大事,程序运行期间的各种状态,都可以通过GDB轻松的获取到;而到了Java中,总感觉像是被困住了手脚,有力没法使,不知道是我还掌握方法,还是事实确实如此,发现Server端的Java程序,几乎没有什么好的debug的方法。也跟一些用了几年Java的朋友聊过,大多给出的答案都是Log。以前在学校刚开始学C/C++的时候,那时候的debug就是靠printf,基本思想跟Log是一样的, 后来结识了神器GDB之后,就深深地爱上了它。现在用上了Java之后,感觉又一夜回到了解放前。我并不是说Log不重要,相反,在越复杂的系统中,Log的地位越发重要,但是对于debug而言,一点点的加Log, 然后重新编译,布署,效率实在是太低了。对于像Java这样没有GDB这样的神器的语言,Log的地位越发重要,因此像Log4j这样的组件就非常流行,前段时间在弄hadoop中异步log相关的一些事情,就顺便把log4j学习了一下,下面整理了一下log4j相关的一些内容。

1. Log4j online manual
地址:http://logging.apache.org/log4j/1.2/manual.html
本文的大部分内容是参考了这个manual,外加一些其他人的博客。

2. Log4j几个重要的概念
(1) Loggers:负责写Log的类,提供写Log相关的API
a. Logger的全名跟java中package的命名基本是差不多,不过这里有一个hierarchy的概念需要理解一下,比如说有三个名字,com.example.a, com.example.a.b, com.example.a.b.c,这里com.example.a叫做com.example.a.b的parent, 叫做com.example.a.b.c的ancestor.
b. Logger里还有一个Level的概念,这跟常规的其它log库都是一致的,Log4j中总共有这么几个Level: TRACE > DEBUG > INFO > WARN > ERROR > FATAL, log的level设得越小,Log写的最小,比如如果Log Level设的是FATAL, 那么就只有FATAL Level的Log才会写出来,其它的都不会。通常,我们都设Log Level为INFO,这样Level小于等于INFO的,包括INFO, WARN, ERROR, FATAL这几个级别的都会写出来。只有在debug的时候才会打开DEBUG或者TRACE Level的日志。
c. 下面是Logger基类对用户提供的API, 非常直观,一看就知道怎么用:

package org.apache.log4j;

public class Logger {
  // Creation & retrieval methods:
  public static Logger getRootLogger();
  public static Logger getLogger(String name);

  // printing methods:
  public void trace(Object message);
  public void debug(Object message);
  public void info(Object message);
  public void warn(Object message);
  public void error(Object message);
  public void fatal(Object message);

  // generic printing method:
  public void log(Level l, Object message);
}

(2) Appenders: 是Log4j中用来指定output destination的,就是哪个Logger的log,写到哪里去
a. Log4j允许同一条Log request写到多个Logger, 具体的实现就是把多个Appender,attach到指定的Logger上去
b. Log4j自带的appender有这些: console, files, GUI components, remote socket servers, JMS, NT Event Loggers, remote UNIX Syslog daemons,用户还可以实现自己的Appender来扩展Log4j的功能,比如比较常见的,实现一个写一个LogStore(比如Scribe)的Appender, 就可以简单地通过配置,把现有的Log4j写的log写到LogStore.
c. Appenders Additivity(相加性): 缺省情况下,每个Log request都会被forward给它对应的Appender,和它的parent & ancestor的Appender。如果appender C的parent或者某个ancestor把它的additivity flag设为false, 那么C收到的Log request只forward到P,P的parent & ancestor都不会收到这个Log request.

(3) Layout: 是Log4j中用来格式化Log的概念
a. Layout的基本语法和C中的printf的类似,具体可以参考:http://logging.apache.org/log4j/1.2/apidocs/org/apache/log4j/PatternLayout.html
b. Log4j中Log行号也是通过Layout来实现的,但这里需要注意的是,文档里说写行号会影响性能,如果不是debug,通常不需要写行号,这一点跟传统C/C++的Log组件不太像

3. Asynchronous Log4j
(1)Log4j的Configuration: 目前Log4j支持通过两种格式的文件来配置,一种是log4j.properties, 简单的key/value格式的文件;一种是log4j.xml,xml文件。具体使用的时候,把写好的配置文件放在程序的classpath中,这样程序运行的时候就能自动找到并加载
(2)关于log4j.properties和log4j.xml,主要的区别是,一些log4j的高级的feature, 只有log4j.xml支持,log4j.properties不支持。比如asynchronous log, 就只有log4j.xml支持。所以,如果是performance critical的程序,通常建议采用log4j.xml来配置log4j
(3)Asynchronous log4j的实现是单独起了一个线程来dump log, 这个跟预想的一致,下面是一个简单的验证:
a. async-log4j的调用栈:
async-log4j
b. sync-log4j的调用栈:
log4j
(4)下面是一个async-log4j配置的例子:

< ?xml version="1.0" encoding="UTF-8"?>
< !DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
 
  
    
    
  

  
  
    
    
    
    
      
    
  
  
    
    
  
  

(5) 这里需要注意一点:AsyncAppender有一个叫Blocking的属性,缺省是true. 缺省情况下,如果buffer满了,在把buffer dump到磁盘的过程中,后来的log request都会被block住。但是,如果把这个属性设为false, 那么,如果buffer满了,在把buffer dump到磁盘的过程中,后来的log就会被丢弃。这里就有一个log的完整性和性能之间的tradeoff, 如果是performance very critical的程序,又不在乎丢几条log, 那么可以考虑把这个属性设为false, 其它情况下,还是用缺省的true,毕竟Log的完整性也非常重要。

4. 其它一点需要注意的点
(1)这里提一下在mannual里介绍的一个与性能比较相关的点,先看下面的程序:

logger.debug("Entry number: " + i + " is " + String.valueOf(entry[i]));
--------------
if(logger.isDebugEnabled() {
     logger.debug("Entry number: " + i + " is " + String.valueOf(entry[i]));
}

这里,在log level为DEBUG的时候,两个程序的开销差不多,第二个就多一次bool的判断,几乎可以忽略。但是在log level小于debug的时候,第一种情况下,log内容中那几个string的拼接是会发生的,而第二种情况下,string的拼接是不会发生的。这里如果string比较长的话,性能影响还是比较明显的。这是一个性能相关的可能优化的点,需要格外注意一下。

]]>
http://www.wuzesheng.com/?feed=rss2&p=2407 2

Warning: mysql_query() [function.mysql-query]: Access denied for user 'root'@'localhost' (using password: NO) in /home/wuzeshengpwmuqzeeas7h4eknug/wwwroot/wp-content/plugins/quickstats/quickstats.php on line 345

Warning: mysql_query() [function.mysql-query]: A link to the server could not be established in /home/wuzeshengpwmuqzeeas7h4eknug/wwwroot/wp-content/plugins/quickstats/quickstats.php on line 345

Warning: mysql_query() [function.mysql-query]: Access denied for user 'root'@'localhost' (using password: NO) in /home/wuzeshengpwmuqzeeas7h4eknug/wwwroot/wp-content/plugins/quickstats/quickstats.php on line 346

Warning: mysql_query() [function.mysql-query]: A link to the server could not be established in /home/wuzeshengpwmuqzeeas7h4eknug/wwwroot/wp-content/plugins/quickstats/quickstats.php on line 346

Warning: mysql_fetch_row(): supplied argument is not a valid MySQL result resource in /home/wuzeshengpwmuqzeeas7h4eknug/wwwroot/wp-content/plugins/quickstats/quickstats.php on line 346