IT技术精华 http://it.taocms.org/ 聚合国内IT技术精华文章,分享IT技术精华,帮助IT从业人士成长 2019-10-20 IT技术精华 14793 [原]演示一下发电机的原理 作者:dog250 发表于 2019/10/19 16:48:25 原文链接 [sourcelinkurl]
阅读:51

立即购买:淘宝网]]>
IT技术精华 2019-10-19 23:10:08
14780 StackOverflow 2019 程序员调查 前些天,StackOverflow 发布了 2019年的年度程序员调查,这个调查报查有90000名程序员参与,这份调度报告平均花了20分钟,可见,这份报告有很多的问题,也是很详细的。这份报告有一些地方,让我有了一些思考。

首先,我们先来看一下之份报告的 Key Results:

  • Python 成为了过去一年中成长最快的语言,把Java挤到了第二位,排在后面的是Rust语言。
  • 有半数以上的被访者在是在16岁写下自己的第一行代码。
  • DevOps Specialists 和 Site Reliability Engineers 是程序员中最有经验,技术最牛,薪资最好的职位。(这对应于国内的——系统架构师)
  • 在几个头部的程序员大国中,中国的程序员最乐观的,他们相信在今天出生的人会有比他们父母更好的人生。对于欧洲的程序员来说,比较法国和德国的程序员,他们对未来并不太乐观。
  • 对于最影响程序员生产力的事,不同的程序员有不同的想法。

第一部分,Developer Profile

在第一部分中,我们可以看到,中国程序员参与这个调查的并不多,程序员主要集中在美国、欧洲、印度这三个地方。所以,这份报告更偏国际上一些。这对于我们中国程序员也有很大的帮助,因为一方面可以看到世界发展的趋势,另一方面也可以了解我们和世界有什么不一样。

对于技术职业来说,整个世界的程序员开始趋于全栈和后端,有51.9%的人是全栈,50%的人是后端,32.8%的人是前端……在这些人中,很多程序员都选了多项,中位数是3项,最常见是前端、后端和全栈全选的。然后,接下来是选两项的,选两项目的包括:数据库管理员和系统管理员,DevOps Specialist 和 Site Reliablility Engineer, 学术研究者和科学家,设计师和前端工程师。

从这些数据中我们可以看见:前后端的界限越来越不明显,设计师和前端的界限也开始模糊。这应该说明,工具和框架的成熟,让后端程序员和设计师也可以进入到前端工程师的领域,或是前端工程师开始进入后端和设计的领域。总之,复合型人才越来越越成为主流,而前后端也趋于一个相互融合的态势。

在接下来的图表中,我们可以看到有80%以上的人是把编程当成自己的爱好(包括相关的女性)。

真是应了那句话——“Programmers who don’t code in their spare time for fun will never become as good as those that do”,是的,如果你对编程没有感到一种快乐,没有在你空闲的时候去以一种的兴趣爱好方式去面对,那么,无论是编程,还是运动,还是去旅游,都不会有太多成效的。

在接下来的编程经验上,有两组如下的数据:

学习编程的年限 编程的年限

我们可以看到无论是学习还是编程,随着时间的拉长,其人数占比越来越少。

下面我们再来看一个年龄图:

调查报告从20岁开始每隔5年划分一个年龄段,我们不难发现从25-29岁开始每个年龄段都比前一个年龄段人数急剧减少大约30-50%,比如25-29年龄段占总人数27.6%,而30-34则只有19.3%。以此类推,到60岁以上,就只剩1%。可以看出5年是大多数程序员的转型周期。这是合理的,因为5年时间足够一个人积累足够的经验技能为职业转型做准备。

我们也可以看到50岁以上的程序员只有4.2%,大约是参与调查人员的300多人,如果这些人20岁左右参加工作,那么说明他们在1990左右就开始写代码,事实上那个时间点别说是程序员了,连电脑用户都不多。电脑和互联网真正暴发的时间还是在1995年 – 2000年之间,不过,那个时间点程序员的总体人数也不多,而行业越来越火才会导致大量的人进入到这个行业中,这个转换过程基本上去需要3-5年,也就是从2000年后才开始有大量的人拥入程序员这个行业,程序员的人数在过去30年间也是呈增涨态势的,所以,我个人认为,所谓的“众多老程序员”的比例会被2005年以后大量拥入程序员行业的年青人所“稀释”。所以,上图的比例不能完全说明程序员是个青春饭

