【酷壳】 闲话登月程序和程序员

2019年7月20日,是有纪念意义的一天,这天不是因为广大网民帮周杰伦在新浪微博上的超话刷到第一,而是阿波罗登月的50周年的纪念日。早在几年前,在Github上放出了当成Apollo飞船使用的源代码(当然是汇编的),但完全不明白为什么这几天会有一些中国的小朋友到这个github的issue里用灌水……,人类历史上这么伟大的一件事,为什么不借这个机会学习一下呢?下面是一些阿波罗登月与程序员相关的小故事,顺着这些东西,你可以把你的周末和精力用得更有价值。

首先,要说的是Apollo 11导航的源代码,这些代码的设计负责人叫Margaret Heafield Hamilton ,是一个女程序员,专业是数学和哲学,1960年得到一个MIT麻省理工大学的临时的软件开发职位,负责在PDP-1和LGP-30上运行天气预报的软件(注:在计算机历史上,PDP系统机器被称为Hack文化的重要推手,PDP-11且推了Unix操作系统,而Unix操作系统则是黑客文化的重要产品。参看《Unix传奇》)。然后,又为美国空军编写探测知敌方飞行的软件,之后,于1965年的时候,她加入了MIT仪器实验室,并成为了这个实验室的主管,这个实验实就是Apollo计划的一部分,她负责编写全新的月球登录的导航软件,以及后来该软件在其他项目中的各个版本。

上图是Hamilton站在她和她的麻省理工团队为阿波罗项目制作的导航软件源代码旁边,在Github上的开源的代码 – Apollo-11 (2016年开源)。我们可以看到,有两个重要的目录,一个目录叫“Comanche055”,一个目录叫“Luminary099”,前是指挥舱用的(英文为 Command Module )后者为登月舱用的(英文为 Lunar Module),这里需要说明一下的是,指挥舱是把登录舱推到月球上,在返回的时候,登录舱是被抛弃掉的,而返回到地球的是指挥舱。如果你想看这两份源代码的纸版,你可以访问这两个链接:Comanche 55 AGC Program ListingLuminary 99 REv.1 AGC Program Listing

如果你仔细比较一下这两个目录中的文件,你会发现有些文件是一样的,不但文件名一样,而且内容也一样。这说明这两个模块中的一些东西是相似的。

这些代码应该是很难读了,因为当时写这些代码的时候,C语言都没有被发明,所以基本上来说都是汇编代码了,而且还可以发现,这些代码的源文件全是以agc后缀结尾的, 看来这还不是我们平时所了解的汇编,所谓的AGC代表了运行这些代码的计算机 – Apollo Guideance Computer 。沿着这个Wikipedia的链接,你可以看到AGC这个电脑的指令是什么样的,看懂那几条指令后,这些源代码也就能读懂了。当然,因为是写成汇编的,所以,读起来还是要费点神的。不过,其中有一个文件是 THE_LUNAR_LANDING.apc 你会不会很好奇想去看看?

打开源文件,你还可以看到每个文件都有很多很多的注释,非常友好,但是也有一些注释比较有趣。这里有一组短视频带你读这些代码 – Exploring the Apollo Guidance Computer(AGC) Code,一供10个小视频,每个2分钟左右,如果你英文听边还行,可以看看,了解一下AGC的工作方式,挺有趣意思的。

下面是AGC在Apollo 1指挥舱里的样子(图片截自上面的视频),这个高质量的3D扫描来自 Simithsonian 3D: Apollo 11 Command Module

这个AGC的操作界又叫DSKY – Display 和 Keyboard的缩写,下图是一个 AGC 模拟器,其官方主页在 https://www.ibiblio.org/apollo/源代码在 Github/VirtualAGC。在这个界面上我们可以看到:下面的键盘上左边有两个键,一个是动词Verb一个是名词Noun,Verb指定操作类型,Noun指定要由Verb命令修改的数据。右边的显示器下面有三个5位的数字,这三个数值显示表示航天器姿态的矢量,以及所需速度变化的显示矢量。是的,当年的导航就靠这三个数字和里面的程序了。

 

如果你想了解AGC更多的细节,你可以看看 这篇 AGC for Dummies

另外,我在Youtube上找到了一个讲当时Apollo电脑的纪录片 – Navigation Computer,太有趣了。比如:21分51秒开始讲存储用的 Rope Memory 绕线内存,Hamilton 也出来讲了一下在这种内存上编程,画内切到一个人用个金属棍在一个像主板一样的东西上,左右穿梭,就像刺绣一样,但是绣的不是图案,而是程序……

看完上面这个纪录篇,我是非常之惊叹,惊叹于50年前的工程能力,惊叹于50年前这些人面对技术的的一丝不苟,对技术的尊重和严谨的这种精神和方法,一点都不比较今天差。

不过,最牛的还不是这个,我在Hamilton的Wikipedia词条上找到了他说的一个事件—— 当年Apollo登陆雷达开关放在了错误的位置,导致AGC收到了不少错误的信号。结果就是AGC既得执行着陆必须的计算,又要接受这些占用其15%时间的额外数据。但是AGC的程序居然可以用高优先级的任务打断低优先级的任务,于是,AGC自动剔除了低级别的任务以保证了重要的任务完成。Hamilton 原话说—— 如果当时的程序不能识别错误并从错误中恢复,我怀疑阿波罗不能成功登月。f the computer hadn’t recognized this problem and taken recovery action, I doubt if Apollo 11 would have been the successful moon landing it was。

看到这里,你有没有觉得——“这个女程序员的一小步,是整个人类的一大步”?

三年前,Apollo的源代码被开源时候,Twitter有个叫 Lin Clark 的人发了一条推:“我妈50年前的代码被放到Github上了”,是的,她是 Hamilton 的女儿,也是一个程序员,目前在 Mozilla工作,Staff Engineer,专长 WebAssembly, Rust, 和 JavaScript 。当她在Twitter上这么自豪地发了一条这样的推后,我不知道各位有什么想法?

 

尤其是那些到Apollo源代码的issue里灌水的人,你们是不是想让你们的孩子在登月100周年纪念的时候说——50年前我爹那个傻叉在Apollo的github的issue列表时灌了水?

(全文完)


关注CoolShell微信公众账号和微信小程序

(转载本站文章请注明作者和出处 酷 壳 – CoolShell ,请勿用于任何商业用途)

——=== 访问 酷壳404页面 寻找遗失儿童。 ===——

陈皓 July 21, 2019 at 07:00PM
from 酷 壳 – CoolShell https://ift.tt/2Z4Julw

VN:F [1.9.22_1171]
Rating: 0.0/5 (0 votes cast)
VN:F [1.9.22_1171]
Rating: 0 (from 0 votes)
Share

【酷壳】 如何超过大多数人

当你看到这篇文章的标题,你一定对这篇文章产生了巨大的兴趣,因为你的潜意识在告诉你,这是一篇“人生秘籍”,而且还是左耳朵写的,一定有很有参考意义,只要读完,一定可以找到超过大多数人的快车道和捷径……然而,当你看到我这样开篇时,你一定会觉得我马上就要有个转折,告诉你这是不可能的,一切都需要付出和努力……然而,你错了,这篇文章还真就是一篇“秘籍”,只要你把这些“秘籍”用起来,你就一定可以超过大多数人。而且,这篇文章只有我这个“人生导师”可以写得好。毕竟,我的生命过到了十六进制2B的年纪,踏入这个社会已超过20年,舍我其谁呢?!

P.S. 这篇文章借鉴于《如何写出无法维护的代码》一文的风格……嘿嘿

相关技巧和最佳实践

要超过别人其实还是比较简单的,尤其在今天的中国,更是简单。因为,你只看看中国的互联网,你就会发现,他们基本上全部都是在消费大众,让大众变得更为地愚蠢和傻瓜。所以,在今天的中国,你基本上不用做什么,只需要不使用中国互联网,你就很自然地超过大多数人了。当然,如果你还想跟他们彻底拉开,甩他们几个身位,把别人打到底层,下面的这些“技巧”你要多多了解一下。

在信息获取上,你要不断地向大众鼓吹下面的这些事:

  • 让大家都用百度搜索引擎查找信息,订阅微信公众号或是到知乎上学习知识……要做到这一步,你就需要把“百度一下”挂在嘴边,然后要经常在群或朋友圈中转发微信公众号的文章,并且转发知乎里的各种“如何看待……”这样的文章,让他们爱上八卦,爱上转发,爱上碎片。
  • 让大家到微博或是知识星球上粉一些大咖,密切关注他们的言论和动向……是的,告诉大家,大咖的任何想法一言一行都可以在微博、朋友圈或是知识星球上获得,让大家相信,你的成长和大咖的见闻和闲扯非常有关系,你跟牛人在一个圈子里你也会变牛。
  • 把今日头条和抖音这样的APP推荐给大家……你只需要让你有朋友成功地安装这两个APP,他们就会花大量的时间在上面,而不能自拔,要让他们安装其实还是很容易的,你要不信你就装一个试玩一会看看(嘿嘿嘿)。
  • 让大家热爱八卦,八卦并不一定是明星的八卦,还可以是你身边的人,比如,公司的同事,自己的同学,职场见闻,社会热点,争议话题,……这些东西总有一些东西会让人心态有很多微妙的变化,甚至花大量的时间去搜索和阅读大量的观点,以及花大量时间与人辩论争论,这个过程会让人上瘾,让人欲罢不能,然而这些事却和自己没有半毛钱关系。你要做的事就是转发其中一些SB或是很极端的观点,造成大家的一睦讨论后,就早早离场……
  • 利用爱国主义,让大家觉得不用学英文,不要出国,不要翻墙,咱们已经是强国了……这点其实还是很容易做到的,因为学习是比较逆人性的,所以,只要你鼓吹那些英文无用论,出国活得更惨,国家和民族都变得很强大,就算自己过得很底层,也有大国人民的感觉。

然后,在知识学习和技能训练上,让他们不得要领并产生幻觉

  • 让他们混淆认识和知识,以为开阔认知就是学习,让他们有学习和成长的幻觉……
  • 培养他们要学会使用碎片时间学习。等他们习惯利用碎片时间吃快餐后,他们就会失去精读一本书的耐性……
  • 不断地给他们各种各样“有价值的学习资料”,让他们抓不住重点,成为一个微信公众号或电子书“收藏家”……
  • 让他们看一些枯燥无味的基础知识和硬核知识,这样让他们只会用“死记硬背”的方式来学习,甚至直接让他们失去信心,直接放弃……
  • 玩具手枪是易用的,重武器是难以操控的,多给他们一些玩具,这样他们就会对玩具玩地得心应手,觉得玩玩具就是自己的专业……
  • 让他们喜欢直接得到答案的工作和学习方式,成为一个伸手党,从此学习再也不思考……
  • 告诉他们东西做出来就好了,不要追求做漂亮,做优雅,这样他们就会慢慢地变成劳动密集型……
  • 让他们觉得自己已经很努力了,剩下的就是运气,并说服他们去‘及时行乐’,然后再也找不到高阶和高效率学习的感觉……
  • 让他们觉得“读完书”、“读过书”就行了,不需要对书中的东西进行思考,进行总结,或是实践,只要囫囵吞枣尽快读完就等同于学好了……

最后,在认知和格局上,彻底打垮他们,让他们变成韭菜。

  • 让他们不要看到大的形势,只看到眼前的一亩三分地,做好一个井底之蛙。其实这很简单,比如,你不要让他们看到整个计算机互联网技术改变人类社会的趋势,你要多让他看到,从事这一行业的人有多苦逼,然后再说一下其它行业或职业有多好……
  • 宣扬一夜暴富以及快速挣钱的案例,最好让他们进入“赌博类”或是“传销类”的地方,比如:股市、数字货币……要让他们相信各种财富神话,相信他们就是那个幸运儿,他们也可以成为巴菲特,可以成为马云……
  • 告诉他们,一些看上去很难的事都是有捷径的,比如:21天就能学会机器学习,用区块链就能颠覆以及重构整个世界等等……
  • 多跟他们讲一些小人物的励志的故事,这样让他们相信,不需要学习高级知识,不需要掌握高级技能,只需要用低等的知识和低级的技能,再加上持续不断拼命重复现有的工作,终有一天就会成功……
  • 多让他们跟别人比较,人比人不会气死人,但是会让人变得浮躁,变得心急,变得焦虑,当一个人没有办法控制自己的情绪,没有办法让自己静下心来,人会失去耐性和坚持,开始好大喜欢功,开始装逼,开始歪门邪道剑走偏锋……
  • 让他们到体制内的一些非常稳定的地方工作,这样他们拥有不思进取、怕承担责任、害怕犯错、喜欢偷懒、得过且过的素质……
  • 让他们到体制外的那些喜欢拼命喜欢加班的地方工作,告诉他们爱拼才会赢,努力加班是一种福报,青春就是用来拼的,让他们喜欢上使蛮力的感觉……
  • 告诉他们你的行业太累太辛苦,干不到30岁。让他们早点转行,不要耽误人生和青春……
  • 当他们要做决定的时候,一定要让他们更多的关注自己会失去的东西,而不是会得到的东西。培养他们患得患失心态,让他们认识不到事物真正的价值,失去判断能力……(比如:让他们觉得跟对人拍领导的马屁忠于公司比自我的成长更有价值)
  • 告诉他们,你现有的技能和知识不用更新,就能过好一辈子,新出来的东西没有生命力的……这样他们就会像我们再也不学习的父辈一样很快就会被时代所抛弃……
  • 每个人都喜欢在一些自己做不到的事上找理由,这种能力不教就会,比如,事情太多没有时间,因为工作上没有用到,等等,你要做的就是帮他们为他们做不到的事找各种非常合理的理由,比如:没事的,一切都是最好的安排;你得不到的那个事没什么意思;你没有面好主要原因是那个面试官问的问题都是可以上网查得到的知识,而不没有问到你真正的能力上;这些东西学了不用很快会忘了,等有了环境再学也不迟……

最后友情提示一下,上述的这些“最佳实践”你要小心,是所谓,贩毒的人从来不吸毒,开赌场的人从来不赌博!所以,你要小心别自己也掉进去了!这就是“欲练神功,必先自宫”的道理。

相关原理和思维模型

对于上面的这些技巧还有很多很多,你自己也可以发明或是找到很多。所以,我来讲讲这其中的一些原理。

一般来说,超过别人一般来说就是两个维度:

  1. 在认知、知识和技能上。这是一个人赖以立足社会的能力(参看《程序员的荒谬之言还是至理名言?》和《21天教你学会C++》)
  2. 在领导力上。所谓领导力就是你跑在别人前面,你得要有比别人更好的能力更高的标准(参看《技术人员发展之路》)

首先,我们要明白,人的技能是从认识开始,然后通过学校、培训或是书本把“零碎的认知”转换成“系统的知识”,而有要把知识转换成技能,就需要训练和实践,这样才能完成从:认识 -> 知识 -> 技能 的转换。这个转换过程是需要耗费很多时间和精力的,而且其中还需要有强大的学习能力和动手能力,这条路径上有很多的“关卡”,每道关卡都会过滤掉一大部分人。比如:对于一些比较枯燥的硬核知识来说,90%的人基本上就倒下来,不是因为他们没有智商,而是他们没有耐心。

认知

要在认知上超过别人,就要在下面几个方面上做足功夫:

1)信息渠道。试想如果别人的信息源没有你的好,那么,这些看不见信息源的人,只能接触得到二手信息甚至三手信息,只能获得被别人解读过的信息,这些信息被三传两递后必定会有错误和失真,甚至会被传递信息的中间人hack其中的信息(也就是“中间人攻击”),而这些找不出信息源的人,只能“被人喂养”,于是,他们最终会被困在信息的底层,永世不得翻身。(比如:学习C语言,放着原作者K&R的不用,硬要用错误百出谭浩强的书,能有什么好呢?)

2)信息质量。信息质量主要表现在两个方面,一个是信息中的燥音,另一个是信息中的质量等级,我们都知道,在大数据处理中有一句名言,叫 garbage in garbage out,你天天看的都是垃圾,你的思想和认识也只有垃圾。所以,如果你的信息质量并不好的话,你的认知也不会好,而且你还要花大量的时间来进行有价值信息的挖掘和处理。

3)信息密度。优质的信息,密度一般都很大,因为这种信息会逼着你去干这么几件事,a)搜索并学习其关联的知识,b)沉思和反省,c)亲手去推理、验证和实践……一般来说,经验性的文章会比知识性的文章会更有这样的功效。比如,类似于像 Effiective C++/Java,设计模式,Unix编程艺术,算法导论等等这样的书就是属于这种密度很大的书,而像Netflix的官方blogAWS CTO的blog等等地方也会经常有一些这样的文章。

知识

要在知识上超过别人,你就需要在下面几个方面上做足功夫:

1)知识树(图)。任何知识,只在点上学习不够的,需要在面上学习,这叫系统地学习,这需要我们去总结并归纳知识树或知识图,一个知识面会有多个知识板块组成,一个板块又有各种知识点,一个知识点会导出另外的知识点,各种知识点又会交叉和依赖起来,学习就是要系统地学习整个知识树(图)。而我们都知道,对于一棵树来说,“根基”是非常重要的,所以,学好基础知识也是非常重要的,对于一个陌生的地方,有一份地图是非常重要的,没有地图的你只会乱窜,只会迷路、练路、走冤枉路!

2)知识缘由。任何知识都是有缘由的,了解一个知识的来龙去脉和前世今生,会让你对这个知识有非常强的掌握,而不再只是靠记忆去学习。靠记忆去学习是一件非常糟糕的事。而对于一些操作性的知识(不需要了解由来的),我把其叫操作知识,就像一些函数库一样,这样的知识只要学会查文档就好了。能够知其然,知其所以然的人自然会比识知识到表皮的人段位要高很多。

3)方法套路。学习不是为了找到答案,而是找到方法。就像数学一样,你学的是方法,是解题思路,是套路,会用方程式解题的和不会用方程式解题的在解题效率上不可比较,而在微积分面前,其它的解题方法都变成了渣渣。你可以看到,掌握高级方法的人比别人的优势有多大,学习的目的就是为了掌握更为高级的方法和解题思路

技能

要在技能上超过别人,你就需要在下面几个方面做足功夫:

1)精益求精。如果你想拥有专业的技能,你要做不仅仅是拼命地重复一遍又一遍的训练,而是在每一次重复训练时你都要找到更好的方法,总结经验,让新的一遍能够更好,更漂亮,更有效率,否则,用相同的方法重复,那你只不过在搬砖罢了。

2)让自己犯错。犯错是有利于成长的,这是因为出错会让人反思,反思更好的方法,反思更完美的方案,总结教训,寻求更好更完美的过程,是技能升级的最好的方式。尤其是当你在出错后,被人鄙视,被人嘲笑后,你会有更大的动力提升自己,这样的动力才是进步的源动力。当然,千万不要同一个错误重复地犯!

3)找高手切磋。下过棋,打个球的人都知道,你要想提升自己的技艺,你必需找高手切磋,在和高手切磋的过程中你会感受到高手的技能和方法,有时候你会情不自禁地哇地一下,我靠,还可以这么玩!

领导力

最后一个是领导力,要有领导力或是影响力这个事并不容易,这跟你的野心有多大,好胜心有多强 ,你愿意付出多少很有关系,因为一个人的领导力跟他的标准很有关系,因为有领导力的人的标准比绝大多数人都要高。

1)识别自己的特长和天赋。首先,每个人DNA都可能或多或少都会有一些比大多数人NB的东西(当然,也可能没有),如果你有了,那么在你过去的人生中就一定会表现出来了,就是那种大家遇到这个事会来请教你的寻求你帮助的现象。那种,别人要非常努力,而且毫不费劲的事。一旦你有了这样的特长或天赋,那你就要大力地扩大你的领先优势,千万不要进到那些会限制你优势的地方。你是一条鱼,你就一定要把别人拉到水里来玩,绝对不要去陆地上跟别人拼,不断地在自己的特长和天赋上扩大自己的领先优势,彻底一骑绝尘。