但是,我们还是要正视老牌资深的程序员越来越少的这个事实,在这份报告第三部分中说了一些和程序员职业生涯相关的调查,如下:

  • 在被问到有多少人对自己的职业满意的时。有40%的人觉得很满意,而有34.3%的人觉得一般满意,有10%的人说不清,还有15%的人是不满意的。可以看到有不少人是对这个职业生涯是有想法的。
  • 在被问到有多少人想转管理而可以挣得更多时。有30%的人是说想转的,有51%的人是明确不转的,还有20%的人是说不知道。可见,想转管理的人最多可能会有一半的人。
  • 在被问到有多少人想转管理时。有1/3的人是明确不想转的,而有1/4的人是明确是想转,而有36%的人则是不说,观望中。可见,的确是有很多想想转管理的。

我们可以看到,程序员中并不是所有的人都是可以坚持这么长时间的,这也挺正常的,对很大一部分人来说,对这个职业是有或多或少的不满意的,也有一部分人可能会随着技术的更新被淘汰,还有另外很大一部分人是想转管理的。所以,能够长时间地跟上形势长时间地喜欢写代码,并且对程序员这个的职业长期满意,不想转管理的,的确是为随时年龄的越大也越来越少

但我们完全可以看出来,程序员的主力军在20-40岁这个区间,而30岁左右的程序员是年富力强(经验和能力都很好)的黄金时间

老程序员在国外似乎不会存在多大的问题,但在国内会有一些问题,所以,对于像我一样喜欢写代码、打算长久做程序员的兄弟,这里分享一些相关的经验。

  1. 持续高效地学习。软件行业的新技术层出不穷,旧的技术淘汰很快,所以我们更要多多学习基础技术和原理,那些都是很难改变的,并且基础扎实了后,学习新的技术也才会更快速。其间我们也不要乱学新技术,我们要关注那些有潜力的技术,也就看准了再学(参看酷壳的《Go语言、Docker和新技术》)。注意,而是跟上大时代已经比较不容易,引领时代的人还是少数,所以,还是要更为高效地学习。
  2. 积极面对他人的不解。 很多时候,总是会有人说:“到了你这个年纪怎么还在做程序员?”,这句话感觉就是对程序员这个职业的一种羞辱,社会的价值观感觉容不下大龄程序员。这个时候,我一般会跟他们解释到,我40来岁了,我觉得自己的状态还很好,工作完成没什么问题,偶尔加班到凌晨也行,新知识和技术我学起来不比年轻人慢,我在这个年纪有的经验比他们都多,而且,我这个年纪还在写代码,说明我真的喜欢这个事,像我这样的人能够长时间坚持做一个职业的人这个世界已经不多了,你们应该珍惜……
  3. 找到自己的定位。我们需要做好职业规划、财务和心理方面的准备。40岁的程序员,所能竞争的一定是自己的认识和经验,所以,40岁以后如果你还是很喜欢这一行业,你的社会阅历和经历以及对这个社会的理解,可以让你做一些有创新的事,除此之外,你还可以做一个教练、老师、咨询、专家……,用你的经验和能力帮助下一代和一些中小型的公司,这不但是他们的刚需,同时也会让重新焕发的。

第二部分,技术

首先,在这部分,主要是了解一些技术,这部分的技术可以给于程序员们一些指导。

最流行的语言 最热门的语言

我们可以看到,

  • Javascript/HTML/CSS是很多人都会用到的,后面的是SQL,这个也没什么问题,无论前后端的人,或多或少都会要用到的,这些技术感觉已经成为了基础必会的技术了,就像数中的加减乘除一样。
  • Python/Java/Shell 是后端开发主流语言的前三强,Python在今年超过了Java。这里让我比较好奇的是居然还有很多人用Shell,这估计跟运维有关,所以,Python的热可能也是通过运维和大数据相关。
  • 流行语言后,第二梯队的是 C# / PHP / C++ / TypeScript / C ,接下来的是: Ruby / Go / Swift / Kotlin /WebAssembly / Rust… 。但在最被程序员喜欢的编程语言中:Rust / Python / TypeScript / Koltin / WebAssembly / Swift / Go… 都是排在前几名的。程序语言每隔一段时间就会整出一些新的语言来,我们一定要明白新出来的东西主要是为了解决什么样的问题,不然很容易迷失。
  • 在后面还有一个编程语言的薪资图,我们可以看到,在上面被提过的这些个编程语言中,Go语言的薪资是最高的(这可能是因为Go语言写关键的系统级的中件间——因为Go语言正在成为云计算的第一编程语言),然后是Scala、Ruby、WebAssembly、Rust、Erlang、Shell、Python、Typescript……

通过这些个信息,我们可以看出主流技术、有潜力的技术,传统过气技术,以及相关薪资,对我们在选择编程语言上有一定的启示。

在后面,我们可以看到:

  • 在 Web 开发框架上,主流使用还是 jQuery, React.js,Angular.js 为最前面的三个前端开发框架。而被程序员所喜欢的则是 React.js,Vue.js,Express, Spring,程序员非常不喜欢 Drupal,jQuery,Ruby on Rails 和Angular.js……
  • 在其它开发框架/库/工具上,主流是Node.js、.NET、Pandas、Unity 3D、Tensorflow、Ansible、Cordova、Xamarin……而程序员比较喜欢的是.NET、Torch/PyTorch、Flutter、Pandas、Tensorflow、Node.js …
  • 在操作系统上,主流使用Linux、Windows、Docker、Android、AWS……,而程序员最喜欢的是Linux、Docker、Kubernetes、Raspberry Pi、AWS、MacOS、iOS……
  • 在数据库上,MySQL、PostgreSQL、MSSQL、SQLite、MongoDB、Redis、Elasticsearch是比较主流的,而程序员非常喜欢的是,Redis、PostgreSQL、Elasticsearch、Firebase、MongoDB……,程序员比较讨厌的是 Couchbase、Oracle、Cassandra、MySQL。

从这些个图表中,我们可以看到主流和有潜力的技术是什么,我们可以看到 Windows 的技术并没有过时,感觉似乎都有可能会卷土重来,但是,开源的技术来势凶凶,正在吞食整个软件业,不容小觑,Docker/Kubernetes无论是在主流应用上还是被程序员的喜好上都是非常猛的,而云平台的AWS开始成为标准平台技术……

接下来的开发工具中,我们可以看到:

  • Visual Studio Code 成为了最流行的开发工具。让我没有想到的是跟在后面的是 Notepad++(好久没用这个工具了,我得找回来用用了),而IntelliJ、Vim、Sublime Text排以后面。 Eclipse 和 Atom 动力不足,Emacs 开始变得小众了。
  • 程序员主要的开发平台还是Windows占了近1/2, MacOS和Linux随后,各占1/4。
  • 有38%的人使用容器技术做开发,30%的人使用容器做测试,在生产线上使用容器的有26%

看样子编程开发工具还是Visual Studio 和 IntelliJ的天下,MacOS/Linux正在抢Windows的开发市场

接下来,StackOverflow给了一个技术圈的图

从上面这个图中,我们可以看以技术的几圈子:

  • Microsoft圈 – Windows、.NET、ASP.NET、C#、Azure、SQL Server
  • Java圈 – Java、Spring
  • 手机圈 – Android、 iOS、Kotlin、Swift、Firebase
  • 前端圈 – Javascript、React.js、Angular.js、PHP
  • 大数据圈 – Python、TensorFlow、Torch/PyTorch
  • 基础平台圈 – Linux、Shell、Vim、Docker、Kubernetes、Elasticsearch、Redis……
  • 其它圈子 – C/C++/汇编圈子、Ruby圈子、Hadoop/Spark圈子、……