2)识别自己的兴趣和事业。没有天赋也没有问题,还有兴趣点,都说兴趣是最好的老师,当年,Linus就是在学校里对minx着迷了,于是整出个Linux来,这就是兴趣驱动出的东西,一般来说,兴趣驱动的事总是会比那些被动驱动的更好。但是,这里我想说明一下什么叫“真∙兴趣”,真正的兴趣不是那种三天热度的东西,而是那种,你愿意为之付出一辈子的事,是那种无论有多大困难有多难受你都要死磕的事,这才是“真∙兴趣”,这也就是你的“野心”和“好胜心”所在,其实上升到了你的事业。相信我,绝大多数人只有职业而没有事业的。

3)建立高级的习惯和方法。没有天赋没有野心,也还是可以跟别人拼习惯拼方法的,只要你有一些比较好的习惯和方法,那么你一样可以超过大多数人。对此,在习惯上你要做到比较大多数人更自律,更有计划性,更有目标性,比如,每年学习一门新的语言或技术,并可以参与相关的顶级开源项目,每个月训练一个类算法,掌握一种算法,每周阅读一篇英文论文,并把阅读笔记整理出来……自律的是非常可怕的。除此之外,你还需要在方法上超过别人,你需要满世界的找各种高级的方法,其中包括,思考的方法,学习的方法、时间管理的方法、沟通的方法这类软实力的,还有,解决问题的方法(trouble shooting 和 problem solving),设计的方法,工程的方法,代码的方法等等硬实力的,一开始照猫画虎,时间长了就可能会自己发明或推导新的方法。

4)勤奋努力执着坚持。如果上面三件事你都没有也没有能力,那还有最后一件事了,那就是勤奋努力了,就是所谓的“一万小时定律”了(参看《21天教你学会C++》中的十年学编程一节),我见过很多不聪明的人,悟性也不够(比如我就是一个),别人学一个东西,一个月就好了,而我需要1年甚至更长,但是很多东西都是死的,只要肯花时间就有一天你会搞懂的,耐不住我坚持十年二十年,聪明的人发明个飞机飞过去了,笨一点的人愚公移山也过得去,因为更多的人是懒人,我不用拼过聪明人,我只用拼过那些懒人就好了。

好了,就这么多,如果哪天你变得消极和不自信,你要来读读我的这篇文章,子曰:温故而知新。

(全文完)


关注CoolShell微信公众账号和微信小程序

(转载本站文章请注明作者和出处 酷 壳 – CoolShell ,请勿用于任何商业用途)

——=== 访问 酷壳404页面 寻找遗失儿童。 ===——

陈皓 June 22, 2019 at 01:47PM
from 酷 壳 – CoolShell http://bit.ly/2Y5Op5d

VN:F [1.9.22_1171]
Rating: 0.0/5 (0 votes cast)
VN:F [1.9.22_1171]
Rating: 0 (from 0 votes)
Share

【酷壳】 HTTP API 认证授权术

我们知道,HTTP是无状态的,所以,当我们需要获得用户是否在登录的状态时,我们需要检查用户的登录状态,一般来说,用户的登录成功后,服务器会发一个登录凭证(又被叫作Token),就像你去访问某个公司,在前台被认证过合法后,这个公司的前台会给你的一个访客卡一样,之后,你在这个公司内去到哪都用这个访客卡来开门,而不再校验你是哪一个人。在计算机的世界里,这个登录凭证的相关数据会放在两种地方,一个地方在用户端,以Cookie的方式(一般不会放在浏览器的Local Storage,因为这很容易出现登录凭证被XSS攻击),另一个地方是放在服务器端,又叫Session的方式(SessonID存于Cookie)。

但是,这个世界还是比较复杂的,除了用户访问,还有用户委托的第三方的应用,还有企业和企业间的调用,这里,我想把业内常用的一些 API认证技术相对系统地总结归纳一下,这样可以让大家更为全面的了解这些技术。注意,这是一篇长文!

本篇文章会覆盖如下技术:

  • HTTP Basic
  • Digest Access
  • App Secret Key + HMAC
  • JWT – JSON Web Tokens
  • OAuth 1.0 – 3 legged & 2 legged
  • OAuth 2.0 – Authentication Code & Client Credential

HTTP Basic

HTTP Basic 是一个非常传统的API认证技术,也是一个比较简单的技术。这个技术也就是使用 usernamepassword 来进行登录。整个过程被定义在了 RFC 2617 中,也被描述在了 Wikipedia: Basic Access Authentication 词条中,同时也可以参看 MDN HTTP Authentication

其技术原理如下:

  1. usernamepassword 做成  username:password 的样子(用冒号分隔)
  2. 进行Base64编码。Base64("username:password") 得到一个字符串(如:把 haoel:coolshell 进行base64 后可以得到 aGFvZW86Y29vbHNoZWxsCg
  3. aGFvZW86Y29vbHNoZWxsCg放到HTTP头中 Authorization 字段中,形成 Authorization: Basic aGFvZW86Y29vbHNoZWxsCg,然后发送到服务端。
  4. 服务端如果没有在头里看到认证字段,则返回401错,以及一个个WWW-Authenticate: Basic Realm='HelloWorld' 之类的头要求客户端进行认证。之后如果没有认证通过,则返回一个401错。如果服务端认证通过,那么会返回200。

我们可以看到,使用Base64的目的无非就是为了把一些特殊的字符给搞掉,这样就可以放在HTTP协议里传输了。而这种方式的问题最大的问题就是把用户名和口令放在网络上传,所以,一般要配合TLS/SSL的安全加密方式来使用。我们可以看到 JIRA Cloud 的API认证支持HTTP Basic 这样的方式。

但我们还是要知道,这种把用户名和密码同时放在公网上传输的方式有点不太好,因为Base64不是加密协议,保是编码协议,所以就算是有HTTPS作为安全保护,给人的感觉还是不放心。

Digest Access

中文称“HTTP 摘要认证”,最初被定义在了 RFC 2069 文档中(后来被 RFC 2617 引入了一系列安全增强的选项;“保护质量”(qop)、随机数计数器由客户端增加、以及客户生成的随机数)。

其基本思路是,请求方把用户名口令和域做一个MD5 –  MD5(username:realm:password) 然后传给服务器,这样就不会在网上传用户名和口令了,但是,因为用户名和口令基本不会变,所以,这个MD5的字符串也是比较固定的,因此,这个认证过程在其中加入了两个事,一个是 nonce 另一个是 qop

  • 首先,调用方发起一个普通的HTTP请求。比如:GET /coolshell/admin/ HTTP/1.1
  • 服务端自然不能认证能过,服务端返回401错误,并且在HTTP头里的 WWW-Authenticate 包含如下信息:
 WWW-Authenticate: Digest realm="testrealm@host.com",
                        qop="auth,auth-int",
                        nonce="dcd98b7102dd2f0e8b11d0f600bfb0c093",
                        opaque="5ccc069c403ebaf9f0171e9517f40e41"
  • 其中的 nonce 为服务器端生成的随机数,然后,客户端做 HASH1=MD5(MD5(username:realm:password):nonce:cnonce) ,其中的 cnonce 为客户端生成的随机数,这样就可以使得整个MD5的结果是不一样的。
  • 如果 qop 中包含了 auth ,那么还得做  HASH2=MD5(method:digestURI) 其中的 method 就是HTTP的请求方法(GET/POST…),digestURI 是请求的URL。
  • 如果 qop 中包含了 auth-init ,那么,得做  HASH2=MD5(method:digestURI:MD5(entityBody)) 其中的 entityBody 就是HTTP请求的整个数据体。
  • 然后,得到 response = MD5(HASH1:nonce:nonceCount:cnonce:qop:HASH2) 如果没有 qopresponse = MD5(HA1:nonce:HA2)
  • 最后,我们的客户端对服务端发起如下请求—— 注意HTTP头的 Authorization: Digest ...
GET /dir/index.html HTTP/1.0
Host: localhost
Authorization: Digest username="Mufasa",
                     realm="testrealm@host.com",
                     nonce="dcd98b7102dd2f0e8b11d0f600bfb0c093",
                     uri="%2Fcoolshell%2Fadmin",
                     qop=auth,
                     nc=00000001,
                     cnonce="0a4f113b",
                     response="6629fae49393a05397450978507c4ef1",
                     opaque="5ccc069c403ebaf9f0171e9517f40e41"

维基百科上的 Wikipedia: Digest access authentication 词条非常详细地描述了这个细节。

摘要认证这个方式会比之前的方式要好一些,因为没有在网上传递用户的密码,而只是把密码的MD5传送过去,相对会比较安全,而且,其并不需要是否TLS/SSL的安全链接。但是,别看这个算法这么复杂,最后你可以发现,整个过程其实关键是用户的password,这个password如果不够得杂,其实是可以被暴力破解的,而且,整个过程是非常容易受到中间人攻击——比如一个中间人告诉客户端需要一个

App Secret Key + HMAC

先说HMAC技术,这个东西来自于MAC – Message Authentication Code,是一种用于给消息签名的技术,也就是说,我们怕消息在传递的过程中被人修改,所以,我们需要用对消息进行一个MAC算法,得到一个摘要字串,然后,接收方得到消息后,进行同样的计算,然后比较这个MAC字符串,如果一致,则表明没有被修改过(整个过程参看下图)。而HMAC – Hash-based Authenticsation Code,指的是利用Hash技术完成这一工作,比如:SHA-256算法。

 

(图片来自 Wikipedia – MAC 词条

我们再来说App ID,这个东西跟验证没有关系,只是用来区分,是谁来调用API的,就像我们每个人的身份证一样,只是用来标注不同的人,不是用来做身份认证的。与前面的不同之处是,这里,我们需要用App ID 来映射一个用于加密的密钥,这样一来,我们就可以在服务器端进行相关的管理,我们可以生成若干个密钥对(AppID, AppSecret),并可以有更细粒度的操作权限管理。

把AppID和HMAC用于API认证,目前来说,玩得最好最专业的应该是AWS了,我们可以通过S3的API请求签名文档看到AWS是怎么玩的。整个过程还是非常复杂的,可以通过下面的图片流程看个大概。基本上来说,分成如下几个步骤:

  1. 把HTTP的请求(方法、URI、查询字串、头、签名头,body)打个包叫 CanonicalRequest,作个SHA-256的签名,然后再做一个base16的编码
  2. 把上面的这个签名和签名算法 AWS4-HMAC-SHA256、时间戳、Scop,再打一个包,叫 StringToSign
  3. 准备签名,用 AWSSecretAccessKey来对日期签一个 DataKey,再用 DataKey 对要操作的Region签一个 DataRegionKey ,再对相关的服务签一个DataRegionServiceKey ,最后得到 SigningKey.
  4. 用第三步的 SigningKey来对第二步的 StringToSign 签名。

 

最后,发出HTTP Request时,在HTTP头的 Authorization字段中放入如下的信息:

Authorization: AWS4-HMAC-SHA256 
               Credential=AKIDEXAMPLE/20150830/us-east-1/iam/aws4_request, 
               SignedHeaders=content-type;host;x-amz-date, 
               Signature=5d672d79c15b13162d9279b0855cfba6789a8edb4c82c400e06b5924a6f2b5d7

 

其中的  AKIDEXAMPLE 是 AWS Access Key ID, 也就是所谓的 AppID,服务器端会根据这个AppID来查相关的 Secret Access Key,然后再验证签名。如果,你对这个过程有点没看懂的话,你可以读一读这篇文章——《Amazon S3 Rest API with curl》这篇文章里有好些代码,代码应该是最有细节也是最准确的了。

这种认证的方式好处在于,AppID和AppSecretKey,是由服务器的系统开出的,所以,是可以被管理的,AWS的IAM就是相关的管理,其管理了用户、权限和其对应的AppID和AppSecretKey。但是不好的地方在于,这个东西没有标准 ,所以,各家的实现很不一致。比如: Acquia 的 HMAC微信的签名算法 (这里,我们需要说明一下,微信的API没有遵循HTTP协议的标准,把认证信息放在HTTP 头的 Authorization 里,而是放在body里)

JWT – JSON Web Tokens

JWT是一个比较标准的认证解决方案,这个技术在Java圈里应该用的是非常普遍的。JWT签名也是一种MAC(Message Authentication Code)的方法。JWT的签名流程一般是下面这个样子:

  1. 用户使用用户名和口令到认证服务器上请求认证。
  2. 认证服务器验证用户名和口令后,以服务器端生成JWT Token,这个token的生成过程如下:
    • 认证服务器还会生成一个 Secret Key(密钥)
    • 对JWT Header和 JWT Payload分别求Base64。在Payload可能包括了用户的抽象ID和的过期时间。
    • 用密钥对JWT签名 HMAC-SHA256(SecertKey, Base64UrlEncode(JWT-Header)+'.'+Base64UrlEncode(JWT-Payload));
  3. 然后把 base64(header).base64(payload).signature 作为 JWT token返回客户端。
  4. 客户端使用JWT Token向应用服务器发送相关的请求。这个JWT Token就像一个临时用户权证一样。

当应用服务器收到请求后:

  1. 应用服务会检查 JWT  Token,确认签名是正确的。
  2. 然而,因为只有认证服务器有这个用户的Secret Key(密钥),所以,应用服务器得把JWT Token传给认证服务器。
  3. 认证服务器通过JWT Payload 解出用户的抽象ID,然后通过抽象ID查到登录时生成的Secret Key,然后再来检查一下签名。
  4. 认证服务器检查通过后,应用服务就可以认为这是合法请求了。

我们可以看以,上面的这个过程,是在认证服务器上为用户动态生成 Secret Key的,应用服务在验签的时候,需要到认证服务器上去签,这个过程增加了一些网络调用,所以,JWT除了支持HMAC-SHA256的算法外,还支持RSA的非对称加密的算法。

使用RSA非对称算法,在认证服务器这边放一个私钥,在应用服务器那边放一个公钥,认证服务器使用私钥加密,应用服务器使用公钥解密,这样一来,就不需要应用服务器向认证服务器请求了,但是,RSA是一个很慢的算法,所以,虽然你省了网络调用,但是却费了CPU,尤其是Header和Payload比较长的时候。所以,一种比较好的玩法是,如果我们把header 和 payload简单地做SHA256,这会很快,然后,我们用RSA加密这个SHA256出来的字符串,这样一来,RSA算法就比较快了,而我们也做到了使用RSA签名的目的。

最后,我们只需要使用一个机制在认证服务器和应用服务器之间定期地换一下公钥私钥对就好了。

这里强烈建议全文阅读 Anglar 大学的 《JSW:The Complete Guide to JSON Web Tokens

OAuth 1.0

OAuth也是一个API认证的协议,这个协议最初在2006年由Twitter的工程师在开发OpenID实现的时候和社交书签网站Ma.gnolia时发现,没有一种好的委托授权协议,后来在2007年成立了一个OAuth小组,知道这个消息后,Google员工也加入进来,并完善有善了这个协议,在2007年底发布草案,过一年后,在2008年将OAuth放进了IETF作进一步的标准化工作,最后在2010年4月,正式发布OAuth 1.0,即:RFC 5849 (这个RFC比起TCP的那些来说读起来还是很轻松的),不过,如果你想了解其前身的草案,可以读一下 OAuth Core 1.0 Revision A ,我在下面做个大概的描述。

根据RFC 5849,可以看到 OAuth 的出现,目的是为了,用户为了想使用一个第三方的网络打印服务来打印他在某网站上的照片,但是,用户不想把自己的用户名和口令交给那个第三方的网络打印服务,但又想让那个第三方的网络打印服务来访问自己的照片,为了解决这个授权的问题,OAuth这个协议就出来了。

  • 这个协议有三个角色:
    • User(照片所有者-用户)
    • Consumer(第三方照片打印服务)
    • Service Provider(照片存储服务)
  • 这个协义有三个阶段:
    • Consumer获取Request Token
    • Service Provider 认证用户并授权Consumer
    • Consumer获取Access Token调用API访问用户的照片

整个授权过程是这样的:

  1. Consumer(第三方照片打印服务)需要先上Service Provider获得开发的 Consumer Key 和 Consumer Secret
  2. 当 User 访问 Consumer 时,Consumer 向 Service Provide 发起请求请求Request Token (需要对HTTP请求签名)
  3. Service Provide 验明 Consumer 是注册过的第三方服务商后,返回 Request Token(oauth_token)和 Request Token Secret (oauth_token_secret
  4. Consumer 收到 Request Token 后,使用HTTP GET 请求把 User 切到 Service Provide 的认证页上(其中带上Request Token),让用户输入他的用户和口令。
  5. Service Provider 认证 User 成功后,跳回 Consumer,并返回 Request Token (oauth_token)和 Verification Code(oauth_verifier
  6. 接下来就是签名请求,用Request Token 和 Verification Code 换取 Access Token (oauth_token)和 Access Token Secret (oauth_token_secret)
  7. 最后使用Access Token 访问用户授权访问的资源。

下图附上一个Yahoo!的流程图可以看到整个过程的相关细节。

因为上面这个流程有三方:User,Consumer 和 Service Provide,所以,又叫 3-legged flow,三脚流程。OAuth 1.0 也有不需要用户参与的,只有Consumer 和 Service Provider 的, 也就是 2-legged flow 两脚流程,其中省掉了用户认证的事。整个过程如下所示:

  1. Consumer(第三方照片打印服务)需要先上Service Provider获得开发的 Consumer Key 和 Consumer Secret
  2. Consumer 向 Service Provide 发起请求请求Request Token (需要对HTTP请求签名)
  3. Service Provide 验明 Consumer 是注册过的第三方服务商后,返回 Request Token(oauth_token)和 Request Token Secret (oauth_token_secret
  4. Consumer 收到 Request Token 后,直接换取 Access Token (oauth_token)和 Access Token Secret (oauth_token_secret)
  5. 最后使用Access Token 访问用户授权访问的资源。

最后,再来说一说OAuth中的签名。

  • 我们可以看到,有两个密钥,一个是Consumer注册Service Provider时由Provider颁发的 Consumer Secret,另一个是 Token Secret。
  • 签名密钥就是由这两具密钥拼接而成的,其中用 &作连接符。假设 Consumer Secret 为 j49sk3j29djd 而 Token Secret 为dh893hdasih9那个,签名密钥为:j49sk3j29djd&dh893hdasih9
  • 在请求Request/Access Token的时候需要对整个HTTP请求进行签名(使用HMAC-SHA1和HMAC-RSA1签名算法),请求头中需要包括一些OAuth需要的字段,如:
    • Consumer Key : 也就是所谓的AppID
    • Token: Request Token 或 Access Token
    • Signature Method :签名算法比如:HMAC-SHA1
    • Timestamp:过期时间
    • Nonce:随机字符串
    • Call Back:回调URL

下图是整个签名的示意图:

图片还是比较直观的,我就不多解释了。

OAuth 2.0

在前面,我们可以看到,从Digest Access, 到AppID+HMAC,再到JWT,再到OAuth 1.0,这些个API认证都是要向Client发一个密钥(或是用密码)然后用HASH或是RSA来签HTTP的请求,这其中有个主要的原因是,以前的HTTP是明文传输,所以,在传输过程中很容易被篡改,于是才搞出来一套的安全签名机制,所以,这些个认证的玩法是可以在HTTP明文协议下玩的。

这种使用签名方式大家可以看到是比较复杂的,所以,对于开发者来说,也是很不友好的,在组织签名的那些HTTP报文的时候,各种,URLEncode和Base64,还要对Query的参数进行排序,然后有的方法还要层层签名,非常容易出错,另外,这种认证的安全粒度比较粗,授权也比较单一,对于有终端用户参与的移动端来说也有点不够。所以,在2012年的时候,OAuth 2.0 的 RFC 6749 正式放出。

OAuth 2.0依赖于TLS/SSL的链路加密技术(HTTPS),完全放弃了签名的方式,认证服务器再也不返回什么 token secret 的密钥了,所以,OAuth 2.0是完全不同于1.0 的,也是不兼容的。目前,Facebook 的 Graph API 只支持OAuth 2.0协议,Google 和 Microsoft Azure 也支持Auth 2.0,国内的微信和支付宝也支持使用OAuth 2.0。

下面,我们来重点看一下OAuth 2.0的两个主要的Flow:

  • 一个是Authorization Code Flow, 这个是 3 legged 的
  • 一个是Client Credential Flow,这个是 2 legged 的。
Authorization Code Flow

Authorization Code 是最常使用的OAuth 2.0的授权许可类型,它适用于用户给第三方应用授权访问自己信息的场景。这个Flow也是OAuth 2.0四个Flow中我个人觉得最完整的一个Flow,其流程图如下所示。

 

下面是对这个流程的一个细节上的解释:

1)当用户(Resource Owner)访问第三方应用(Client)的时候,第三方应用会把用户带到认证服务器(Authorization Server)上去,主要请求的是 /authorize API,其中的请求方式如下所示。

https://login.authorization-server.com/authorize?
        client_id=6731de76-14a6-49ae-97bc-6eba6914391e
        &response_type=code
        &redirect_uri=http%3A%2F%2Fexample-client.com%2Fcallback%2F
        &scope=read
        &state=xcoiv98CoolShell3kch

其中:

    • client_id为第三方应用的App ID
    • response_type=code为告诉认证服务器,我要走Authorization Code Flow。
    • redirect_uri意思是我跳转回第三方应用的URL
    • scope意是相关的权限
    • state 是一个随机的字符串,主要用于防CSRF攻击。

2)当Authorization Server收到这个URL请求后,其会通过 client_id来检查 redirect_uriscope是否合法,如果合法,则弹出一个页面,让用户授权(如果用户没有登录,则先让用户登录,登录完成后,出现授权访问页面)。

3)当用户授权同意访问以后,Authorization Server 会跳转回 Client ,并以其中加入一个 Authorization Code。 如下所示:

https://example-client.com/callback?
        code=Yzk5ZDczMzRlNDEwYlrEqdFSBzjqfTG
        &state=xcoiv98CoolShell3kch

我们可以看到,

    • 请流动的链接是第 1)步中的 redirect_uri
    • 其中的 state 的值也和第 1)步的 state一样。