看到谁的圈子大了吧,圈子大的并不代表技术实力强或是有前途,不过可以代表在那个圈子相关的关联技术,一方面,可以给你一些相关的参考,另一方面,整体可以让你看到全部的目前比较主流的技术。

第三部份 工作

在第三部份工作中,我们可以看到如下的一些数据:

  • 有3/4的程序员是全职的,10%左右的程序员是自由职业,6%左右的程序员是失业的,这个比例在北美、印度和欧洲都差不多。
  • 有1/3的人在过去一年内换过工作,1/4的人在过去1-2年间换过工作,1/3的人在2-4年换过工作。
  • 程序员找工作时,影响程序员的几个主要因素是:技术(编程语言、框架和使用的技术)、办公环境和公司文化、灵活的时间和安排、更专业的机会、远程工作……
  • 影响程序员工作的几大因素是:有干扰的工作环境、开会、要干一些和开发无关的事、人手不够、管理不够、工具不够、通勤时间……
  • 对于工程质量,有近70%的人有Code Review,而30%的则没有;有60%多的人有Unit Test,而不到40%的没有……

从工作中我们可以看到,程序员还是比较关心技术和公司文化的,换工作也是这个职业很正常的特性,他们并不喜欢被打扰,希望有足够的时间,而对于工程质量还是很有追求的。

最后用一张程序员的“每周工作时间” 来结束本文!

祝大家快乐!

(全文完)


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

(内容来自coolshell.cn)


立即购买:淘宝网]]>
IT技术精华 2019-10-19 00:10:14
14779 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如果不够得杂,其实是可以被暴力破解的,而且,整个过程是非常容易受到中间人攻击——比如一个中间人告诉客户端需要的 Basic 的认证方式 或是 老旧签名认证方式(RFC2069)。

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.cn)


立即购买:淘宝网]]>
IT技术精华 2019-10-19 00:10:12
14778 如何超过大多数人 当你看到这篇文章的标题,你一定对这篇文章产生了巨大的兴趣,因为你的潜意识在告诉你,这是一本人生的“武林秘籍”,而且还是左耳朵写的,一定有干货满满,只要读完,一定可以练就神功并找到超过大多数人的快车道和捷径……然而…… 当你看到我这样开篇时,你一定会觉得我马上就要有个转折,告诉你这是不可能的,一切都需要付出和努力……然而,你错了,这篇文章还真就是一篇“秘籍”,只要你把这些“秘籍”用起来,你就一定可以超过大多数人。而且,这篇文章只有我这个“人生导师”可以写得好。毕竟,我的生命过到了十六进制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.cn)


立即购买:淘宝网]]>
IT技术精华 2019-10-19 00:10:11
14777 50年前的登月程序和程序员有多硬核 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。其中的55 和 90 是各自的build 版本号。

我们细看一下,这些文件的日期是,1969年7月14日,而Apollo 11登月的日期是1969年7月16日起程,7月19日经过月球背面,7月20日下午8点登月。代码写好,两天后就直接上生产,然后就登月,还是导航代码,这代码写的的健壮性得有多强。

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

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

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

当时的AGC有32公斤,主频只有2MHz,2K的RAM,36K的ROM。嗯,当年就是这么一个小玩意,把人送上了月球,今天,一个聊天程序就占内存几GB……

下面是AGC在Apollo 1指挥舱里的样子(图片截自上面的视频),这个高质量的3D扫描来自 Simithsonian 3D: Apollo 11 Command Module (我觉得美国人干这些事干就是很漂亮啊,这种高清的3D扫描太牛了,如果你仔细看,这个舱里还有宇航员在舱壁上的手写)

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

 

如果你想了解AGC更多的细节,你可以看看 这篇 AGC for Dummies。这篇文章讲述了AGC这个嵌入式系统的背景和操作指令。一份详细的AGC 汇编语言手册可以让你了解更多的细节。

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

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

不过,最牛的还不是这个,我在Hamilton的Wikipedia词条上找到了他说的一个事件—— 当年Apollo登陆雷达开关放在了错误的位置,导致AGC收到了不少错误的信号。结果就是AGC既得执行着陆必须的计算,又要接受这些占用其15%时间的额外数据。但是AGC的程序居然可以用高优先级的任务打断低优先级的任务,于是,AGC自动剔除了低级别的任务以保证了重要的任务完成。Hamilton 原话说—— 如果当时的程序不能识别错误并从错误中恢复,我怀疑阿波罗不能成功登月。if 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。

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

Hamilton 的牛逼之外还在于,她是第一个将“软件工程”提出来的人,在MIT,她想让软件开发就像其它工程一样,有相应的工程纪律,给于相关的尊重,于是她创造了Software Engineering这个词。2018年,IEEE在纪念软件工程50周年的时候,他们把 Hamilton 请过去讲了一个叫 What the Errors Tell Us 的主题。她绝对可以称得上是程序员的Pioneer。

三年前,Apollo的源代码被开源时候,Twitter有个叫 Lin Clark 的人发了一条推:“我妈50年前的代码被放到Github上了”,虽然,她不是 Hamilton 的女儿,但她妈妈也是Apolload其中一个程序员,现在Lin Clark同样也是一个程序员,目前在 Mozilla工作,Staff Engineer,专长 WebAssembly, Rust, 和 JavaScript ,也是个非常厉害的程序,Youtube上各种演讲,也是一个跟他妈妈一样牛的人。

当她在Twitter上这么自豪地发了一条这样的推后,我不知道各位有什么想法?想不想你的后代在未来也会这样自豪的发条微博?

 

最后,尤其是想对那些到Apollo源代码的issue里发spam垃圾信息的人说一下,你看看人家,再看看你们自己,你们是不是想让你们的孩子在登月100周年纪念的时候说——50年前我爹那个傻叉在Apollo的github的issue列表时写了些垃圾,还以为自己多机灵?!

(全文完)


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

(内容来自coolshell.cn)


立即购买:淘宝网]]>
IT技术精华 2019-10-19 00:10:10
14776 HTTP的前世今生 HTTP (Hypertext transfer protocol) 翻译成中文是超文本传输协议,是互联网上重要的一个协议,由欧洲核子研究委员会CERN的英国工程师 Tim Berners-Lee v发明的,同时,他也是WWW的发明人,最初的主要是用于传递通过HTML封装过的数据。在1991年发布了HTTP 0.9版,在1996年发布1.0版,1997年是1.1版,1.1版也是到今天为止传输最广泛的版本(初始RFC 2068 在1997年发布, 然后在1999年被 RFC 2616 取代,再在2014年被 RFC 7230 /7231/7232/7233/7234/7235取代),2015年发布了2.0版,其极大的优化了HTTP/1.1的性能和安全性,而2018年发布的3.0版,继续优化HTTP/2,激进地使用UDP取代TCP协议,目前,HTTP/3 在2019年9月26日 被 Chrome,Firefox,和Cloudflare支持,所以我想写下这篇文章,简单地说一下HTTP的前世今生,让大家学到一些知识,并希望可以在推动一下HTTP标准协议的发展。

HTTP 0.9 / 1.0

0.9和1.0这两个版本,就是最传统的 request – response的模式了,HTTP 0.9版本的协议简单到极点,请求时,不支持请求头,只支持 GET 方法,没了。HTTP 1.0 扩展了0.9版,其中主要增加了几个变化:

  • 在请求中加入了HTTP版本号,如:GET /coolshell/index.html HTTP/1.0
  • HTTP 开始有 header了,不管是request还是response 都有header了。
  • 增加了HTTP Status Code 标识相关的状态码。
  • 还有 Content-Type 可以传输其它的文件了。

我们可以看到,HTTP 1.0 开始让这个协议变得很文明了,一种工程文明。因为:

  • 一个协议有没有版本管理,是一个工程化的象征。
  • header是协议可以说是把元数据和业务数据解耦,也可以说是控制逻辑和业务逻辑的分离。
  • Status Code 的出现可以上请求双方以及第三方的监控或管理程序有了统一的认识。最关键是还是控制错误和业务错误的分离。

(注:国内很多公司HTTP无论对错只返回200,这种把HTTP Status Code 全部抹掉完全是一种工程界的倒退)