4)接下来,Client 就可以使用 Authorization Code 获得 Access Token。其需要向 Authorization Server 发出如下请求。

POST /oauth/token HTTP/1.1
Host: authorization-server.com
 
code=Yzk5ZDczMzRlNDEwYlrEqdFSBzjqfTG
&grant_type=code
&redirect_uri=https%3A%2F%2Fexample-client.com%2Fcallback%2F
&client_id=6731de76-14a6-49ae-97bc-6eba6914391e
&client_secret=JqQX2PNo9bpM0uEihUPzyrh

5)如果没什么问题,Authorization 会返回如下信息。

{
  "access_token": "iJKV1QiLCJhbGciOiJSUzI1NiI",
  "refresh_token": "1KaPlrEqdFSBzjqfTGAMxZGU",
  "token_type": "bearer",
  "expires": 3600,
  "id_token": "eyJ0eXAiOiJKV1QiLCJhbGciO.eyJhdWQiOiIyZDRkM..."
}

其中,

    • access_token就是访问请求令牌了
    • refresh_token用于刷新 access_token
    • id_token 是JWT的token,其中一般会包含用户的OpenID

6)接下来就是用 Access Token 请求用户的资源了。

GET /v1/user/pictures
Host: https://example.resource.com

Authorization: Bearer iJKV1QiLCJhbGciOiJSUzI1NiI

 

 Client Credential Flow

Client Credential 是一个简化版的API认证,主要是用于认证服务器到服务器的调用,也就是没有用户参与的的认证流程。下面是相关的流程图。

这个过程非常简单,本质上就是Client用自己的 client_idclient_secret向Authorization Server 要一个 Access Token,然后使用Access Token访问相关的资源。

请求示例

POST /token HTTP/1.1
Host: server.example.com
Content-Type: application/x-www-form-urlencoded

grant_type=client_credentials
&client_id=czZCaGRSa3F0Mzpn
&client_secret=7Fjfp0ZBr1KtDRbnfVdmIw

返回示例

{
  "access_token":"MTQ0NjJkZmQ5OTM2NDE1ZTZjNGZmZjI3",
  "token_type":"bearer",
  "expires_in":3600,
  "refresh_token":"IwOGYzYTlmM2YxOTQ5MGE3YmNmMDFkNTVk",
  "scope":"create"
}

这里,容我多扯一句,微信公从平台的开发文档中,使用了OAuth 2.0 的 Client Credentials的方式(参看文档“微信公众号获取access token”),我截了个图如下所谓。我们可以看到,微信公众号使用的是GET方式的请求,把AppID和AppSecret放在了URL中,虽然这也符合OAuth 2.0,但是并不好,因为大多数网关代理会把整个URI请求记到日志中。我们只要脑补一下腾讯的网关的Access Log,里面的日志一定会有很多的各个用户的AppID和AppSecret……

 

小结

讲了这么多,我们来小结一下(下面的小结可能会有点散)

两个概念和三个术语
  • 区分两个概念:Authentication(认证) 和 Authorization (授权),前者是证明请求者是身份,就像身份证一样,后者是为了获得权限。身份是区别于别人的证明,而权限是证明自己的特权。Authentication为了证明操作的这个人就是他本人,需要提供密码、短信验证码,甚至人脸识别。Authorization 则是不需要在所有的请求都需要验人,是在经过Authorization后得到一个Token,这就是Authorization。就像护照和签证一样。
  • 区分三个概念:编码Base64Encode、签名HMAC、加密RSA。编码是为了更的传输,等同于明文,签名是为了信息不能被篡改,加密是为了不让别人看到是什么信息。
明白一些初衷
  • 使用复杂地HMAC哈希签名方式主要是应对当年没有TLS/SSL加密链路的情况。
  • JWT把 uid 放在 Token中目的是为了去掉状态,但不能让用户修改,所以需要签名。
  • OAuth 1.0区分了两个事,一个是第三方的Client,一个是真正的用户,其先拿Request Token,再换Access Token的方法主要是为了把第三方应用和用户区分开来。
  • 用户的Password是用户自己设置的,复杂度不可控,服务端颁发的Serect会很复杂,但主要目的是为了容易管理,可以随时注销掉。
  • OAuth 协议有比所有认证协议有更为灵活完善的配置,如果使用AppID/AppSecret签名的方式,又需要做到可以有不同的权限和可以随时注销,那么你得开发一个像AWS的IAM这样的账号和密钥对管理的系统。
相关的注意事项
  • 无论是哪种方式,我们都应该遵循HTTP的规范,把认证信息放在 Authorization HTTP 头中。
  • 不要使用GET的方式在URL中放入secret之类的东西,因为很多proxy或gateway的软件会把整个URL记在Access Log文件中。
  • 密钥Secret相当于Password,但他是用来加密的,最好不要在网络上传输,如果要传输,最好使用TLS/SSL的安全链路。
  • HMAC中无论是MD5还是SHA1/SHA2,其计算都是非常快的,RSA的非对称加密是比较耗CPU的,尤其是要加密的字符串很长的时候。
  • 最好不要在程序中hard code 你的 Secret,因为在github上有很多黑客的软件在监视各种Secret,千万小心!这类的东西应该放在你的配置系统或是部署系统中,在程序启动时设置在配置文件或是环境变量中。
  • 使用AppID/AppSecret,还是使用OAuth1.0a,还是OAuth2.0,还是使用JWT,我个人建议使用TLS/SSL下的OAuth 2.0。
  • 密钥是需要被管理的,管理就是可以新增可以撤销,可以设置账户和相关的权限。最好密钥是可以被自动更换的。
  • 认证授权服务器(Authorization Server)和应用服务器(App Server)最好分开。

(全文完)


关注CoolShell微信公众账号和微信小程序

(转载本站文章请注明作者和出处 酷 壳 – CoolShell ,请勿用于任何商业用途)

——=== 访问 酷壳404页面 寻找遗失儿童。 ===——

陈皓 May 09, 2019 at 09:37PM
from 酷 壳 – CoolShell http://bit.ly/2JdiUCG

VN:F [1.9.22_1171]
Rating: 0.0/5 (0 votes cast)
VN:F [1.9.22_1171]
Rating: 0 (from 0 votes)
Share

【酷壳】 打造高效的工作环境 – Shell 篇

注:本文由雷俊(Javaer/Emacser)和我一起编辑,所以文章版权归雷俊与我共同所有,转载者必需注明出处和我们两位作者。原文最早发于酷壳微信公众号,后来我又做了一些修改,再发到博客这边。

程序员是一个很懒的群体,总想着能够让代码为自己干活,他们不断地把工作生活中的一些事情用代码自动化了,从而让整个社会的效率运作地越来越高。所以,程序员在准备去优化这个世界的时候,都会先要优化自己的工作环境,是所谓“工欲善其事,必先利其器”。

我们每个程序员都应该打造一套让自己更为高效的工作环境。那怕就是让你少输入一次命令,少按一次键,少在鼠标和键盘间切换一次,都会让程序员的工作变得更为的高效。所以,程序员一般需要一台性能比较好,不会因为开了太多的网页或程序就卡得不行的电脑,还要配备多个显示器,一个显示器写代码,一个查文档,一个测试运行结果,而不必在各种窗口来来回回的切换……在大量的窗口间切换经常会迷路,而且也容易出错(分不清线上或测试环境)……

除了硬件上的装备,软件上也是能够提升程序员生产力的地方,在软件层面提升程序员生产力的东西有一个很重要的事就是命令行和脚本,使用鼠标和图形界面则会大大降低程序员的生产力。酷壳以前也写过一些,如《你可能不知道的Shell》和《 应该知道的Linux技巧》,但是Unix/Linux Shell就是一个大宝库,怎么写也写不完,不然,怎么会有“Where is the Shell, there is a way”。

命令行

在不同的操作系统下,都有着很不错的命令行工具,比如 Mac 下的 Iterm2,Linux 下的原生命令行,如果你是在 Windows 下工作,问题也不大,因为 Windows 下现在有了 WSL。WSL 提供了一个由微软开发的Linux兼容的内核接口(不包含Linux内核代码),然后可以在其上运行GNU用户空间,例如 Ubuntu,openSUSE,SUSE Linux Enterprise Server,Debian和Kali Linux。这样的用户空间可能包含 Bash shell 和命令语言,使用本机 GNU/Linux 命令行工具(sed,awk 等),编程语言解释器(Ruby,Python 等),甚至是图形应用程序(使用主机端的X窗口系统)。

使用命令行可以完成所有日常的操作,新建文件夹(mkdir)、新建文件(touch)、移动(mv)、复制(cp)、删除(rm)等等。而且使用 Linux/Unix 命令行最好的方式是可以用 awksedgrepxargsfindsort 等等这样的命令,然后用管道把其串起来,就可以完成一个你想要的功能,尤其是一些简单的数据统计功能。这是Linux命令行不可比拟的优势。比如:

  • 查看连接你服务器 top10 用户端的 IP 地址:

netstat -nat | awk '{print $5}' | awk -F ':' '{print $1}' | sort | uniq -c | sort -rn | head -n 10

  • 查看一下你最常用的10个命令:

cat .bash_history | sort | uniq -c | sort -rn | head -n 10 (or cat .zhistory | sort | uniq -c | sort -rn | head -n 10

(注:awk 和 sed 是两大神器,所以,我以前的也有两篇文章来介绍它们——《awk简明教程》和《sed简明教程》,你可以前往一读)

在命令行中使用 alias 可以将使用频率很高命令或者比较复杂的命令合并成一个命令,或者修改原生的命令。

下面这几个命令,可能是你天天都在敲的。所以,你应该设置成 alias 来提高效率

alias nis="npm install --save "
alias svim='sudo vim'
alias mkcd='foo(){ mkdir -p "$1"; cd "$1" }; foo '
alias install='sudo apt get install'
alias update='sudo apt-get update; sudo apt-get upgrade'
alias ..="cd .."
alias ...="cd ..; cd .."
alias www='python -m SimpleHTTPServer 8000'
alias sock5='ssh -D 8080 -q -C -N -f user@your.server'

你还可以参考如下的一些文章,看看别人是怎么用好 alias 的

命令行中除了原生的命令之外,还有很多可以提升使用体验的工具。下面罗列一些很不错的命令,把原生的命令增强地很厉害:

  • fasd 增强了 cd 命令 。
  • bat 增强了 cat 命令 。如果你想要有语法高亮的 cat,可以试试 ccat 命令。
  • exa 增强了 ls 命令,如果你需要在很多目录上浏览各种文件 ,ranger 命令可以比 cd 和 cat 更有效率,甚至可以在你的终端预览图片。
  • fd 是一个比 find 更简单更快的命令,他还会自动地忽略掉一些你配置在 .gitignore 中的文件,以及 .git 下的文件。
  • fzf 会是一个很好用的文件搜索神器,其主要是搜索当前目录以下的文件,还可以使用 fzf --preview 'cat {}'边搜索文件边浏览内容。
  • grep 是一个上古神器,然而,ackag 和 rg 是更好的grep,和上面的 fd一样,在递归目录匹配的时候,会使用你配置在 .gitignore 中的规则。
  • rm 是一个危险的命令,尤其是各种 rm -rf …,所以,trash 是一个更好的删除命令。
  • man 命令是好读文档的命令,但是man的文档有时候太长了,所以,你可以试试 tldr 命令,把文档上的一些示例整出来给你看。
  • 如果你想要一个图示化的ping,你可以试试 prettyping 。
  • 如果你想搜索以前打过的命令,不要再用 Ctrl +R 了,你可以使用加强版的 hstr  。
  • htop  是 top 的一个加强版。然而,还有很多的各式各样的top,比如:用于看IO负载的 iotop,网络负载的 iftop, 以及把这些top都集成在一起的 atop
  • ncdu  比 du 好用多了用。另一个选择是 nnn
  • 如果你想把你的命令行操作建录制成一个 SVG 动图,那么你可以尝试使用 asciinema 和 svg-trem 。
  • httpie 是一个可以用来替代 curlwget 的 http 客户端,httpie 支持 json 和语法高亮,可以使用简单的语法进行 http 访问: http -v github.com
  • tmux 在需要经常登录远程服务器工作的时候会很有用,可以保持远程登录的会话,还可以在一个窗口中查看多个 shell 的状态。
  • Taskbook 是可以完全在命令行中使用的任务管理器 ,支持 ToDo 管理,还可以为每个任务加上优先级。
  • sshrc 是个神器,在你登录远程服务器的时候也能使用本机的 shell 的 rc 文件中的配置。
  • goaccess  这个是一个轻量级的分析统计日志文件的工具,主要是分析各种各样的 access log。

关于这些增加命令,主要是参考自下面的这些文章

  1. 10 Tools To Power Up Your Command Line
  2. 5 More Tools To Power Up Your Command Line (Part 2 Of Series)
  3. Power Up Your Command Line, Part 3
  4. Power Up Your Command Line
  5. Hacker Tools

Shell 和脚本

shell 是可以与计算机进行高效交互的文本接口。shell 提供了一套交互式的编程语言(脚本),shell的种类很多,比如 shbashzsh 等。

shell 的生命力很强,在各种高级编程语言大行其道的今天,很多的任务依然离不开 shell。比如可以使用 shell 来执行一些编译任务,或者做一些批处理任务,初始化数据、打包程序等等。

现在比较流行的是 zsh + oh-my-zsh + zsh-autosuggestions 的组合,你也可以试试看。其中 zsh 和 oh-my-zsh 算是常规操作了,但是 zsh-autosuggestions 特别有用,可以超级快速的帮你补全你输入过的命令,让命令行的操作更加高效。

另外,fish 也是另外一个牛逼的shell,比如:命令行自动完成(根据历史记录),命令行命令高亮,当你要输入命令行参数的时候,自动提示有哪些参数…… fish在很多地方也是用起来很爽的。和上面的 oh-my-zsh 有点不分伯仲了。

你也许会说,用 Python 脚本或 PHP 来写脚本会比 Shell 更好更没有 bug,但我要申辩一下:

  • 其一,如果你有一天要维护线上机器的时候,或是到了银行用户的系统(与外网完全隔离,而且服务器上没有安装 Python/PHP 或是他们的的高级库,那么,你只有 Shell 可以用了)。
  • 其二,而且,如果要跟命令行交互很多的话,Shell 是不二之选,试想一下,如果你要去 100 台远程的机器上查access.log 日志中有没有某个错误,完成这个工作你是用 PHP/Python 写脚本快还是用 Shell 写脚本快呢?

所以,我们还要学会只使用传统的grep/awk/sed等等这些POSIX的原生的系统默认安装的命令

当然,要写好一个脚本并不容易,下面有一些小模板供你参考:

处理命令行参数的一个样例

while [ "$1" != "" ]; do
    case $1 in
        -s  )   shift	
		SERVER=$1 ;;  
        -d  )   shift
		DATE=$1 ;;
	--paramter|p ) shift
		PARAMETER=$1;;
        -h|help  )   usage # function call
                exit ;;
        * )     usage # All other parameters
                exit 1
    esac
    shift
done 

命令行菜单的一个样例

#!/bin/bash
# Bash Menu Script Example

PS3='Please enter your choice: '
options=("Option 1" "Option 2" "Option 3" "Quit")
select opt in "${options[@]}"
do
    case $opt in
        "Option 1")
            echo "you chose choice 1"
            ;;
        "Option 2")
            echo "you chose choice 2"
            ;;
        "Option 3")
            echo "you chose choice $REPLY which is $opt"
            ;;
        "Quit")
            break
            ;;
        *) echo "invalid option $REPLY";;
    esac
done

颜色定义,你可以使用 echo -e "${Blu}blue ${Red}red ${RCol}etc...." 进行有颜色文本的输出

RCol='\e[0m'    # Text Reset

# Regular           Bold                Underline           High Intensity      BoldHigh Intens     Background          High Intensity Backgrounds
Bla='\e[0;30m';     BBla='\e[1;30m';    UBla='\e[4;30m';    IBla='\e[0;90m';    BIBla='\e[1;90m';   On_Bla='\e[40m';    On_IBla='\e[0;100m';
Red='\e[0;31m';     BRed='\e[1;31m';    URed='\e[4;31m';    IRed='\e[0;91m';    BIRed='\e[1;91m';   On_Red='\e[41m';    On_IRed='\e[0;101m';
Gre='\e[0;32m';     BGre='\e[1;32m';    UGre='\e[4;32m';    IGre='\e[0;92m';    BIGre='\e[1;92m';   On_Gre='\e[42m';    On_IGre='\e[0;102m';
Yel='\e[0;33m';     BYel='\e[1;33m';    UYel='\e[4;33m';    IYel='\e[0;93m';    BIYel='\e[1;93m';   On_Yel='\e[43m';    On_IYel='\e[0;103m';
Blu='\e[0;34m';     BBlu='\e[1;34m';    UBlu='\e[4;34m';    IBlu='\e[0;94m';    BIBlu='\e[1;94m';   On_Blu='\e[44m';    On_IBlu='\e[0;104m';
Pur='\e[0;35m';     BPur='\e[1;35m';    UPur='\e[4;35m';    IPur='\e[0;95m';    BIPur='\e[1;95m';   On_Pur='\e[45m';    On_IPur='\e[0;105m';
Cya='\e[0;36m';     BCya='\e[1;36m';    UCya='\e[4;36m';    ICya='\e[0;96m';    BICya='\e[1;96m';   On_Cya='\e[46m';    On_ICya='\e[0;106m';
Whi='\e[0;37m';     BWhi='\e[1;37m';    UWhi='\e[4;37m';    IWhi='\e[0;97m';    BIWhi='\e[1;97m';   On_Whi='\e[47m';    On_IWhi='\e[0;107m';