但是,HTTP1.0性能上有一个很大的问题,那就是每请求一个资源都要新建一个TCP链接,而且是串行请求,所以,就算网络变快了,打开网页的速度也还是很慢。所以,HTTP 1.0 应该是一个必需要淘汰的协议了。

 HTTP/1.1

HTTP/1.1 主要解决了HTTP 1.0的网络性能的问题,以及增加了一些新的东西:

  • 可以设置 keepalive 来让HTTP重用TCP链接,重用TCP链接可以省了每次请求都要在广域网上进行的TCP的三次握手的巨大开销。这是所谓的“HTTP 长链接” 或是 “请求响应式的HTTP 持久链接”。英文叫 HTTP Persistent connection.
  • 然后支持pipeline网络传输,只要第一个请求发出去了,不必等其回来,就可以发第二个请求出去,可以减少整体的响应时间。(注:非幂等的POST 方法或是有依赖的请求是不能被pipeline化的)
  • 支持 Chunked Responses ,也就是说,在Response的时候,不必说明 Content-Length 这样,客户端就不能断连接,直到收到服务端的EOF标识。这种技术又叫 “服务端Push模型”,或是 “服务端Push式的HTTP 持久链接
  • 还增加了 cache control 机制。
  • 协议头注增加了 Language, Encoding, Type 等等头,让客户端可以跟服务器端进行更多的协商。
  • 还正式加入了一个很重要的头—— HOST这样的话,服务器就知道你要请求哪个网站了。因为可以有多个域名解析到同一个IP上,要区分用户是请求的哪个域名,就需要在HTTP的协议中加入域名的信息,而不是被DNS转换过的IP信息。
  • 正式加入了 OPTIONS 方法,其主要用于 CORS – Cross Origin Resource Sharing 应用。

HTTP/1.1应该分成两个时代,一个是2014年前,一个是2014年后,因为2014年HTTP/1.1有了一组RFC(7230 /7231/7232/7233/7234/7235),这组RFC又叫“HTTP/2 预览版”。其中影响HTTP发展的是两个大的需求:

  • 一个需要是加大了HTTP的安全性,这样就可以让HTTP应用得广泛,比如,使用TLS协议。
  • 另一个是让HTTP可以支持更多的应用,在HTTP/1.1 下,HTTP已经支持四种网络协议:
    • 传统的短链接。
    • 可重用TCP的的长链接模型。
    • 服务端push的模型。
    • WebSocket模型。

自从2005年以来,整个世界的应用API越来多,这些都造就了整个世界在推动HTTP的前进,我们可以看到,自2014的HTTP/1.1 以来,这个世界基本的应用协议的标准基本上都是向HTTP看齐了,也许2014年前,还有一些专用的RPC协议,但是2014年以后,HTTP协议的增强,让我们实在找不出什么理由不向标准靠拢,还要重新发明轮子了。

HTTP/2

虽然 HTTP/1.1 已经开始变成应用层通讯协议的一等公民了,但是还是有性能问题,虽然HTTP/1.1 可以重用TCP链接,但是请求还是一个一个串行发的,需要保证其顺序。然而,大量的网页请求中都是些资源类的东西,这些东西占了整个HTTP请求中最多的传输数据量。所以,理论上来说,如果能够并行这些请求,那就会增加更大的网络吞吐和性能。

另外,HTTP/1.1传输数据时,是以文本的方式,借助耗CPU的zip压缩的方式减少网络带宽,但是耗了前端和后端的CPU。这也是为什么很多RPC协议诟病HTTP的一个原因,就是数据传输的成本比较大。

其实,在2010年时,Google 就在搞一个实验型的协议,这个协议叫SPDY,这个协议成为了HTTP/2的基础(也可以说成HTTP/2就是SPDY的复刻)。HTTP/2基本上解决了之前的这些性能问题,其和HTTP/1.1最主要的不同是:

  • HTTP/2是一个二进制协议,增加了数据传输的效率。
  • HTTP/2是可以在一个TCP链接中并发请求多个HTTP请求,移除了HTTP/1.1中的串行请求。
  • HTTP/2会压缩头,如果你同时发出多个请求,他们的头是一样的或是相似的,那么,协议会帮你消除重复的部分。这就是所谓的HPACK算法(参看RFC 7541 附录A)
  • HTTP/2允许服务端在客户端放cache,又叫服务端push,也就是说,你没有请求的东西,我服务端可以先送给你放在你的本地缓存中。比如,你请求X,我服务端知道X依赖于Y,虽然你没有的请求Y,但我把把Y跟着X的请求一起返回客户端。

对于这些性能上的改善,在Medium上有篇文章你可看一下相关的细节说明和测试“HTTP/2: the difference between HTTP/1.1, benefits and how to use it

当然,还需要注意到的是HTTP/2的协议复杂度比之前所有的HTTP协议的复杂度都上升了许多许多,其内部还有很多看不见的东西,比如其需要维护一个“优先级树”来用于来做一些资源和请求的调度和控制。如此复杂的协议,自然会产生一些不同的声音,或是降低协议的可维护和可扩展性。所以也有一些争议。尽管如此,HTTP/2还是很快地被世界所采用。

HTTP/2 是2015年推出的,其发布后,Google 宣布移除对SPDY的支持,拥抱标准的 HTTP/2。过了一年后,就有8.7%的网站开启了HTTP/2,根据 这份报告 ,截止至本文发布时(2019年10月1日 ), 在全世界范围内已经有41%的网站开启了HTTP/2。

HTTP/2的官方组织在 Github 上维护了一份各种语言对HTTP/2的实现列表,大家可以去看看。

我们可以看到,HTTP/2 在性能上对HTTP有质的提高,所以,HTTP/2 被采用的也很快,所以,如果你在你的公司内负责架构的话,HTTP/2是你一个非常重要的需要推动的一个事,除了因为性能上的问题,推荐标准也是架构师的主要职责,因为,你企业内部的架构越标准,你可以使用到开源软件,或是开发方式就会越有效率,跟随着工业界的标准的发展,你的企业会非常自然的享受到标准所带来的红利。

HTTP/3

然而,这个世界没有完美的解决方案,HTTP/2也不例外,其主要的问题是:若干个HTTP的请求在复用一个TCP的连接,底层的TCP协议是不知道上层有多少个HTTP的请求的,所以,一旦发生丢包,造成的问题就是所有的HTTP请求都必需等待这个丢了的包被重传回来,哪怕丢的那个包不是我这个HTTP请求的。因为TCP底层是没有这个知识了。

这个问题又叫Head-of-Line Blocking问题,这也是一个比较经典的流量调度的问题。这个问题最早主要的发生的交换机上。下图来自Wikipedia。

图中,左边的是输入队列,其中的1,2,3,4表示四个队列,四个队列中的1,2,3,4要去的右边的output的端口号。此时,第一个队列和第三个队列都要写右边的第四个端口,然后,一个时刻只能处理一个包,所以,一个队列只能在那等另一个队列写完后。然后,其此时的3号或1号端口是空闲的,而队列中的要去1和3号端号的数据,被第四号端口给block住了。这就是所谓的HOL blocking问题。

HTTP/1.1中的pipeline中如果有一个请求block了,那么队列后请求也统统被block住了;HTTP/2 多请求复用一个TCP连接,一理发生丢包,就会block住所有的HTTP请求。这样的问题很讨厌。好像基本无解了。

是的TCP是无解了,但是UDP是有解的 !于是HTTP/3破天荒地把HTTP地底层的TCP协议改成了UDP!