取当前运行脚本绝对路径的示例:(注:Linux下可以用 dirname $(readlink -f $0) )

FILE="$0"
while [[ -h ${FILE} ]]; do
    FILE="`readlink "${FILE}"`"
done
pushd "`dirname "${FILE}"`" > /dev/null
DIR=`pwd -P`
popd > /dev/null

如何在远程服务器运行一个本地脚本

#无参数
ssh user@server 'bash -s' < local.script.sh

#有参数
ssh user@server ARG1="arg1" ARG2="arg2" 'bash -s' < local_script.sh

如何检查一个命令是否存在,用 which 吗?最好不要用,因为很多操作系统的 which 命令没有设置退出状态码,这样你不知道是否是有那个命令。所以,你应该使用下面的方式。

# POSIX 兼容:
command -v <the_command>


# bash 环境:
hash <the_command> 
type <the_command>

# 示例:
gnudate() {
    if hash gdate 2> /dev/null; then
        gdate "$@"
    else
        date "$@"
    fi
}

然后,如果要写出健壮性更好的脚本,下面是一些相关的技巧:

  • 使用 -e 参数,如:set -e 或是 #!/bin/sh -e,这样设置会让你的脚本出错就会停止运行,这样一来可以防止你的脚本在出错的情况下还在拼拿地干活停不下来。
  • 使用 -u 参数,如: set -eu,这意味着,如果你代码中有变量没有定义,就会退出。
  • 对一些变理,你可以使用默认值。如:${FOO:-'default'}
  • 处理你代码的退出码。这样方便你的脚本跟别的命令行或脚本集成。
  • 尽量不要使用 ; 来执行多个命令,而是使用 &&,这样会在出错的时候停止运行后续的命令。
  • 对于一些字符串变量,使用引号括起,避免其中有空格或是别的什么诡异字符。
  • 如果你的脚有参数,你需要检查脚本运行是否带了你想要的参数,或是,你的脚本可以在没有参数的情况下安全的运行。
  • 为你的脚本设置 -h 和 --help 来显示帮助信息。千万不要把这两个参数用做为的功能。
  • 使用 $() 而不是 “ 来获得命令行的输出,主要原因是易读。
  • 小心不同的平台,尤其是 MacOS 和 Linux 的跨平台。
  • 对于 rm -rf 这样的高危操作,需要检查后面的变量名是否为空,比如:rm -rf $MYDIDR/* 如果 $MYDIR为空,结果是灾难性的。
  • 考虑使用 “find/while” 而不是 “for/find”。如:for F in $(find . -type f) ; do echo $F; done 写成 find . -type f | while read F ; do echo $F ; done 不但可以容忍空格,而且还更快。
  • 防御式编程,在正式执行命令前,把相关的东西都检查好,比如,文件目录有没有存在。

你还可以使用ShellCheck 来帮助你检查你的脚本。

最后推荐一些 Shell 和脚本的参考资料。

各种有意思的命令拼装,一行命令走天涯:

下面是一些脚本集中营,你可以在里面淘到各种牛X的脚本:

甚至写脚本都可以使用框架:

Google的Shell脚本的代码规范:

最后,别忘了几个和shell有关的索引资源:

最后,如果你还有什么别的更好的玩的东西,欢迎在评论区留言,或是到 coolshellx/ariticles @ github 修改本文。

 


关注CoolShell微信公众账号可以在手机端搜索文章

(转载本站文章请注明作者和出处 酷 壳 – CoolShell ,请勿用于任何商业用途)

——=== 访问 酷壳404页面 寻找遗失儿童。 ===——

陈皓 March 17, 2019 at 01:53PM
from 酷 壳 – CoolShell https://ift.tt/2W6efEU

VN:F [1.9.22_1171]
Rating: 0.0/5 (0 votes cast)
VN:F [1.9.22_1171]
Rating: 0 (from 0 votes)
Share

【酷壳】 谈谈我的“三观”

也许是人到了四十多了,敢写这么大的命题,我也醉了,不过,我还是想把我的想法记录下来,算是对我思考的一个snapshot,给未来的我看看,要么被未来的我打脸,要么打未来我的脸。无论怎么样,我觉得对我自己都很有意义。注意,这篇文章是长篇大论。

三观是世界观、人生观和价值观,

  • 世界观代表你是怎么看这个世界的。是左还是右,是激进还是保守,是理想还是现实,是乐观还是悲观……
  • 人生观代表你要想成为什么样的人。是成为有钱人,还是成为人生的体验者,是成为老师,还是成为行业专家,是成为有思想的人,还是成为,
  • 价值观则是你觉得什么对你来说更重要。是名是利,是过程还是结果,是付出还是索取,是国家还是自己,是家庭还是职业……

人的三观其实是会变的,回顾一下我的过去,我感觉我的三观至少有这么几比较明显的变化,学生时代、刚走上社会的年轻时代,三十岁后的时代,还有现在。估计都差不多。

  • 学生时代的三观更多的是学校给的,用各种标准答案给的,是又红又专的
  • 刚走上社会后发现完全不是这么一回事,但学生时代的三观根深蒂固,三观开始分裂,内心开始挣扎
  • 三十岁后,不如意的事越来越多,对社会越来越了解,一部分人屈从现实,一部分人不服输继续奋斗,而一部分人已展露才能,分裂的三观开始收敛
  • 四十岁时,经历过的事太多,发现留给自己的时间不多,世界太复杂,而还有好多事没做,从而变得与世无争,也变得更为地自我。

年轻的时候,抵制过日货,虽然没上过街,但是也激动过,一次是1999南斯拉夫大使馆被炸,一次是2005反日示威,以前,我也是一个爱国愤青。但是后来,有过各种机会出国长时间生活工作,加拿大、英国、美国、日本……随着自己的经历和眼界的开阔,自己的三观自己也随着有了很多的变化,发现有些事并不是自己一开始所认识的那样,而且还是截然相反的。我深深感觉到,要有一个好的世界观,你需要亲身去经历和体会这个世界,而不是听别人说。所以,当我看到身边的人情绪激动地要抵制这个国家,搞死那个民族的时候,我都会建议他去趟那个国家最好在在那个国家呆上一段时间,亲自感受一下。

再后来发现,要抵制的越来越多,小时候的美英帝国主义,然后是日本,再后面是法国、韩国、菲利宾、印度、德国、瑞典、加拿大……从小时候的台独到现在的港独、藏独、疆独……发现再这样下去,基本上来说,自己的人生也不用干别的事了……另外,随着自己的成长,越来越明白,抵制这个抵制那个只不过是幼稚和狭隘的爱国主义,真想强国,想别让他人看得起,就应该把时间和精力放在努力学习放在精益求精上,做出比他们更好的东西来。另外,感觉用对内的爱国主义解决对外的外交问题也有点驴唇不对马嘴,无非也就是转移一下内部的注意力罢了,另外还发现爱国主义还可以成为消费营销手段……不是我不爱国,是我觉得世道变复杂了,我只是一个普通的老百姓,能力有限,请不要赋予我那么大的使命,我只想在我的专业上精进,能力所能及地帮助身边的人,过一个简单纯粹安静友善的生活……

另外,为什么国与国之间硬要比个你高我低,硬要分个高下,硬要争出个输赢,我也不是太理解,世界都已经发展到全球化的阶段了,很多产品早就是你中有我,我中有你的情况了。举个例子,一部手机中的元件,可能来自全世界数十个国家,我们已经说不清楚一部手机是究竟是哪个国家生产的了。即然,整个世界都在以一种合作共赢全球化的姿态下运作,认准自己的位置,拥抱世界,持续向先进国家学习,互惠互利,不好吗?你可能会说,不是我们不想这样,是别人不容我们发展……老实说,大的层面我也感受不到,但就我在的互联网计算机行业方面,我觉得整个世界的开放性越来越好,开源项目空前地繁荣,世界上互联网文化也空前的开放,在计算机和互联网行业,我们享受了太多的开源和开放的红利,人家不开放,我们可能在很多领域还落后数十年。然而现在很多资源我们都访问不了,用个VPN也非法,你说是谁阻碍了发展?我只想能够流畅地访问互联网,让我的工作能够更有效率,然而,我在自己的家里却像做贼一样去学习新知识新技术,随时都有可能被抓进监狱……

随着自己的经历越多,发现这个世界越复杂,也发现自己越渺小,很多国家大事并不是我不关心,是我觉得那根本不是我这个平头老百姓可以操心的事,这个世界有这个世界运作的规律和方法,而还有很多事情超出了我能理解的范围,也超出了我能控制的范围,我关心不关心都一个样,这些大事都不会由我的意志所决定的。而所谓的关心,无非就是喊喊口号,跟人争论一下,试图改变其它老百姓的想法,然而,对事情的本身的帮助却没有多大意义。过上几天,生活照旧,人家该搞你还不是继续搞你,而你自己并不因为做这些事而过得更好。

我对国与国之间的关系的态度是,有礼有节,不卑不亢,对待外国人,有礼貌但也要有节气,既不卑躬屈膝,也不趾高气昂,整体上,我并不觉得我们比国外有多差,但我也不觉得我们比国外有多好,我们还在成长,还需要帮助和协作,四海之内皆兄弟,无论在哪个国家,在老百姓的世界里,哪有那么多矛盾。有机会多出去走走,多结交几个其它民族的朋友,你会觉得,在友善和包容的环境下,你的心情和生活可以更好

我现在更多关心的是和我生活相关的东西,比如:上网、教育、医疗、食品、治安、税务、旅游、收入、物价、个人权益、个人隐私……这些东西对我的影响会更大一些,也更值得关注,可以看到过去的几十年,我们国家已经有了长足的进步,这点也让我让感到很开心和自豪的,在一些地方也不输给强国。虽然依然有一些事的仍然没有达到我的预期,并也超出了我个人的控制范围无法改变,但整体都在变好。不过,未来的变数谁也不好说,我的安全感似乎还不足够,所以,我还是要继续努力,以便我可以有更多的选项。有选项总比没得选要好。所以,我想尽一切办法,努力让选项多起来,无法改变无法影响,那就只能提高自己有可选择的可能性。

另外,在网上与别人对一些事或观点的争论,我觉得越来越无聊,以前被怼了,一定要怼回去,现在不会了,视而不见,不是怕了,是因为,网络上的争论在我看来大多数都是些没有章法,逻辑混乱的争论。

  • 很多讨论不是说事,直接就是怼人骂人。随意就给人扣个帽子。
  • 非黑即白的划分,你说这个不是黑的,他们就把你划到白的那边。
  • 飘移观点,复杂化问题。东拉西扯,牵强附会,还扯出其它不相关的事来混淆。
  • 杠精很多,不关心你的整体观点,抓住一个小辫子大作文章。

很明显,与其花时间教育这些人,不如花时间提升自己,让自己变得更优秀,这样就有更高的可能性去接触更聪明更成功更高层次的人。因为,一方面,你改变不了他们,另外,改变他们对你自己也没什么意义,改变自己,提升自己,让自己成长才有意义。时间是宝贵的,那些人根本不值得花时间,应该花时间去结交更有素质更聪明的人,做更有价值的事。

美国总统富兰克林·罗斯福妻子埃莉诺·罗斯福(Eleanor Roosevelt)说过下面的一句话。

Great minds discuss ideas;
Average minds discuss events;
Small minds discuss people

把时间多放在一些想法上,对自己对社会都是有意义的,把时间放在八卦别人,说长到短,你也不可能改善自己的生活,你批评这个批评那个,看不上这个看不起那个,不会让你有成长,也不会提升你的影响力,你的影响力不是你对别人说长道短的能力,而是别人信赖你并希望得到你的帮助的现象。多交一些有想法的朋友,多把自己的想法付诸实践,那怕没有成功,你的人生也会比别人过得有意义。

如果你看过我以前的文章,你会看到一些吐槽性质的文章,而后面就再也没有了。另外,我也不再没有针对具体的某个人做出评价,因为人太复杂的了,经历的越多,你就会发现你很难评价人,与其花时间在评论人和事上,不如把时间花在做一些力所能及的事来改善自己或身边的环境。所以,我建议大家少一些对人的指责和批评,通过对一件事来引发你的思考,想一想有什么可以改善,有什么方法可以做得更好,有哪些是自己可以添砖加瓦的?你会发现,只要你坚持这么做,你个人的提升和对社会的价值会越来越大,而你的影响力也会越来越大

现在的我,即不是左派也不是右派,我不喜欢爱国主义,我也不喜欢崇洋媚外,我更多的时候是一个自由派,哪边我都不站,我站我自己。因为,生活在这样的一个时代,能让自己过好都是一些比较奢望的事了。

《教父》里有这样的人生观:第一步要努力实现自我价值,第二步要全力照顾好家人,第三步要尽可能帮助善良的人,第四步为族群发声,第五步为国家争荣誉。事实上作为男人,前两步成功,人生已算得上圆满,做到第三步堪称伟大,而随意颠倒次序的那些人,一般不值得信任。这也是古人的“修身齐家治国平天下”!所以,在你我准备要开始要“平天下”的时候,也得先想想,自己的生活有没有过好了,家人照顾好了么,身边有哪些力所能及的事是可以去改善的……

穷则独善其身,达则兼济天下。提升自己,实现自我,照顾好自己的家人,帮助身边的人。这已经很不错了!

什么样的人干什么样的事,什么样的阶段做什么样的选择,有人的说,选择比努力更重要的,我深以为然,而且,我觉得选择和决定,比努力更难,努力是认准了一个事后不停地发力,而要决定去认谁哪一个事则是令人彷徨和焦虑的(半途而废的人也很多)。面对人生,你每天都在作一个一个的决定,在做一个又一个的选择,有的决定大,有的决定小,你的人生的轨迹就是被这一个一个的决定和选择所走走出来的。

我在24岁放弃了一房子离开银行到小公司的时候,我就知道,人生的选择就是一个翘翘板,你要一头就没有另一头,选择是有代价的,你不选择的代价更大;选择是要冒险的,你不敢冒险的风险更大;选择是需要放弃的,因为无论怎么选你都要放弃什么。想想你老了以后,回头一看,好多事情在年轻的时候都不敢做,而你再也没有机会,你就知道不敢选择不敢冒险的代价有多大了。选择就是一种 trade-off,这世上根本不会有什么完美,只要你想做事,你有雄心壮志,你的人生就是一个坑接着一个坑,你所以做的就是找到你喜欢的方向跳坑。

所以, 你要想清楚你要什么,不要什么,而且还不能要得太多,这样你才好做选择。否则,你影响你的因子太多,决定不好做,也做不好。

就像最前面说的一样,你是激进派还是保守派,你是喜欢领导还是喜欢跟从,你是注重长期还是注重短期,你是注重过程还是注重结果……等等,你对这些东西的坚持和守护,成为了你的“三观”,而你的三观则影响着你的选择,而你的选择影响着你的人生。

下面是一些大家经常在说,可能也是大多数人关心的问题,就这些问题,我也谈谈我的价值取向。

挣钱。挣钱是一个大家都想做的事,但你得解决一个很核心的问题,那就是为什么别人愿意给你钱?对于挣钱的价值观从我大学毕业到现我就没怎么变过,那就是我更多关注的是怎么提高自己的能力,让自己值那个价钱,让别人愿意付钱。另外一方面,我发现,越是有能力的人,就越不计较一些短期得失,越计较短期得失的人往往都是很平庸的人。有能力的人不会关心自己的年终奖得拿多少,会不会晋升,他们更多的关心自己真正的实力有没有超过更多的人,更多的关注的是自己长远的成长,而不是一时的利益。聪明的人从来不关心眼前的得失,不会关心表面上的东西,他们更多关心的是长期利益,关心长期利益的人一定不是投机者,一定是投资者,投资会把自己的时间精力金钱投资在能让自己成长和提升的地方,那些让自己可以操更大的盘的地方,他们培养自己的领导力和影响力,而投机者在职场上会通过溜须拍马讨好领导,在学习上追求速成,在投资上使用跟随策略,在创业上甚至会不择手段,当风险来临时,投机者是几乎完全没有抗风险能力的,他们所谓的能力只不过因为形势好。

 

技术。对于计算机技术来说,要学的东西实在是太多,我并不害怕要学的东西很多,因为学习能力是一个好的工程师必需具备的事,我不惧怕困难和挑战。我觉得在语言和技术争论谁好谁坏是一种幼稚的表现, 没有完美的技术,Engineering 玩的是 Tradeoff。所以,我对没有完美的技术并不担心,但是我反而担心的是,当我们进入到一些公司后,这些公司会有一些技术上的沉淀也就是针对公司自己的专用技术,比如一些中间件,一些编程框架,lib库什么的。老实说,我比较害怕公司的专用技术,因为一旦失业,我建立在这些专用技术上的技能也会随之瓦解,有时候,我甚至害怕把我的技术建立在某一个平台上,小众的不用说了,大众的我也比较担扰,比如Windows或Unix/Linux上,因为一旦这个平台不流行或是被取代,那么我也会随之淘汰(过去的这20年已经发生过太多这样的事了)。为了应对这样的焦虑,我更愿意花时间在技术的原理和技术的本质上,这导致我需要了解各种各样的技术的设计方法,以及内在原理。所以,当国内的绝大多数程序员们更多的关注架构性能的今天,我则花更多的时间去了解编程范式,代码重构,软件设计,计算机系统原理,领域设计,工程方法……因为只有原理、本质和设计思想才可能让我不会被绑在某个专用技术或平台上,除非,我们人类的计算机这条路没走对。

 

职业。在过去20多年的职业生涯中,我从基层工程师做到管理,很多做技术的人都会转管理,但我却还是扎根技术,就算是在今天,还是会抠很多技术细节,包括写代码。因为我心里觉得,不写代码的人一定是做不好技术管理的,因为做技术管理有人要做技术决定,从不上手技术的人是做不好技术决定的,另一方面,我觉得管理是支持性的工作,不是产出性的工作,大多数的管理者无非是因为组织大了,所以需要管人管事,所以,必然要花大量的时间和精力处理各种问题,甚至办公室政治,然而,如果有一天失业了,大环境变得不好了,一个管理者和一个程序员要出去找工作,程序员会比管理者更能自食其力。所以,我并不觉得管理者这个职业有意思,我还是觉得程序员这个有创造性的职业更有趣。通常来说,管理者的技能力需要到公司和组织里才能展现,而有创造力的技能的人是可以自己独立的能力,所以,我觉得程序员的技能比管理者的技能能让我更稳定更自地活着。所以,我更喜欢“电影工作组”那样的团队和组织形式。

 

打工。对于打工,也就是加入一家公司工作,无论是在一家小公司还是一家大公司工作,都会有好的和不好的,任何公司都有其不完美的地方,这个需要承认。首先第一的肯定是完成公司交给你的任务,然后我会尽我所能在工作找到可以提高效率的地方进行改善。在推动公司/部门/团队在一技术和工程方面进步并不是一件很容易的事,因为进步是需要成本的,这成本并不一定是公司和团队愿意接授的,而另外,从客观规律上来说,一件事的进步一定是会有和现状有一些摩擦的。有的人害怕有摩擦而忍了,而我则不是,我觉得与别人的摩擦并不可怕,因为大家的目标都是基本一致的,只是做事的标准和方式不一样,这是可能沟通的,始终是会相互理解的。而如果你没有去推动一个事,我觉得对于公司对于我个人来说,都是一种对人生的浪费,敬业也好,激情也好,其就是体现在你是否愿意冒险去推动一件于公于私都有利的事,而不是成为一个“听话”、“随大流”、“懒政”的人,即耽误了公司也耽误了自己。所以,我更信仰的是《做正确的事情,等着被开除》,这些东西,可参看《我看绩效考核》,以及我在Gitchat上的一些问答

 

创业。前两天,有个小伙来跟我说,说他要离开BAT要去创业公司了,说在那些更自由一些,没有大公司的种种问题。我毫不犹豫地教育了他一下,我说,你选择这个创业公司的动机不对啊,你无非就是在逃避一些东西罢了,你把创业公司当做是一个避风港,这是不对的,创业公司的问题可能会更多,去创业公司的更好的心态是,这个创业公司在干的事业是不是你的事业?说白了,如果你是为了你的事业,为了解决个什么,为了改进个什么,那么,创业是适合你的,也只有在做自己事业的时候,你才能不惧困难,才会勇敢地面对一切那种想找一个安稳的避风港呆着的心态是不会让你平静地,你要知道世界本来就是不平静的,找了自己的归宿和目标才可能让你真正的平静。所以,在我现的创业团队,我不要求大家加班,我也不洗脑,对于加入的人,我会跟他讲各种问题和各种机遇,并一直在拷问他的“灵魂”,我们在做的事是不是你自己的事业诉求?可不可以更好?每个人都应该为自己的事业为自己的理想去活一次,追逐自己的事业和理想并不容易,需要有很大的付出,而也只有你心底里的那个理想值得这么大的付出……

 

客户。基于上述的价值观,在我现在创业的时候,我在面对客户的时候,也是一样的,我并不会完全的迁就于客户,我的一些银行客户和互联网客户应该体会到我的做的方式了,我并不觉得迁就用户,用户要什么我就应该给什么,用户想听什么,我就说什么,虽然这样可以省着精力,更圆滑,但这都不是我喜欢的,我更愿意鲜明地表达我的观点,并拉着用户跟我一起成长,因为我并不觉得完成客户的项目有成就感,我的成就感来自客户的成长。所以,面对客户有些做得不对有问题有隐患的地方,或是我有什么做错的事,我基本上都是直言不讳地说出来,因为我觉得这是对客户和对自己最大的尊重。我并不是在这里装,因为,我也想做一些更高级更有技术含量的事,所以,对于一些还达到的客户,我如果不把他们拉上来,我也对不起自己。

 

在我“不惑之年”形成了这些价值观体系,也许未来还会变,也许还不成熟,总之,我不愿跟大多数人一样,因为大多数人都是随遇而安随大流的,因为这样风险最小,而我想走一条属于自己的路,做真正的自己,就像我24岁从银行里出来时想的那样,我选择对了一个正确的专业(计算机科学),呆在了一个正确的年代(信息化革命),这样的“狗屎运”几百年不遇,如果我还患得患失,那我岂不辜负活在这样一个刺激的时代?!我所要做的就是在这个时代中做有价值的事就好了!这个时代真的是太好了!

(全文完)


关注CoolShell微信公众账号可以在手机端搜索文章

(转载本站文章请注明作者和出处 酷 壳 – CoolShell ,请勿用于任何商业用途)

——=== 访问 酷壳404页面 寻找遗失儿童。 ===——

陈皓 February 26, 2019 at 04:02PM
from 酷 壳 – CoolShell https://ift.tt/2U7TXdJ

VN:F [1.9.22_1171]
Rating: 0.0/5 (0 votes cast)
VN:F [1.9.22_1171]
Rating: 0 (from 0 votes)
Share

【酷壳】 记一次Kubernetes/Docker网络排障

昨天周五晚上,临下班的时候,用户给我们报了一个比较怪异的Kubernetes集群下的网络不能正常访问的问题,让我们帮助查看一下,我们从下午5点半左右一直跟进到晚上十点左右,在远程不能访问用户机器只能远程遥控用户的情况找到了的问题。这个问题比较有意思,我个人觉得其中的调查用到的的命令以及排障的一些方法可以分享一下,所以写下了这篇文章。

问题的症状

用户直接在微信里说,他们发现在Kuberbnetes下的某个pod被重启了几百次甚至上千次,于是开启调查这个pod,发现上面的服务时而能够访问,时而不能访问,也就是有一定概率不能访问,不知道是什么原因。而且并不是所有的pod出问题,而只是特定的一两个pod出了网络访问的问题。用户说这个pod运行着Java程序,为了排除是Java的问题,用户用 docker exec -it 命令直接到容器内启了一个 Python的 SimpleHttpServer来测试发现也是一样的问题。

我们大概知道用户的集群是这样的版本,Kuberbnetes 是1.7,网络用的是flannel的gw模式,Docker版本未知,操作系统CentOS 7.4,直接在物理机上跑docker,物理的配置很高,512GB内存,若干CPU核,上面运行着几百个Docker容器。

问题的排查

问题初查

首先,我们排除了flannel的问题,因为整个集群的网络通信都正常,只有特定的某一两个pod有问题。而用 telnet ip port 的命令手工测试网络连接时有很大的概率出现 connection refused 错误,大约 1/4的概率,而3/4的情况下是可以正常连接的。

当时,我们让用户抓个包看看,然后,用户抓到了有问题的TCP连接是收到了 SYN 后,立即返回了 RST, ACK

我问一下用户这两个IP所在的位置,我们知道了,10.233.14.129 是 docker010.233.14.145 是容器内的IP。所以,这基本上可以排除了所有和kubernets或是flannel的问题,这就是本地的Docker上的网络的问题。

对于这样的情况,在 telnet 上会显示 connection refused 的错误信息,对于我个人的经验,这种 SYN完直接返回 RST, ACK的情况只会有三种情况:

  1.  TCP链接不能建立,绝大多数情况都是服务端没有相关的端口号
  2. TCP链接建错误,有可能是因为修改了TCP参数
  3. 有防火墙iptables的设置

因为当时还在开车,在等红灯的时候,我感觉到有点像 NAT 的网络中服务端开启了 tcp_tw_recycle 和 tcp_tw_reuse 的症况(详细参看《TCP的那些事(上)》),所以,让用户查看了一上TCP参数,发现用户一个TCP的参数都没有改,全是默认的,于是我们排除了TCP参数的问题。

然后,我也不觉得容器内还会设置上iptables,所以,我怀疑容器内的端口号没有侦听上,但是马上又好了,这可能会是应用的问题。于是我让用户那边看一下,应用的日志,并用 kublet describe看一下运行的情况,并把宿主机的 iptables 看一下。

然而,我们发现并没有任何的问题。这时,我们失去了所有的调查线索,感觉不能继续下去了……

重新梳理

这个时候,回到家,大家吃完饭,和用户通了一个电话,把所有的细节再重新梳理了一遍,这个时候,用户提供了一个比较关键的信息—— “抓包这个事,在 docker0 上可以抓到,然而到了容器内抓不到容器返回 RST, ACK ” !然而,根据我的知识,我知道在 docker0 和容器内的 veth 网卡上,中间再也没有什么网络设备了(参看《Docker基础技术:LINUX NAMESPACE(下)》)!

于是这个事把我们逼到了最后一种情况 —— IP地址冲突了!

Linux下看IP地址冲突还不是一件比较简单事的,而在用户的生产环境下没有办法安装一些其它的命令,所以只能用已有的命令,这个时候,我们发现用户的机器上有 arping 于是我们用这个命令来检测有没有冲突的IP地址。使用了下面的命令:

$ arping -D -I docker0 -c 2 10.233.14.145
$ echo $?

根据文档,-D 参数是检测IP地址冲突模式,如果这个命令的退状态是 0 那么就有冲突。结果返回了 1 。而且,我们用 arping IP的时候,没有发现不同的mac地址。 这个时候,似乎问题的线索又断了

因为客户那边还在处理一些别的事情,所以,我们在时断时续的情况下工作,而还一些工作都需要用户完成,所以,进展有点缓慢,但是也给我们一些时间思考问题。

柳暗花明

现在我们知道,IP冲突的可能性是非常大的,但是我们找不出来是和谁的IP冲突了。而且,我们知道只要把这台机器重启一下,问题一定就解决掉了,但是我们觉得这并不是解决问题的方式,因为重启机器可以暂时的解决掉到这个问题,而如果我们不知道这个问题怎么发生的,那么未来这个问题还会再来。而重启线上机器这个成本太高了。

于是,我们的好奇心驱使我们继续调查。我让用户 kubectl delete 其中两个有问题的pod,因为本来就服务不断重启,所以,删掉也没有什么问题。删掉这两个pod后(一个是IP为 10.233.14.145 另一个是 10.233.14.137),我们发现,kubernetes,在其它机器上重新启到了这两个服务的实例。然而,在问题机器上,这两个IP地址居然还可以ping得通

好了,IP地址冲突的问题可以确认了。因为10.233.14.xxx 这个网段是docker 的,所以,这个IP地址一定是在这台机器上。所以,我们想看看所有的 network namespace 下的 veth 网卡上的IP。

在这个事上,我们费了点时间。

  • 首先,我们到 /var/run/netns目录下查看系统的network namespace,发现没有。
  • 然后,我们到 /var/run/docker/netns 目录下查看Docker的namespace,发现有好些。
  • 于是,我们用指定位置的方式查看Docker的network namespace里的IP地址
$ ls /var/run/docker/netns | xargs -I {} nsenter --net=/var/run/docker/netns/{} ip addr 

我们发现了比较诡异的事情。

  • 10.233.14.145 我们查到了,docker的namespace下还有这个IP。
  • 10.233.14.137,这个IP没有在docker的network namespace下查到。

于是我上网查了一下,发现了一个docker的bug – 在docker remove/stop 一个容器的时候,没有清除相应的network namespace,这个问题被报告到了 Issue#31597 然后被fix在了 PR#31996,并Merge到了 Docker的 17.05版中。而用户的版本是 17.09,应该包含了这个fix。

要查看所有network namespace,只有最后一条路了,那就是到 /proc/ 目录下,把所有的pid下的 /proc/<pid>/ns 目录给穷举出来。好在这里有一个比较方便的命令可以干这个事 – lsns

于是我写下了如下的命令:

$ lsns -t net | awk ‘{print $4}' | xargs -t -I {} nsenter -t {} -n ip addr | grep -C 4 "10.233.14.137"

解释一下。

  • lsns -t net 列出所有开了network namespace的进程,其每4列是进程PID
  • 把所有开过network namespace的进程PID拿出来,转给 xargs 命令
  • xargs 命令把这些PID 依次传给 nsenter 命令,
    • xargs -t 的意思是会把相关的执行命令打出来,这样我知道是那个PID。
    • xargs -I {}  是声明一个占位符来替换相关的PID

最后,我们发现,虽然在 /var/run/docker/netns 下没有找到 10.233.14.137 ,但是在 lsns 中找到了三个进程,其用了10.233.14.137 这个IP,而且他们的MAC地址全是一样的!冲突了这么多。通过ps 命令,可以查到这三个进程,有两个是java的,还有一个是/pause (这个应该是kubernetes的沙盒)。

我们继续乘胜追击,穷追猛打,用pstree命令把整个进程树打出来。发现上述的三个进程的父进程都在多个同样叫 docker-contiane 的进程下!

这明显还是docker的,但是在docker ps 中却找不道相应的容器,什么鬼!我快崩溃了……

继续看进程树,发现,这些 docker-contiane 的进程的父进程不在 dockerd 下面,而是在 systemd 这个超级父进程PID 1下,我靠!进而发现了一堆这样的野进程(这种野进程或是僵尸进程对系统是有害的,至少也是会让系统进入亚健康的状态,因为他们还在占着资源)。

docker-contiane 应该是 dockerd 的子进程,被挂到了 pid 1 只有一个原因,那就是父进程“飞”掉了,只能找 pid 1 当养父。这说明,这台机器上出现了比较严重的 dockerd 进程退出的问题,而且是非常规的,因为 systemd 之所以要成为 pid 1,其就是要监管所有进程的子子孙孙,居然也没有管理好,说明是个非常规的。(注,关于 systemd,请参看《Linux PID 1 和 Systemd 》,关于父子进程的事,请参看《Unix高级环境编程》一书)

接下来就要看看 systemddockerd 记录的日志了……

总结

通过这个调查,可以总结一下,

1) 对于问题调查,需要比较扎实的基础知识,知道问题的成因和范围。

2)如果走不下去了,要生新梳理,回头看一下过的一些蛛丝马迹,但认真推敲每一个细节。

3) 各种诊断工具要比较熟悉,这会让你事半功倍。

4)系统维护和做清洁比较类似,你需要经党看看系统中是否有一些僵尸进程或是一些垃圾东西,这些东西要及时清理掉。

最后,多说一下,很多人都说,Docker适合放在物理机内运行,这并不完全对,因为他们只考虑到了性能成本,没有考虑到运维成本,在这样512GB中启动几百个容器的玩法,其实并不好,因为这本质上是个大单体,因为你一理要重启某些关键进程或是机器,你的影响面是巨大的

(全文完)


关注CoolShell微信公众账号可以在手机端搜索文章

(转载本站文章请注明作者和出处 酷 壳 – CoolShell ,请勿用于任何商业用途)

——=== 访问 酷壳404页面 寻找遗失儿童。 ===——

陈皓 December 08, 2018 at 11:57AM
from 酷 壳 – CoolShell https://ift.tt/2G500Nn

VN:F [1.9.22_1171]
Rating: 0.0/5 (0 votes cast)
VN:F [1.9.22_1171]
Rating: 0 (from 0 votes)
Share

【酷壳】 程序员练级攻略(2018) 与我的专栏

写极客时间8个月了,我的专栏现在有一些定的积累了,今天我想自己推荐一下我的专栏。因为最新的系列《程序员练级攻略(2018)版》正在连载中,我现有非足的自信向大家推荐我的这个技术专栏。推荐就从最新的这一系统的文章开始。

2011年,我在 CoolShell 上发表了 《程序员技术练级攻略》一文,得到了很多人的好评(转载的不算,在我的网站上都有近1000W的访问量了)。并且陆续收到了一些人的反馈,说跟着这篇文章找到了不错的工作。几年过去,也收到了好些邮件和私信,希望我把这篇文章更新一下,因为他们觉得有点落伍了。是的,老实说,抛开这几年技术的更新迭代不说,那篇文章写得也不算特别系统,同时标准也有点低,当时是给一个想要入门的朋友写的,所以,非常有必要从头更新一下《程序员练级攻略》这一主题

目前,我在我极客时间的专栏上更新《程序员练级攻略(2018版)》。升级版的《程序员练级攻略》会比Coolshell上的内容更多,也更专业。这篇文章有【入门篇】、【修养篇】、【专业基础篇】、【软件设计篇】、【高手成长篇】五大篇章,它们会帮助你从零开始,一步步地,系统地,从陌生到熟悉,到理解掌握,从编码到设计再到架构,从码农到程序员再到工程师再到架构师的一步一步进阶,完成从普通到精通到卓越的完美转身……

这篇文章是我写得最累也是最痛苦的文章,原因如下:

  •  学习路径的梳理。这是一份计算编程相关知识地图,也是一份成长和学习路径。所以有太多的推敲了,知识的路径,体,地图……这让我费了很多工夫,感觉像在编写一本教材一样,即不能太高大上,也不能误人子弟。
  • 新旧知识的取舍。另外,因为我的成长经历中很多技术都成了过去时,所以对于新时代的程序员应该学习新的技术,然后,很多基础技术在今天依然管用,所以,在这点上,哪些要那些不要,也花了我很多的工夫。
  • 文章书籍的推荐。为了推荐最好的学习资料和资源,老实说,我几乎翻遍了整个互联网,进行了大量的阅读和比较。这个过程让我也受益非浅。一开始,这篇文章的大小居然在500K左右,太多的信息就是没有信息,所以在信息的筛选上我花费了很多的工夫,删掉了60%的内容。但是,依然很宠大。

总之,你一定会被这篇文章的内容所吓到的,是的,我就是故意这样做的,因为,这本来就没有什么捷径,也不可能速成,很多知识都是硬骨头,你只能一口一口的啃,我故意这样做就是为了让你不要有“速成”的幻想,也可以轻而一举的吓退那些不想用功不想努力的人

但是,我们也要知道《易经》有云:“取法其上,得乎其中,取法其中,得乎其下,取法其下,法不得也”。所以,我这里会给你立个比较高标准,你要努力达到,相信我,就算是达不到,也会比你一开始期望的要高很多……

下面是这份练级攻略的目录,目前只在极客时间上发布,你需要付费阅读(在本文最后有相关的二维码)。

 

那么,除程序员练级攻略外,我还写了哪些内容?下面是迄今为止我所有的文章的目录。你可以在下面看一下相关的目录。这也算是我开收费专栏来8个月给大家的一份答卷吧。我也没有想到,我居然写了这么多的文章,而且对很多人都很有用。

首先是个人成长和经验之谈的东西,在这里的文章还没有完全更新完,未来要更新什么我也不清楚,但是可以呈现出来的内容和方向如下所示,供你参考。对于个人成长中的内容,都是我多年来的心得和体会,从读者的反馈来看是非常不错的,你一定要要阅读的。

分布式系统架构,我一共出了两个系列,一个是分布式系统架构的本质,另一个是设计模式。前者偏概念,后者偏技术。这里旨在让你看到整个分布式系统设计的一个非常系统的蓝图,但是因为在手机端上,不可能写得非常细,所以,会缺失一些细节,这些细节我是故意缺失的,主要是有几方面的原因,

  • 一方面,这是为了阅读的效果,手机上的文章不过长,所以,不能有太多的细节。
  • 另一方面,也是是想留给大家自行学习,而不是一定要我把饭喂到你的嘴里,你才能吃得着。学习不只是为要答案,而是学方法
  • 最后是我的私心,因为我也在创业,所以,技术细节上东西正是我在做的产品,所以,如果你想了解得更细,你需要和我有更商业合作。

 

区块链的技术专栏本来不在我的写作计划中的,但是因为来问我这方面的技术人太多了,所以,就被问了一系列的文章,这里的文章除了一些技术上的科普,同样有有很多我的观点,你不但可以学到技术,还可以了解一些金融知识和相关的逻辑,我个人觉得这篇文章是让你有独立思考的文章。

我的专栏还在继续,接下来还有一个系列的文章——《从技术到管理》,欢迎关注,也欢迎扫码订阅。

最后友情提示一下:在手机上学习并不是最好的学习方式,也不要在我的专栏上进行学习,把我的专栏当成一个你的助手,当成一个向导,当成一个跳板,真正的学习还是要在线下,专心的,系统地、有讨论地、不断实践地学习,这点希望大家切记!

 

(全文完)


关注CoolShell微信公众账号可以在手机端搜索文章

(转载本站文章请注明作者和出处 酷 壳 – CoolShell ,请勿用于任何商业用途)

——=== 访问 酷壳404页面 寻找遗失儿童。 ===——

陈皓 May 29, 2018 at 12:38PM
from 酷 壳 – CoolShell https://ift.tt/2IWQLAc

VN:F [1.9.22_1171]
Rating: 0.0/5 (0 votes cast)
VN:F [1.9.22_1171]
Rating: 0 (from 0 votes)
Share

【酷壳】 关于Facebook 的 React 专利许可证

随着Apache、百度、Wordpress都在和Facebook的React.js以及其专利许可证划清界限,似乎大家又在讨论Facebook的这个BSD+PATENT的许可证问题了。这让我想起了之前在Medium读过的一篇文章——《React, Facebook, and the Revocable Patent License, Why It’s a Paper》,我觉得那篇文章写的不错,而且还是一个会编程的律师写的,所以有必要把这篇文章传播到中文社区这边来。注意,我不会全部翻译,我只是用我的语言来负责搬运内容和观点,我只想通过这篇文章让大家了解一下这个世界以及专利相关的知识,这样可以避免你看到某乎的“怎么看待XXX”这类的问题中有自己的独立思考和自我判断。;-)

这篇文章的作者叫Dennis Walsh,他自称是亚历桑那和加利福尼亚州的律师,主要针对版权法和专利诉论的法律领域。但是这个律师不一样,他说他更很喜欢商业和软件多一些。现在他用React/GraphQL/Elixir在写一个汽车代理销售相关的软件,而且已经发布到第2版了。

首先,作者表明,专利法经常被人误解,因为其实充满了各种晦涩难懂的法律术语,所以,作者用个例子来讲述专利的一个原则 —— 专利并不是授于让你制造或开发的权利,而是授予你可以排他的权利。(事实上似乎也是这样,申请专利很多时候都不是为了制作相关的产品,而是为了防止别人使用类似的技术制作相关的产品)他用了一个非常通俗易懂的例子:

如果有公司X为铅笔申请了专利,而另一家公司Y为用于铅笔的橡皮擦申请了专利。那么,公司X可以阻止公司Y来生产铅笔,而对带橡皮擦的铅笔没办法,但是公司Y的专利可以让公司X不能生产带有橡皮擦的铅笔。

所以,公司Y的橡皮擦专利又被广泛地叫作“Blocking Patent”。公司Y不能说他发明了铅笔,因为这是公司X的专利,但是,他们可以让公司X无法对铅笔做出某些改进。

于是,因为这种 Blocking Patent 存在,对于开源的公司是不利的,因为根据上面的那个例子来说,开源公司就是公司X,他们做了一个基础的软件,而公司Y在上面做了些改进,并注册成了专利,从而导致开源的公司X无法对它基础开源软件作出被公司Y专利阻止的改进,开源的公司X希望能够免费地使用公司Y的橡皮擦专利,因为毕竟是它发明了铅笔并放弃了铅笔的专利。

于是就出来了“专利反击条款”(Patent Retaliation Clauses)。一般来说有两个专利条款,一种是弱条款,一种是强条款。

Weak Patent Retaliation Clauses – 这种条款声明,如果许可证持有者用某个专利来打击许可证颁布者,那么专利就视为终止。用人话来表达就是,公司X做了一个开源铅笔,而公司Y注册了橡皮檫专利。此时,公司X做了一支带像皮擦的铅笔,公司Y马上对公司X提起专利侵权诉讼。那么,公司Y就失去了对底层铅笔的专利控制。(正如前面所说的,公司Y的橡皮擦专利因为在起诉公司X的开源铅笔,而失去了对开源铅笔的专利排他权利)

Strong Patent Retailiation Clauses – 这种条款声明,比“弱条款”要的更多。具体来说就是,任何专利声明终结许可证,而不管这个专利有没有和你基础的软件有关系。用人话来说就是,公司Y使用他们的热气球专利来起诉公司X,那么公司Y就失去了他们对铅笔的专利限制。

我个人理解起来,这两种条款看上去是防御性质的。

Facebook的React的Patent License如下:

The license granted hereunder will terminate, automatically and without notice,if you (or any of your subsidiaries, corporate affiliates or agents) initiatedirectly or indirectly, or take a direct financial interest in, any Patent Assertion: (i) against Facebook or any of its subsidiaries or corporateaffiliates, (ii) against any party if such Patent Assertion arises in whole orin part from any software, technology, product or service of Facebook or any ofits subsidiaries or corporate affiliates, or (iii) against any party relating to the Software. Notwithstanding the foregoing, if Facebook or any of itssubsidiaries or corporate affiliates files a lawsuit alleging patentinfringement against you in the first instance, and you respond by filing apatent infringement counterclaim in that lawsuit against that party that isunrelated to the Software, the license granted hereunder will not terminateunder section (i) of this paragraph due to such counterclaim.

这些条款中和基础软件没有任何关系,所以,这个条款是“强专利反击条款”

在后面,本文的作者又解解释了,为什么React的“强专利反击条款”就跟没有似的。他在文中针对一些歇斯底里的言论,如:“Facebook不用害怕专利诉讼了,而且他可以随时偷袭你家的专利仓库”,也作出了一些解释来分析这个事。

Contractural Liability – 意思是说,专利方面的东西只会影响专利上的事,而不会影响和专利无关的事,React底层协议是BSD-3许可证还是会被保留。换句话说,React的“强专利反击条款”只生效于专利层面,而不会对非常专利的软件使用产生问题,如果和专利无关,React还是走BSD-3的许可协议。

Copyright Liability – 这个和Contractural Liablitity 一样。作者说,如果有人有特别的案例或是有说服力的论据来说明Facebook的这个条款会作用于非专利的地方,那么,请告诉他。

Patent Liability – 专利的责任和损害是两件事,非专业人士总是会把其搞混。

第一个问题是Liability, 要搞清这个事,得搞清“Patent’s Claims”,作者说,现在的很多专利都是一些想法,很多投机份子随便一拍脑袋就发明出一个想法,然后就去注册专利了。但是可以被用来法律执行的只有“Patent’s Claims”(专利的权利主张)。这些权利主张相当相当的晦涩难读,而且是会故意被模糊掉的,因为,当你清楚的定义了你的发明是什么,那么,就可以清楚的定义出来什么不是你的发明。比如:一个铅笔专利权利主张里说,“这一个用石墨和木头组合起来的写字工具”,那么,只要我不用木头和石墨做组合,那么我就不是专利侵权。所以,一般来说,一个好的专利主张是,“这是一个用于标记表面的装置,其包括:与标记端相连的握持端”。作者这里给了一个苹果公司的滑动解锁专利的示例。可以感受一下产品规格说明和专利权利主张完全是两码事。

上面这些说明,专利这些事吧,在法律界里是非常非常困难作出评估的。所以,这个社会每年都会给律师们几十亿美金来一遍又一遍地回答这些问题,而且还经常回答错了。而对于美国的法律,对于专利诉讼会有一个叫Markman hearing的审前听证会(马克曼听证会),自从1996年美国最高法的“马克曼诉威斯幽仪器公司案”这个听证会就变成了一个惯例,美国联邦法院用这个听证会来向决定专利权利主张的解释,然而,上诉法院经常性的推翻审判法院的裁决。(对于美国法律来说,一般是法官认证法律,陪审团认定事实,然而,对于专利而言,1996年的那个案件认为专利术语是一个需要法官决定的法律问题,而不是陪审团决定的事实问题。关于马克曼听证会的事,可以参看本文未尾的附录)

所以,要决定Facebook的专利责任,我们需要评估Facebook的专利及其权利主张。具体来说,要明确Facebook对于React这个底层技术的专利权利主张是什么?但是作者搜了一下,发现什么也没有找到。也就是说,对于USPTO(美国专利商标局)或法院来说,他们没办法对Facebook的这样没有为React申请专利的方式来执行任何和专利的诉讼,也就是说,Facebook的这个React License的条款,美国政府是无法在法律上支持的。

第二个问题是专利损害。就算是Facebook可以评估出来一个合法可执行的专利来保护React,对于专利损害也是很有问题的。作者说他到目前还没有发现一个开源软件被专利侵权的事,就算有这样的案例,也不会是这里说的这个事。作者觉得在这个事上操作起来就是一个笑话。

另外,作者认为,React 专利许可证这个事就是个纸老虎。因为,一方面,这个专利不像电信通讯里的那些专利,你拿不掉。作者认为要从你的代码中把React去掉虽然难,但是也不是什么很难的事,另外,要打这样的专利官司,一般来说,在美国至少要花100-200万美金的费用才能发起诉讼,而要胜诉则需要需要200多万到2000万美金的费用,你觉得你要花多钱才能把React从你的代码库中剔除?肯定比这钱少。

作者还认为,Facebook玩这个事虽然出发点不错,但是感觉并不聪明,从目前的情况看下来,就像他想咬你一口,但却没有牙。

后面,作者还说了一下,转成别的框架会不会有问题?比如:你用Preact/Vue或是你自研的东西?作者说,未必,如果Facebook真的为React注册了专利,比如:React里的组件技术、虚拟DOM渲染技术等等。那么,你用Pract/Vue或是带这样技术的自研的框架,那么,从你使用的第一天就在侵犯Facebook的专利权了。而,使用React反而不会有这么大的风险,因为Facebook让你免费的用React。作者说,用别的框架的法律风险比用其它替代品的风险更高。

后面,作者也更新了一篇文章 《Using GraphQL? Why Facebook Now Owns You》,意思是,用React可能还好,但是用GraphQL就有问题了。因为找到了GraphQL的专利—— “Graph Query Logic”

后来我查了一下,我发现,React也有个相关的专利—— “Efficient event delegation in browser scripts ”,看上去和虚拟DOM渲染有关。Holy Shit!

好了,用还是不用React我也不知道,总之,这个世界比较复杂,我只是想借这篇文章来学习一下法律上的相关东西,欢迎听到大家的观点。

最后,请允许我调侃一下来结束本文——“不用担心React的许可证问题,因为前端不是一年半就用新的框架重写一次么?”哈哈。

延伸阅读

马克曼听证会 – Markman Hearing

马克曼听证会的一些背景知识,下面的文字来源于《“马克曼听证”制度的由来及启示

与美国专利诉讼的悠长历史相比,1996年才经美国最高法院确立的“马克曼听证”(Markman Hearing,也称为Claim Construction,即权利要求书的解释)无疑是一项年轻的制度。但由于几乎所有的专利侵权诉讼中都会遇到涉案专利权利要求书的解释这一核心问题,且因“马克曼听证”结果往往清楚地预示了案件结果,经“马克曼听证”获得有利结论的一方一旦据此向法庭提起不审即判的动议,专利侵权诉讼往往可就此快速了结,因此该制度的确立成为美国专利诉讼历史上的一件大事。

“马克曼听证”制度的由来

“马克曼听证”制度确立之前,在专利侵权诉讼中的权利要求书解释,通常交由陪审团在对案件事实进行裁决时一并做出,且并不会在诉讼文件上单独就陪审团这一问题的判断进行记录。1991年,马克曼(Markman)先生因认为其拥有的专利号为RE33054的“干洗衣物贮存及追踪控制装置”专利权被Westview公司所侵犯,遂向宾夕法尼亚州东区联邦地方法院提起了专利侵权诉讼。

该专利是用扫描的方式,将客户的衣物编号扫描后输入电脑中做分类标示,并在衣物干洗过程中追踪衣物位置,干洗完成后自动将衣物放回客户固定的存贮位置。被告的产品则是同时运用扫描器和电脑两种方式,将客户干洗衣物的资料存入电脑并显示费用、日期等相关信息。本案陪审团的裁决认为被告装置构成对原告专利权利的侵犯,但该地方法院认为系争专利与被告装置在功能实施上并不一致,遂推翻陪审团的裁决,判决被告不构成侵权。

马克曼不服,于1995年向联邦上诉法院提起上诉,但其上诉理由仅为联邦地方法院错误地解释了陪审团关于专利权利要求书解释中某个词语的涵义。联邦上诉法院在审理该案时,将案件的核心问题定为两个:一是原告对于请求项解释有无权利请求陪审团裁决;二是联邦地方法院是否正确地解释了“Inventory”一词。该院多数法官经审理后认为,权利要求书范围的解释与确定,属于法律问题而非事实问题,因而属于法院权限,而不应交由陪审团决定,且此前将此问题交由陪审团确定并不妥当。同时,由于认为原告专利与被告装置存在实质功能上的差异,联邦上诉法院亦不认为被告构成专利侵权。少数持不同意见的该院法官主要是质疑这一结论违反了美国第七宪法修正案(即所有根据美国法律进行的普通法诉讼,只要争议金额超过20美元,即有要求陪审团审判的权利)。

马克曼不服,向最高法院提出上诉。1996年4月23日,美国最高法院就马克曼诉Westview器械公司案(Markman v. Westview Instruments, Inc. 517 U.S. 370 (1996))做出终审裁决,裁决认定:权利要求书的解释是联邦地区法院法官应当处理的法律问题,而不是应当由陪审团来认定的事实问题,尽管在解释权利要求书的过程中可能会包含一些对于事实问题的解释,且这样做并不违反第七修正案赋予给陪审团的权利。这一裁决标志着“马克曼听证”制度的正式确立。

“马克曼听证”制度的不足

该案判决是美国专利诉讼史上的一个重大转折。“马克曼听证”成为法官专门用于解释专利权利要求的一个经常性听证程序,用以解决专利侵权诉讼的核心问题。由于该听证并非普遍适用,因此,十几年来,联邦民事诉讼规则并未正式对其有任何规定,而是给予法院绝对的自由裁量权。但是,何时可以进行“马克曼听证”?如何进行?是否有必要进行?类似问题在一定程度上困扰了审理专利侵权案件较多的法院。

2001年,加州北区联邦地区法院率先制定了供本法院使用的专利审判专属规则(Patent Local Rules),其中第四部分即为权利要求书的解释程序(Claim Construction Proceddings),对“马克曼听证”的时间、流程、限制及当事人的义务均进行了规定。此后,各州纷纷效仿。目前,乔治亚州北区联邦法院、得克萨斯州东区联邦法院、得克萨斯州南区联邦法院、宾夕法尼亚州西区联邦法院等都制订了书面的“马克曼听证”程序指南。近年来,不断有新的案例在解释与细化着“马克曼听证”,如2006年的Wilson Sporting Goods Co.诉Hillerich & Bradsby Co.案,2005年的Phillips诉AWH Corp.案,2008年的Howmedica Osteonics Corp.诉Wright Medical Technology, Inc.案,这些司法实践大大拓展与丰富了“马克曼听证”使用的实体和程序规则,使之日渐成为美国专利诉讼中一个复杂、完备的司法程序。以至于竟然有人开发了模拟“马克曼听证”程序,只要你愿意,可以下载并训练,以熟悉和确保有真正的权利要求书解释时不会出现不利于自己的问题。

但是,该听证带来的问题也逐渐受到重视。有人质疑说该程序导致专利诉讼费用增加,因为“马克曼听证”通常会单独进行,且程序复杂,因此导致当事人花费大量的时间与精力,更为重要的是,由于40%至60%的联邦地区法院案件会在联邦巡回上诉法院被推翻,因此,花费巨大的“马克曼听证”似乎价值有限。同时,权利要求书的解释要求是不多不少,忠实于技术发明思想与发明事实,但由于地区法院分散,法官的相关技术知识不十分专业,将权利要求书解释这样的问题交给他们,难免会带来一些无法克服的问题。

“马克曼听证”制度的启示

我国民事诉讼中并无陪审团制度,案件的事实问题与法律问题均由法官审理与确定。在专利侵权诉讼中,对于案件中涉及到的技术问题可以通过专家鉴定等方式解决,但并不因此免除法官审理案件的义务,即法律问题的判断归于法官,事实的法律属性判断仍然归于法官。同时,权利要求书的解释在我国的专利侵权诉讼中并不是一个单独的程序,而是合并在案件审理过程中。因此,仅就我国的司法审判而言,“马克曼听证”制度并无直接的借鉴意义。

但是,对于那些已经走出和正在走出国门的企业来说,了解与掌握这一重要的专利诉讼程序却是极其重要的。通领科技集团的积极尝试充分证明了这一点,而且随着这一程序的不断成熟,美国国际贸易法院(ITC)也开始在审理时适用“马克曼听证”制度。所以,知道“马克曼听证”意味着什么,确保所提交的用于解释权利要求的文件确实充分,学会利用“马克曼听证”,无论是对于破解美国的专利诉讼威胁,还是为未来准备有效的法律武器,无疑都非常重要。(知识产权报 作者 魏玮)

 

(全文完)


关注CoolShell微信公众账号可以在手机端搜索文章

(转载本站文章请注明作者和出处 酷 壳 – CoolShell ,请勿用于任何商业用途)

——=== 访问 酷壳404页面 寻找遗失儿童。 ===——

陈皓 September 19, 2017 at 02:08PM
from 酷 壳 – CoolShell http://ift.tt/2wrxd0l

VN:F [1.9.22_1171]
Rating: 0.0/5 (0 votes cast)
VN:F [1.9.22_1171]
Rating: 0 (from 0 votes)
Share

【酷壳】 如何免费的让网站启用HTTPS

今天,我把CoolShell变成https的安全访问了。我承认这件事有点晚了,因为之前的HTTP的问题也有网友告诉我,被国内的电信运营商在访问我的网站时加入了一些弹窗广告。另外,HTTP的网站在搜索引擎中的rank会更低。所以,这事早就应该干了。现在用HTTP访问CoolShell会被得到一个 301 的HTTPS的跳转。下面我分享一下启用HTTPS的过程。

我用的是 Let’s Encrypt这个免费的解决方案。Let’s Encrypt 是一个于2015年推出的数字证书认证机构,将通过旨在消除当前手动创建和安装证书的复杂过程的自动化流程,为安全网站提供免费的SSL/TLS证书。这是由互联网安全研究小组(ISRG – Internet Security Research Group,一个公益组织)提供的服务。主要赞助商包括电子前哨基金会Mozilla基金会Akamai以及Cisco等公司(赞助商列表)。

2015年6月,Let’s Encrypt得到了一个存储在硬件安全模块中的离线的RSA根证书。这个由IdenTrust证书签发机构交叉签名的根证书被用于签署两个证书。其中一个就是用于签发请求的证书,另一个则是保存在本地的证书,这个证书用于在上一个证书出问题时作备份证书之用。因为IdenTrust的CA根证书目前已被预置于主流浏览器中,所以Let’s Encrypt签发的证书可以从项目开始就被识别并接受,甚至当用户的浏览器中没有信任ISRG的根证书时也可以。

以上介绍文字来自 Wikipedia 的 Let’s Encrypt 词条

为你的网站来安装一个证书十分简单,只需要使用电子子前哨基金会EFF的 Certbot,就可以完成。

1)首先,打开 https://certbot.eff.org 网页。

2)在那个机器上图标下面,你需要选择一下你用的 Web 接入软件 和你的 操作系统。比如,我选的,nginx 和 Ubuntu 14.04

3)然后就会跳转到一个安装教程网页。你就照着做一遍就好了。

以Coolshell.cn为例 – Nginx + Ubuntu

首先先安装相应的环境:

$ sudo apt-get update
$ sudo apt-get install software-properties-common
$ sudo add-apt-repository ppa:certbot/certbot
$ sudo apt-get update
$ sudo apt-get install python-certbot-nginx

然后,运行如下命令:

$ sudo certbot --nginx

certbot 会自动检查到你的 nginx.conf 下的配置,把你所有的虚拟站点都列出来,然后让你选择需要开启 https 的站点。你就简单的输入列表编号(用空格分开),然后,certbot 就帮你下载证书并更新 nginx.conf 了。

你打开你的 nginx.conf 文件 ,你可以发现你的文件中的 server 配置中可能被做了如下的修改:

listen 443 ssl; # managed by Certbot
ssl_certificate /etc/letsencrypt/live/http://ift.tt/2wOZrB9; # managed by Certbot
ssl_certificate_key /etc/letsencrypt/live/http://ift.tt/2vpjHpD; # managed by Certbot
include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot

 # Redirect non-https traffic to https
if ($scheme != "https") {
  return 301 https://$host$request_uri;
} # managed by Certbot

然后,就 nginx -s reload 就好了。

但是,Let’s Encrypt 的证书90天就过期了,所以,你还要设置上自动化的更新脚本,最容易的莫过于使用 crontab 了。使用 crontab -e 命令加入如下的定时作业(每个月都强制更新一下):

0 0 1 * * certbot renew --force-renewal

当然,你也可以每天凌晨1点检查一下:

0 1 * * * certbot renew 

注:crontab 中有六个字段,其含义如下:

  • 第1个字段:分钟 (0-59)
  • 第2个字段:小时 (0-23)
  • 第3个字段:日期 (1-31)
  • 第4个字段:月份 (1-12 [12 代表 December])
  • 第5个字段:一周当中的某天 (0-7 [7 或 0 代表星期天])
  • /path/to/command – 计划执行的脚本或命令的名称

这么方便的同时,我不禁要问,如果是一些恶意的钓鱼网站也让自己的站点变成https的,这个对于一般用来说就有点难以防范了。哎……

当然,在nginx或apache上启用HTTPS后,还没有结束。因为你可能还需要修改一下你的网站,不然你的网站在浏览时会出现各种问题。

启用HTTPS后,你的网页中的所有的使用 http:// 的方式的地方都要改成 https:// 不然你的图片,js, css等非https的连接都会导致浏览器抱怨不安全而被block掉。所以,你还需要修改你的网页中那些 hard code http:// 的地方。

对于我这个使用wordpress的博客系统来说,有这么几个部分需要做修改。

1)首先是 wordpress的 常规设置中的 “WordPress 地址” 和 “站点地址” 需要变更为 https 的方式。

2)然后是文章内的图片等资源的链接需要变更为 https 的方式。对此,你可以使用一个叫 “Search Regex” 插件来批量更新你历史文章里的图片或别的资源的链接。比如:把 http://coolshell.cn 替换成了 https://coolshell.cn

3)如果你像我一样启用了文章缓存(我用的是WP-SuperCache插件),你还要去设置一下 “CDN” 页面中的 “Site URL” 和 “off-site URL” 确保生成出来的静态网页内是用https做资源链接的。

基本上就是这些事。希望大家都来把自己的网站更新成 https 的。

嗯,12306,你什么时候按照这个教程做一下你的证书?

(全文完)


关注CoolShell微信公众账号可以在手机端搜索文章

(转载本站文章请注明作者和出处 酷 壳 – CoolShell ,请勿用于任何商业用途)

——=== 访问 酷壳404页面 寻找遗失儿童。 ===——

陈皓 August 26, 2017 at 02:06PM
from 酷 壳 – CoolShell http://ift.tt/2xAOZu8

VN:F [1.9.22_1171]
Rating: 0.0/5 (0 votes cast)
VN:F [1.9.22_1171]
Rating: 0 (from 0 votes)
Share

【酷壳】 我看绩效考核

(本来,这篇文章应该在5月份完成,我拖延症让我今天才完成)

前些天,有几个网友找我谈绩效考核的事,都是在绩效上被差评的朋友。在大致了解情况后,我发现他们感到沮丧和郁闷的原因,不全是自己没有做好事情,他们对于自己没有做好公司交给的事,一方面,持一些疑义,因为我很明显地感到他们和公司对一件是否做好的标准定义有误差,另一方面,他们对于自己的工作上的问题也承认。不过,让他们更多感到沮丧的原因则是,公司、经理或HR和他们的谈话,让他们感觉整个人都被完全否定了,甚至有一种被批斗的感觉。这个感觉实在是太糟糕了。

因为我也有相似的经历,所以,我想在这里写下一篇文章,谈谈自己的对一些绩效考核的感受。先放出我的两个观点:

1)制定目标和绩效,目的不是用来考核人的,而用来改善提高组织和人员业绩和效率的。

2)人是复杂的,人是有状态波动的,任何时候都不应该轻易否定人,绩效考核应该考核的是事情,而不是人。

我个人比较坚持的认为——绩效分应该打给项目,打给产品,打给部门,打给代码,而不是打给人。然而现在的管理体制基本上都是打给人,而很多根本不擅长管理的经理和HR以及很多不会独立思考的吃瓜群众基本上都会把矛头指向个人,所以,当然会有开批斗会的感觉。

 

举几个例子

为了讲清楚我的上述观点,请让我先铺垫一下,先说几个例子吧,韩寒的例子我就不说了。

苏步青同学在小学时成绩很糟糕,全班倒数第一。

华罗庚同学上学时数学还考不及格,要不是王维克老师的鼓励并让他受上了数学,他可能也就完全埋没了。

爱因斯坦同学三岁时不会讲话,九岁时说不清话,小学和中学成绩很普通,与人交往能力太差,老师和同学都不喜欢他,教他希腊文和拉丁文的老师对他更是厌恶,曾经公开骂他:“爱因斯坦,你长大后肯定不会成器。”而且因为怕他在课堂上会影响其他学生,竟想把他赶出校门。爱因斯坦也是想尽了办法逃学。十六岁的爱因斯坦报考了瑞士苏黎世的联邦工业大学工程系,可是入学考试却告失败。

郑渊洁上学时,老师要求写《早起的鸟有虫子吃》,郑渊洁唱反调写《早起的虫子被鸟吃》,再加上数学老师发难,于是被开除了。从此郑渊洁没有上过一天学。

列夫尔斯泰大贵族出身,2岁丧母,9岁丧父,16岁上大学,大学三年级自动退学回家进行改革。在青年时期不好好读书,考试不及格,留级。他赌博、借债、鬼混……

这个的例子太多了,我从另一个方面举几个体育运动相关的例子,可能年轻的朋友都不知道,可以问问你们的父母。

80年代,中国有一批非常优秀的体育运动员,比如:体操王子李宁,打破过世界跳高记录的朱建华,还有乒乓球世界冠军马文革,还有羽毛球世界冠军赵建华,记得有一年参加世界比较,他们全输了,而输的还很惨。于是国内的一些媒体和民众开始骂他们,甚至说他们是民族的败类、耻辱,还有很多人找上门要教训他们……

如果我们把绩效分比做在学校里的考试分,那么你是否会和我一样认为,考试的成绩只能代表这个人对这些知识点的掌握或理解,而且仅仅在这个时间点,根本不代表这个人根本就不行,更不代表他一直不行。因为挂科太多被学校开除的同学,并不见得这些人在社会上就无活生活下去,反而,他们中的有些人可能会考试成绩好的人还活得好。不是么?这样的例子在我们身边还少吗?

所以,当我看到某HR说某老员工——“他今天要不自己离开,未来一年也一定会因为绩效问题而被公司开了的”,除了感到居然有人类可以预知他人未来的可笑之外,我感到是一种悲哀,一种管理体制上的悲哀,我感到了在这HR考评背后一股非常强的暗流和不可见的力量让她干出了这样一件匪夷所思的事。

好些公司还考评价值观,价值观无可厚非,我觉得一个企业的价值观是非常必要的,但是考核价值观是件非常危险的事情。这个世界上和传统势力唱反调的人实在是太多了,而被定性为价值观有问题被迫害的人也是多了去了。被批斗被侮辱被毒打的老舍;因为同性恋问题,被迫害而自杀的图灵;因为不同意“地心说”被烧死的哥白尼,…… 这样的事情已经够多了,新的时代里不应该再发生这样的事了,无论大小。

考核价值观最大的问题就是非常容易的上纲上线,也非常容易的被制造政治斗争,也非常容易的扼杀各种不同思想,老实说,这从很大程度上是一种洗脑的手段——通过对人制造一种紧张或恐惧而达到控制思想的目的。

 

对公司和管理者想说的话

下面我来谈谈绩效考核我的一些观点。在谈这个观点前,你可以移步看一下这篇新闻报道——《绩效主义毁了索尼》。而近年来,“放弃绩效考核”的斗争已经从科技企业中的Adobe、戴尔、微软、亚马逊,席卷到德勤、埃森哲、普华永道等咨询服务类企业。甚至通用电气(GE)——曾经的绩效管理的鼻祖,也宣布抛弃正式的年度绩效考核。在刚过去的2016年,腾讯的张小龙对微信事业群发出“警惕KPI”的呼声;李彦宏在内部信中将百度的掉队归咎于“从管理层到员工对短期KPI的追逐”;雷军干脆宣布小米“继续坚持‘去KPI’的战略,放下包袱,解掉绳索,开开心心地做事。”;王石也在个人微博中感慨:“绩效主义像企业的脓包”。

绩效考核在本质上就是像学校教育以分数论英雄,而忽略员工的成长和素质教育是一个道理。当学生和老师只关注考试分数时,而只有考试分数来评价老师和学生的优良中差时,老师和学生就会开始使用一些非常形式的方式来达到这个目标,比如:死记硬被,记套路,题海战术…… 而学习的能力的考评彻底地沦为了一种形式主义。反而,分数考的越高,脑子越死。(注:美国现行教育是不允许通过学生考试成绩来评价老师的能力的)

近几年来,一些大公司开始使用 OKR – Objectives, Key Result ,但是在实践过程中,我发现好些公司用OKR,本质上还是KPI – Key Performance Indicator, 因为OKR里面有一个Key Result,用来衡量 Objectives 的结果指标。于是,使用者习惯性的设置上了KPI。我个人认为 OKR 有两个非常大的特性:0)由员工提出,1)以目标为导向。2)全员共享。

举个例子,OKR可能会是制定成下面这个样子的:

Objectives:增强用户体验,

Key Results:

1)用户操作步骤减少20%以上,

2)客服减少40%以上工单,

3)用户99.9%的系统操作的响应时间为100ms以下

然后,把这个目标分解给产品、用户体验、技术团队,形成子的Objectives并关连上相庆的父级的Key Result,比如,产品部门定义的Objectives:1)优化注册流程,减少2个步骤,2)优化红包算法,让用户更容易理解,3)提高商品质量,减少用户投诉。后端技术团队定义的Objectives: 1)定义SLA以及相关监控指标,2)自动化运维,减少故障恢复时间,3)提高性能,吞吐量在xxxqps下的99.9%的响应时间为xxms ……

这个Objective会从公司最高层一直分解到一线员工,信息完全透明,每个人都可以看到所有人被分解到目标,每个人都知道自己在为什么样的目标而奋头,而每个人也可以质疑,改进,建议调整最高层的目标和方向。而不是领到的是被层层消化过的变味的二手,三手甚至四五手的信息。

而 KPI 最大的问题就是用 OKR 里的 Key Results 拿来当目标,从而导致员工只知道要做什么,不知道为什么,不知道为什么,不能理解目标,工作也就成了实实在在的应付!

松下公司早在1933年,就召集168名员工,把松下未来250年的远景规划目标公布于众,从1956年开始,就定期宣布并解读自身的“五年计划”,帮助每位员工的目光从眼前的短期利益移开,树立自己的理想和目标,也促进了松下的可持续性发展。

然而,今时不同往昔,随着产品周期的不断缩减、竞争对手的持续涌入、高新技术的频频迭代,企业的战略的变化与调整变得更加频繁,朝令夕改的经营策略已经成为兵家常态。 在这一过程中,有多少员工了解调整之后的战略呢?员工的绩效指标又根据战略调整多少次了呢?

KPI本身是一种被动的、后置的考察,在工作完成之后考察员工的行为是否符合标准。因此,员工对于公司的目标漠不关心,只关心自己的KPI,因为这才是自己的最大的利益,为了达到KPI,有的员工开始不思考,并使用一些简单粗暴的玩法,其实这样既害了公司,也害了自己。自己的成长和进步也因为强大的 KPI 而抛在了脑后。

当然,KPI 绩效考核一般来说,不一定会毁掉公司的,相反,对于喜欢使用蛮力的劳动密集型的公司来说,可能还有所帮助,然而,KPI毁掉的一定是团队的文化和团队的挑战精神,以及创新和对事业的热情,甚至会让其中的人失去应有的正常的判断力(分不清充分和必要条件,分不清很多事的因果关系)。

 

对职场人想说的话

那么,对于个人来说,如何面对公司给自己的绩效考核呢?如何面对他们的绩效考核呢?

还是用学校考试分数来做对比,如果说,用考试分数论英雄,一个人考高分就是绩效上的人才,考不及格的人就是人渣,这对吗?当然不是。也许仅于对于考试来说可以把人分成三六九等,但是对于整个人生来说,考试成绩和一个人在这个社会里的的成就并没有非常直接的因果关系。面对现实的社会,最终很多成绩好的人为成绩差的人工作的例子也有很多很多了。

我想说什么?我想说的是——用一颗平常心来面对公司给你打的分数,因为那并不代表你的整个人生。但是,你要用一颗非常严肃的心来面对自己的个人发展和成长,因为这才是真正的事。

换句话说,如果要给一个人打绩效分,那不是由一个公司在一个短期的时间时打出来,而是由这个人在一个长期的时间里所能达到的成就得出来的。

就像WhatsApp的联合创始人Brian Acton 在 2009年时面试Facebook时没有面试通过,然而在 5 年以后,他把自己创办的公司以190亿美元卖给了FaceBook。阿里巴巴的马云不也一样吗?找工作各种被拒,开办的第一个公司成绩也不好,20年前,一堆人都说马云这也不行那也不行,然而,后面呢?反过来说,也很多职业经理人在公司里绩效非常好,然后到了创业公司却搞得非常的糟糕,这又说明了什么呢?

这就像动物一样,有的动物适合在水里生活,有的动物适合在陆地上,鱼在陆地上是无法生存的,你让老虎去完成游泳的工作,你让鱼去完成鸟类的工作,你能考核到什么呢?我们每个人都有适合自己的环境,找到适合自己的环境才是最关键的!与其去关注别人对自己的评价,不如去寻找适合自己的环境。

所以,一个特定环境下的绩效考核并不代表什么,而那些妄图用绩效考核去否定一个人的做法,或多或少就是“法西斯”或“红卫兵”的玩法

好了!让我们不要再说绩效考核了,让我们回到,真正让自己提高,让自己成长,让自己的强的话题上来吧。这里,我需要转引一篇文章《Do the Right Thing, Wait go get fired》,文中提到《 Team Geek》这本书中的一句话

做正确的事情,等着被开除。

谷歌新员工(我们称做“Nooglers”)经常会问我是如何让自己做事这么高效的。我半开玩笑的告诉他们这很简单:我选择做正确的事情,为谷歌,为世界,然后回到座位上,等着被开除。如果没有被开除,那我就是做了正确的事情——为所有人。如果被开除了,那选错了老板。总之,两方面,我都是赢。这是我的职业发展策略。

嗯,考试分数不是关键,别人对你的评价也不是关键,自己有没有成长有没有提高有没有上一个台阶才是关键。KPI不是关键,有没有在做正确的事,这才是关键!不是这样吗?

其它

我大学四年级时,觉得马上就要离开学校了,当时想干点以后再以没有机会干的事。想来想去,就是上学这么多年来,从来没有不及格过,于是我任性了一把,挂了一个科,去补考了一下。挂科的时候也收到一些同学的笑话,还有老师的批评,不过,这让我感觉我的学校经历更完整了。因为,这让我在22岁的时候,就经历并大概明白了一些人生的道理。

从98年工作到2013年来,就像一个好学生一样,我从来没有出现过任何的工作绩效问题,反正还经常在工作中成为标杠型的人,然并卵,只有自己成长才是最真实的感觉。“做正确的事,等着被开除”,这可能是我迄今为止在职场里做的最疯狂也是最正确的事了。因为,这让我有更多的经历,也让我内心变得越来越强大,也让我找到了更具挑战的事,更让我对自己有更清楚的认识。

最后,我知道一定会有人来怼我,所以,最后我还想留段话,留给那些还是想通过绩效来否定人的人。

如果你对我的绩效或技术能力有怀疑,没问题,那么希望你能做到下述我已做到的事,再来喷我,谢谢!

在你40岁,在父亲病重,孩子上学问题、房贷并未还清、你是全家唯一收入来源之类的中年危机的情况下,辞去你现在的工作,不加入任何一家公司,不用自己的任何一分钱积蓄,不要任何人的投资和帮助。只通过自己的技术能力,为别人解决相应的技术难题(不做任何无技术含量的外包项目),来生存养家,并除了能照顾好自己的家人没有降低自己的生活水平之外,还能在养活3个每人年薪36万元的工程师

请问这样的绩效能打个几分呢?呵呵。

当然,不管怎么说,我还有很路要走,还有很多不足,我还要继续努力。所以,我挑了一条对我来说最难走的路,作死创业……

(全文完)


关注CoolShell微信公众账号可以在手机端搜索文章

(转载本站文章请注明作者和出处 酷 壳 – CoolShell ,请勿用于任何商业用途)

——=== 访问 酷壳404页面 寻找遗失儿童。 ===——

陈皓 July 09, 2017 at 06:03PM
from 酷 壳 – CoolShell http://ift.tt/2tZDRsi

VN:F [1.9.22_1171]
Rating: 0.0/5 (0 votes cast)
VN:F [1.9.22_1171]
Rating: 0 (from 0 votes)
Share

【酷壳】 Go语言的修饰器编程

之前写过一篇《Python修饰器的函数式编程》,这种模式很容易的可以把一些函数装配到另外一些函数上,可以让你的代码更为的简单,也可以让一些“小功能型”的代码复用性更高,让代码中的函数可以像乐高玩具那样自由地拼装。所以,一直以来,我对修饰器decoration这种编程模式情有独钟,这里写一篇Go语言相关的文章。

看过Python修饰器那篇文章的同学,一定知道这是一种函数式编程的玩法——用一个高阶函数来包装一下。多唠叨一句,关于函数式编程,可以参看我之前写过一篇文章《函数式编程》,这篇文章主要是,想通过从过程式编程的思维方式过渡到函数式编程的思维方式,从而带动更多的人玩函数式编程,所以,如果你想了解一下函数式编程,那么可以移步先阅读一下。所以,Go语言的修饰器编程模式,其实也就是函数式编程的模式。

不过,要提醒注意的是,Go 语言的“糖”不多,而且又是强类型的静态无虚拟机的语言,所以,无法做到像 Java 和 Python 那样的优雅的修饰器的代码。当然,也许是我才才疏学浅,如果你知道有更多的写法,请你一定告诉我。先谢过了。

简单示例

我们先来看一个示例:

继续阅读

VN:F [1.9.22_1171]
Rating: 0.0/5 (0 votes cast)
VN:F [1.9.22_1171]
Rating: 0 (from 0 votes)
Share

如何重构“箭头型”代码

本文主要起因是,一次在微博上和朋友关于嵌套好几层的if-else语句的代码重构的讨论(微博原文),在微博上大家有各式各样的问题和想法。按道理来说这些都是编程的基本功,似乎不太值得写一篇文章,不过我觉得很多东西可以从一个简单的东西出发,到达本质,所以,我觉得有必要在这里写一篇的文章。不一定全对,只希望得到更多的讨论,因为有了更深入的讨论才能进步。

文章有点长,我在文章最后会给出相关的思考和总结陈词,你可以跳到结尾。

所谓箭头型代码,基本上来说就是下面这个图片所示的情况。

那么,这样“箭头型”的代码有什么问题呢?看上去也挺好看的,有对称美。但是……

关于箭头型代码的问题有如下几个:

1)我的显示器不够宽,箭头型代码缩进太狠了,需要我来回拉水平滚动条,这让我在读代码的时候,相当的不舒服。

2)除了宽度外还有长度,有的代码的if-else里的if-else里的if-else的代码太多,读到中间你都不知道中间的代码是经过了什么样的层层检查才来到这里的。

总而言之,“箭头型代码”如果嵌套太多,代码太长的话,会相当容易让维护代码的人(包括自己)迷失在代码中,因为看到最内层的代码时,你已经不知道前面的那一层一层的条件判断是什么样的,代码是怎么运行到这里的,所以,箭头型代码是非常难以维护和Debug的

微博上的案例 与 Guard Clauses

OK,我们先来看一下微博上的那个示例,代码量如果再大一点,嵌套再多一点,你很容易会在条件中迷失掉(下面这个示例只是那个“大箭头”下的一个小箭头)

FOREACH(Ptr<WfExpression>, argument, node->arguments) {
    int index = manager->expressionResolvings.Keys().IndexOf(argument.Obj());
    if (index != -1) {
        auto type = manager->expressionResolvings.Values()[index].type;
        if (! types.Contains(type.Obj())) {
            types.Add(type.Obj());
            if (auto group = type->GetTypeDescriptor()->GetMethodGroupByName(L"CastResult", true)) {
                int count = group->GetMethodCount();
                for (int i = 0; i < count; i++) { auto method = group->GetMethod(i);
                    if (method->IsStatic()) {
                        if (method->GetParameterCount() == 1 &&
                            method->GetParameter(0)->GetType()->GetTypeDescriptor() == description::GetTypeDescriptor<DescriptableObject>() &&
                            method->GetReturn()->GetTypeDescriptor() != description::GetTypeDescriptor<void>() ) {
                            symbol->typeInfo = CopyTypeInfo(method->GetReturn());
                            break;
                        }
                    }
                }
            }
        }
    }
}

上面这段代码,可以把条件反过来写,然后就可以把箭头型的代码解掉了,重构的代码如下所示:

FOREACH(Ptr<WfExpression>, argument, node->arguments) {
    int index = manager->expressionResolvings.Keys().IndexOf(argument.Obj());
    if (index == -1)  continue;
    
    auto type = manager->expressionResolvings.Values()[index].type;
    if ( types.Contains(type.Obj()))  continue;
    
    types.Add(type.Obj());

    auto group = type->GetTypeDescriptor()->GetMethodGroupByName(L"CastResult", true);
    if  ( ! group ) continue;
 
    int count = group->GetMethodCount();
    for (int i = 0; i < count; i++) { auto method = group->GetMethod(i);
        if (! method->IsStatic()) continue;
       
        if ( method->GetParameterCount() == 1 &&
               method->GetParameter(0)->GetType()->GetTypeDescriptor() == description::GetTypeDescriptor<DescriptableObject>() &&
               method->GetReturn()->GetTypeDescriptor() != description::GetTypeDescriptor<void>() ) {
            symbol->typeInfo = CopyTypeInfo(method->GetReturn());
            break;
        }
    }
}

这种代码的重构方式叫 Guard Clauses

这里的思路其实就是,让出错的代码先返回,前面把所有的错误判断全判断掉,然后就剩下的就是正常的代码了

抽取成函数

微博上有些人说,continue 语句破坏了阅读代码的通畅,我觉得他们一定没有好好读这里面的代码,其实,我们可以看到,所有的 if 语句都是在判断是否出错的情况,所以,在维护代码的时候,你可以完全不理会这些 if 语句,因为都是出错处理的,而剩下的代码都是正常的功能代码,反而更容易阅读了。当然,一定有不是上面代码里的这种情况,那么,不用continue ,我们还能不能重构呢?

当然可以,抽成函数:

bool CopyMethodTypeInfo(auto &method, auto &group, auto &symbol) 
{
    if (! method->IsStatic()) {
        return true;
    }
    if ( method->GetParameterCount() == 1 &&
           method->GetParameter(0)->GetType()->GetTypeDescriptor() == description::GetTypeDescriptor<DescriptableObject>() &&
           method->GetReturn()->GetTypeDescriptor() != description::GetTypeDescriptor<void>() ) {
        symbol->typeInfo = CopyTypeInfo(method->GetReturn());
        return false;
    }
    return true;
}

void ExpressionResolvings(auto &manager, auto &argument, auto &symbol) 
{
    int index = manager->expressionResolvings.Keys().IndexOf(argument.Obj());
    if (index == -1) return;
    
    auto type = manager->expressionResolvings.Values()[index].type;
    if ( types.Contains(type.Obj())) return;

    types.Add(type.Obj());
    auto group = type->GetTypeDescriptor()->GetMethodGroupByName(L"CastResult", true);
    if  ( ! group ) return;

    int count = group->GetMethodCount();
    for (int i = 0; i < count; i++) { auto method = group->GetMethod(i);
        if ( ! CopyMethodTypeInfo(method, group, symbol) ) break;
    }
}

...
...
FOREACH(Ptr<WfExpression>, argument, node->arguments) {
    ExpressionResolvings(manager, arguments, symbol)
}
...
...

你发出现,抽成函数后,代码比之前变得更容易读和更容易维护了。不是吗?

有人说:“如果代码不共享,就不要抽取成函数!”,持有这个观点的人太死读书了。函数是代码的封装或是抽象,并不一定用来作代码共享使用,函数用于屏蔽细节,让其它代码耦合于接口而不是细节实现,这会让我们的代码更为简单,简单的东西都能让人易读也易维护。这才是函数的作用。

嵌套的 if 外的代码

微博上还有人问,原来的代码如果在各个 if 语句后还有要执行的代码,那么应该如何重构。比如下面这样的代码。

for(....) {
    do_before_cond1()
    if (cond1) {
        do_before_cond2();
        if (cond2) {
            do_before_cond3();
            if (cond3) {
                do_something();
            }
            do_after_cond3();
        }
        do_after_cond2();
    }
    do_after_cond1();
}

上面这段代码中的那些 do_after_condX() 是无论条件成功与否都要执行的。所以,我们拉平后的代码如下所示:

for(....) {
    do_before_cond1();
    if ( !cond1 ) {
        do_after_cond1();
        continue
    } 
    do_after_cond1();

    do_before_cond2();
    if ( !cond2 ) { 
        do_after_cond2();
        continue;
    }
    do_after_cond2();

    do_before_cond3();
    if ( !cond3 ) {
        do_after_cond3();
        continue;
    }
    do_after_cond3();

    do_something();  
}

你会发现,上面的 do_after_condX 出现了两份。根据 DRY 原则,我们保留一份,那么直接掉到 if 条件前就好了,如下所示:

for(....) {
    do_before_cond1();
    do_after_cond1();
    if ( !cond1 ) continue;
 
    do_before_cond2();
    do_after_cond2();
    if ( !cond2 ) continue;

    do_before_cond3();
    do_after_cond3();
    if ( !cond3 ) continue;

    do_something();  
}

此时,你会说,我靠,居然,改变了执行的顺序,把条件放到 do_after_condX() 后面去了。这会不会有问题啊?

其实,你再分析一下之前的代码,你会发现,本来,cond1 是判断 do_before_cond1() 是否出错的,如果有成功了,才会往下执行。而 do_after_cond1() 是无论如何都要执行的。从逻辑上来说,do_after_cond1()其实和do_before_cond1()的执行结果无关,而 cond1 却和是否去执行 do_before_cond2() 相关了。如果我把断行变成下面这样,反而代码逻辑更清楚了。

for(....) {

    do_before_cond1();
    do_after_cond1();


    if ( !cond1 ) continue;  // <-- cond1 成了是否做第二个语句块的条件
    do_before_cond2();
    do_after_cond2();

    if ( !cond2 ) continue; // <-- cond2 成了是否做第三个语句块的条件
    do_before_cond3();
    do_after_cond3();

    if ( !cond3 ) continue; //<-- cond3 成了是否做第四个语句块的条件
    do_something(); 
 
}

于是乎,在未来维护代码的时候,维护人一眼看上去就明白,代码在什么时候会执行到哪里。 这个时候,你会发现,把这些语句块抽成函数,代码会干净的更多,再重构一版:

bool do_func3() {
   do_before_cond2();
   do_after_cond2();
   return cond3;
}

bool do_func2() {
   do_before_cond2();
   do_after_cond2();
   return cond2;
}

bool do_func1() {
   do_before_cond1();
   do_after_cond1();
   return cond1;
}

// for-loop 你可以重构成这样
for (...) {
    bool cond = do_func1();
    if (cond) cond = do_func2();
    if (cond) cond = do_func3();
    if (cond) do_something();
}

// for-loop 也可以重构成这样
for (...) {
    if ( ! do_func1() ) continue;
    if ( ! do_func2() ) continue;
    if ( ! do_func3() ) continue;
    do_something();
}

上面,我给出了两个版本的for-loop,你喜欢哪个?我喜欢第二个。这个时候,因为for-loop里的代码非常简单,就算你不喜欢 continue ,这样的代码阅读成本已经很低了。

状态检查嵌套

接下来,我们再来看另一个示例。下面的代码的伪造了一个场景——把两个人拉到一个一对一的聊天室中,因为要检查双方的状态,所以,代码可能会写成了“箭头型”。

int ConnectPeer2Peer(Conn *pA, Conn* pB, Manager *manager)
{
    if ( pA->isConnected() ) {
        manager->Prepare(pA);
        if ( pB->isConnected() ) {
            manager->Prepare(pB);
            if ( manager->ConnectTogther(pA, pB) ) {
                pA->Write("connected");
                pB->Write("connected");
                return S_OK;
            }else{
                return S_ERROR;
            }

        }else {
            pA->Write("Peer is not Ready, waiting...");
            return S_RETRY;
        }
    }else{
        if ( pB->isConnected() ) {
            manager->Prepare();
            pB->Write("Peer is not Ready, waiting...");
            return S_RETRY;
        }else{
            pA->Close();
            pB->Close();
            return S_ERROR;
        }
    }
    //Shouldn't be here!
    return S_ERROR;
}

重构上面的代码,我们可以先分析一下上面的代码,说明了,上面的代码就是对 PeerA 和 PeerB 的两个状态 “连上”, “未连上” 做组合 “状态” (注:实际中的状态应该比这个还要复杂,可能还会有“断开”、“错误”……等等状态), 于是,我们可以把代码写成下面这样,合并上面的嵌套条件,对于每一种组合都做出判断。这样一来,逻辑就会非常的干净和清楚。

int ConnectPeer2Peer(Conn *pA, Conn* pB, Manager *manager)
{
    if ( pA->isConnected() ) {
        manager->Prepare(pA);
    }

    if ( pB->isConnected() ) {
        manager->Prepare(pB);
    }

    // pA = YES && pB = NO
    if (pA->isConnected() && ! pB->isConnected()  ) {
        pA->Write("Peer is not Ready, waiting");
        return S_RETRY;
    // pA = NO && pB = YES
    }else if ( !pA->isConnected() && pB->isConnected() ) {
        pB->Write("Peer is not Ready, waiting");
        return S_RETRY;
    // pA = YES && pB = YES
    }else if (pA->isConnected() && pB->isConnected()  ) {
        if ( ! manager->ConnectTogther(pA, pB) ) {
            return S_ERROR;
        }
        pA->Write("connected");
        pB->Write("connected");
        return S_OK;
    }

    // pA = NO, pB = NO
    pA->Close();
    pB->Close();
    return S_ERROR;
}

延伸思考

对于 if-else 语句来说,一般来说,就是检查两件事:错误状态

检查错误

对于检查错误来说,使用 Guard Clauses 会是一种标准解,但我们还需要注意下面几件事:

1)当然,出现错误的时候,还会出现需要释放资源的情况。你可以使用 goto fail; 这样的方式,但是最优雅的方式应该是C++面向对象式的 RAII 方式。

2)以错误码返回是一种比较简单的方式,这种方式有很一些问题,比如,如果错误码太多,判断出错的代码会非常复杂,另外,正常的代码和错误的代码会混在一起,影响可读性。所以,在更为高组的语言中,使用 try-catch 异常捕捉的方式,会让代码更为易读一些。

检查状态

对于检查状态来说,实际中一定有更为复杂的情况,比如下面几种情况:

1)像TCP协议中的两端的状态变化。

2)像shell各个命令的命令选项的各种组合。

3)像游戏中的状态变化(一棵非常复杂的状态树)。

4)像语法分析那样的状态变化。

对于这些复杂的状态变化,其本上来说,你需要先定义一个状态机,或是一个子状态的组合状态的查询表,或是一个状态查询分析树。

写代码时,代码的运行中的控制状态或业务状态是会让你的代码流程变得混乱的一个重要原因,重构“箭头型”代码的一个很重要的工作就是重新梳理和描述这些状态的变迁关系

总结

好了,下面总结一下,把“箭头型”代码重构掉的几个手段如下:

1)使用 Guard Clauses 。 尽可能的让出错的先返回, 这样后面就会得到干净的代码。

2)把条件中的语句块抽取成函数。 有人说:“如果代码不共享,就不要抽取成函数!”,持有这个观点的人太死读书了。函数是代码的封装或是抽象,并不一定用来作代码共享使用,函数用于屏蔽细节,让其它代码耦合于接口而不是细节实现,这会让我们的代码更为简单,简单的东西都能让人易读也易维护,写出让人易读易维护的代码才是重构代码的初衷

3)对于出错处理,使用try-catch异常处理和RAII机制。返回码的出错处理有很多问题,比如:A) 返回码可以被忽略,B) 出错处理的代码和正常处理的代码混在一起,C) 造成函数接口污染,比如像atoi()这种错误码和返回值共用的糟糕的函数。

4)对于多个状态的判断和组合,如果复杂了,可以使用“组合状态表”,或是状态机加Observer的状态订阅的设计模式。这样的代码即解了耦,也干净简单,同样有很强的扩展性。

5) 重构“箭头型”代码其实是在帮你重新梳理所有的代码和逻辑,这个过程非常值得为之付出。重新整思路去想尽一切办法简化代码的过程本身就可以让人成长。

(全文完)


关注CoolShell微信公众账号可以在手机端搜索文章

(转载本站文章请注明作者和出处 酷 壳 – CoolShell ,请勿用于任何商业用途)

——=== 访问 酷壳404页面 寻找遗失儿童。 ===——

陈皓 April 05, 2017 at 06:07PM
from 酷 壳 – CoolShell http://ift.tt/2nJ6B2n

VN:F [1.9.22_1171]
Rating: 0.0/5 (0 votes cast)
VN:F [1.9.22_1171]
Rating: 0 (from 0 votes)
Share

AWS 的 S3 故障回顾和思考

Gitlab的误删除数据事件没几天,“不沉航母” AWS S3 (Simple Storage Service)几天前也“沉”了4个小时,墙外的半个互联网也跟着挂了。如约,按 AWS 惯例,AWS今天给出了一个简单的故障报告《Summary of the Amazon S3 Service Disruption in the Northern Virginia (US-EAST-1) Region》。这个故障和简单来说和Gitlab一样,也是人员误操作。先简单的说一下这份报中说了什么。

故障原因

简单来说,这天,有一个 AWS 工程师在调查 Northern Virginia (US-EAST-1) Region 上 S3 的一个和账务系统相关的问题,这个问题是S3的账务系统变慢了(我估计这个故障在Amazon里可能是Sev2级,Sev2级的故障在Amazon算是比较大的故障,需要很快解决),Oncall的开发工程师(注:Amazon的运维都是由开发工程师来干的,所以Amazon内部嬉称SDE-Software Developer Engineer 为 Someone Do Everything)想移除一个账务系统里的一个子系统下的一些少量的服务器(估计这些服务器上有问题,所以想移掉后重新部署),结果呢,有一条命令搞错了,导致了移除了大量的S3的控制系统。包括两个很重要的子系统:

继续阅读

VN:F [1.9.22_1171]
Rating: 0.0/5 (0 votes cast)
VN:F [1.9.22_1171]
Rating: 0 (from 0 votes)
Share

从Gitlab误删除数据库想到的

昨天,Gitlab.com发生了一个大事,某同学误删了数据库,这个事看似是个低级错误,不过,因为Gitlab把整个过程的细节都全部暴露出来了,所以,可以看到很多东西,而对于类似这样的事情,我自己以前也干过,而在最近的两公司中我也见过(Amazon中见过一次,阿里中见过至少四次),正好通过这个事来说说一下自己的一些感想和观点吧。我先放个观点:你觉得有备份系统就不会丢数据了吗?

事件回顾

整个事件的回顾Gitlab.com在第一时间就放到了Google Doc上,事后,又发了一篇Blog来说明这个事,在这里,我简单的回顾一下这个事件的过程。

首先,一个叫YP的同学在给gitlab的数据库做一些负载均衡的工作,在做这个工作时的时候突发了一个情况,Gitlab被DDoS攻击,数据库的使用飙高,在block完攻击者的IP后,发现有个staging的数据库(db2.staging)已经落后生产库4GB的数据,于是YP同学在Fix这个staging库的同步问题的时候,发现db2.staging有各种问题都和主库无法同步,在这个时候,YP同学已经工作的很晚了,在尝试过很多方法后,发现db2.staging都hang在那里,无法同步,于是他想把db2.staging的数据库删除了,这样全新启动一个新的复制,结果呢,删除数据库的命令错误的敲在了生产环境上(db1.cluster),结果导致整个生产数据库被误删除。(陈皓注:这个失败基本上就是 “工作时间过长” + “在多数终端窗口中切换中迷失掉了”

继续阅读

VN:F [1.9.22_1171]
Rating: 0.0/5 (0 votes cast)
VN:F [1.9.22_1171]
Rating: 0 (from 0 votes)
Share

Chrome开发者工具中的小技巧

Chrome的开发者工具是个很强大的东西,相信程序员们都不会陌生,不过有些小功能可能并不为大众所知,所以,写下这篇文章,有的功能可能会比较实用,有的则不一定,也欢迎大家交流。

话不多话,我们开始。

代码格式化

有很多css/js的代码都会被 minify 掉,你可以点击代码窗口左下角的那个 { }  标签,chrome会帮你给格式化掉。

强制DOM状态

有些HTML的DOM是有状态的,比如<a> 标签,其会有 active,hover, focus,visited这些状态,有时候,我们的CSS会来定关不同状态的样式,在分析网页查看网页上DOM的CSS样式时,我们可以点击CSS样式上的 :hov 这个小按钮来强制这个DOM的状态。

继续阅读

VN:F [1.9.22_1171]
Rating: 0.0/5 (0 votes cast)
VN:F [1.9.22_1171]
Rating: 0 (from 0 votes)
Share

从 MongoDB “赎金事件” 看安全问题

今天上午(2017年1月7日),我的微信群中同时出现了两个MongoDB被黑掉要赎金的情况,于是在调查这个事的过程中,我发现了这个事。这个事件应该是2017年开年的第一次比较大的安全事件吧,发现国内居然没有什么报道,国内安全圈也没有什么动静(当然,他们也许知道,只是不想说吧),Anyway,让我这个非安全领域的人来帮补补位。

事件回顾

这个事情应该是从2017年1月3日开始的,是由安全圈的大拿 Victor Gevers (网名:0xDUDE 的Chairman),他在2016年12月27日,他发现了一些在互联网上用户的MongoDB没有任何的保护措施,被攻击击把数据库删除了,并留下了一个叫 WARNING 的数据库,这张表的内容如下:

{
    "_id" : ObjectId("5859a0370b8e49f123fcc7da"),
    "mail" : "harak1r1@sigaint.org",
    "note" : "SEND 0.2 BTC TO THIS ADDRESS 13zaxGVjj9MNc2jyvDRhLyYpkCh323MsMq AND CONTACT THIS EMAIL WITH YOUR IP OF YOUR SERVER TO RECOVER YOUR DATABASE !"
}

基本上如下所示:

MongoDB ransom demand (via Victor Gevers)
MongoDB ransom demand (via Victor Gevers)

说白了就是黑客留下的东西——老子把你的MongoDB里的数据库给转走了,如果你要你的数据的话,给我0.2个的比特币(大约USD200)。然后,他的twitter上不断地发布这个“赎金事件”的跟踪报道。与此同时,中国区的V2EX上也发现了相关的攻击问题 《自己装的 mongo 没有设置密码结果被黑了

继续阅读

VN:F [1.9.22_1171]
Rating: 0.0/5 (0 votes cast)
VN:F [1.9.22_1171]
Rating: 0 (from 0 votes)
Share

技术人员的发展之路

2012年的时候写过一篇叫《程序算法与人生选择》的文章,我用算法来类比如何做选择,说白了就是怎么去计算,但是并没有讲程序员可以发展的方向 有哪些。 所以,就算是有这些所谓的方法论,我们可能对自己的发展还是会很纠结和无所事从,尤其是人到了30岁,这种彷徨和迷惑越来越重。虽然我之前也写过一篇《编程年龄和编程技能》的文章,但是还是有很多做技术的人对于自己能否在年纪大时还能去做技术感到没有信心。我猜测,这其中,最大的问题的是,目前从事技术工作的种种负面的经历(比如经常性的加班,被当成棋子或劳动力等等),让人完全看不到希望和前途,尤其是随着年纪越来越大,对未来的越来越没有信心。

继续阅读

VN:F [1.9.22_1171]
Rating: 0.0/5 (0 votes cast)
VN:F [1.9.22_1171]
Rating: 0 (from 0 votes)
Share