然后又是Google 家的协议进入了标准 – QUIC (Quick UDP Internet Connections)。接下来是QUIC协议的几个重要的特性,为了讲清楚这些特性,我需要带着问题来讲(注:下面的网络知识,如果你看不懂的话,你需要学习一下《TCP/IP详解》一书(在我写blog的这15年里,这本书推荐了无数次了),或是看一下本站的《TCP的那些事》。):

  • 首先是上面的Head-of-Line blocking问题,在UDP的世界中,这个就没了。这个应该比较好理解,因为UDP不管顺序,不管丢包(当然,QUIC的一个任务是要像TCP的一个稳定,所以QUIC有自己的丢包重传的机制)
  • TCP是一个无私的协议,也就是说,如果网络上出现拥塞,大家都会丢包,于是大家都会进入拥塞控制的算法中,这个算法会让所有人都“冷静”下来,然后进入一个“慢启动”的过程,包括在TCP连接建立时,这个慢启动也在,所以导致TCP有一些时候性能也不行。QUIC才不管这个,是个相对比较激进的协议。QUIC有一套自己的丢包重传和拥塞控制的协,一开始QUIC是重新实现一TCP 的 CUBIC算法,但是随着BBR算法的成熟(BBR也在借鉴CUBIC算法的数学模型),QUIC也可以使用BBR算法。这里,多扯几名,从模型来说,以前的TCP的拥塞控制算法玩的是数学模型,而新型的TCP拥塞控制算法是以BBR为代表的测量模型,理论上来说,后者会更好,但QUIC的团队在一开始觉得BBR不如CUBIC的算法好,所以没有用。现在的BBR 2.x借鉴了CUBIC数学模型让拥塞控制更公平。这里有文章大家可以一读“TCP BBR : Magic dust for network performance.
  • 接下来,现在要建立一个HTTP的连接,先是TCP的三次握手,然后是TLS的三次握手,要整出六次网络交互,一个链接才建好,虽说HTTP/1.1和HTTP/2的连接复用解决这个问题,但是基于UDP后,UDP也得要实现这个事。于是QUIC直接把TCP的和TLS的合并成了三次握手。

 

所以,QUIC是一个在UDP之上的伪TCP +TLS +HTTP/2的多路复用的协议。

但是对于UDP还是有一些挑战的,这个挑战主要来自互联网上的各种网络设备,这些设备根本不知道是什么QUIC,他们看QUIC就只能看到的就是UDP,所以,在一些情况下,UDP就是有问题的,

  • 比如在NAT的环境下,如果是TCP的话,NAT路由或是代理服务器,可以通过记录TCP的四元组(源地址、源端口,目标地址,目标端口)来做连接映射的,然而,在UDP的情况下不行了。于是,QUIC引入了个叫connection id的不透明的ID来标识一个链接,用这种业务ID很爽的一个事是,如果你从你的3G/4G的网络切到WiFi网络(或是反过来),你的链接不会断,因为我们用的是connection id,而不是四元组。
  • 然而就算引用了connection id,也还是会有问题 ,比如一些不够“聪明”的等价路由交换机,这些交换机会通过四元组来做hash把你的请求的IP转到后端的实际的服务器上,然而,他们不懂connection id,只懂四元组,这么导致属于同一个connection id但是四元组不同的网络包就转到了不同的服务器上,这就是导致数据不能传到同一台服务器上,数据不完整,链接只能断了。所以,你需要更聪明的算法(可以参看 Facebook 的 Katran 开源项目 )

好了,就算搞定上面的东西,还有一些业务层的事没解,这个事就是 HTTP/2的头压缩算法 HPACK,HPACK需要维护一个动态的字典表来分析请求的头中哪些是重复的,HPACK的这个数据结构需要在encoder和decoder端同步这个东西。在TCP上,这种同步是透明的,然而在UDP上这个事不好干了。所以,这个事也必需要重新设计了,基于QUIC的QPACK就出来了,利用两个附加的QUIC steam,一个用来发送这个字典表的更新给对方,另一个用来ack对方发过来的update。

目前看下来,HTTP/3目前看上去没有太多的协议业务逻辑上的东西,更多是HTTP/2 + QUIC协议。但,HTTP/3 因为动到了底层协议,所以,在普及方面上可能会比 HTTP/2要慢的多的多。但是,可以看到QUIC协议的强大,细思及恐,QUIC这个协议真对TCP是个威胁,如果QUIC成熟了,TCP是不是会有可能成为历史呢?

未来十年,让我们看看UDP是否能够逆袭TCP……

(全文完)


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

(内容来自coolshell.cn)


立即购买:淘宝网]]>
IT技术精华 2019-10-19 00:10:07