What a May Day

周四晚上跟生日的父母吃过晚饭后,傻乎乎的带着小梦梦在孩子王逛,一点没有意识到已经是五月的最后一天。有时候,记录的习惯更多的就是提醒自己,时间的昼夜不舍。

这个月有意识的接触了挺多人,对于鄙人这种社交贫瘠的人来说,这个月花在这方面的时间算是奢侈的了。有老友也有新朋友。一次跟新朋友印象深刻的夜谈。已经很久没有跟新认识的人如此没有负担的沟通和交流了。对了,上一次有意思的聊天也是去年的这个时候。初夏,真是一个神奇的时节,一切都开始要变得明亮而耀眼。

五月在做和要做的事情越来越多,一种此情无计可消除,才下眉头,却上心头之感。一部分算是甜蜜的负担,而立之年,一些事情逐渐跟自己是否准备好已经没有必然关系,而是直接去解决它就对了。一部分是多过去时光的辜负,亡羊补牢,希望犹未晚矣。前天看韩老师的5X兴趣社区,看到李伟龙一个视频的幕后花絮,对话挺走心的:时间只会让你老去,其他什么都不会带来;只有你想改变的时候,你才能改变。

神奇的五月,居然达成了跑渣的第一次5公里(一个都不好意思提的配速)。从去年4月份参加跑团开始跑步,到现在已经一年多了,跑步成绩上没有任何提升,也是我预料之中的。对于这件事,我其实想得很明白:我一点都不喜欢运动,但是要支撑我的情怀和要做的事情,我必须要有这个练习和准备。显然,如果保持当前的做事的节奏,也许一周一次的跑步很快就无法支撑自己在做的事情,但是只要保持这件事情的惯性,我相信这股力量不会让自己失望。

这个月最喜欢的书是吴军老师的《智能时代》。因为一直在订阅吴军老师在得到的专栏,因此书中的很多内容其实都在专栏中听过了(如此说来,维护专栏及时高产如母猪,也是需要有存货当备份的🤣)。有两点体会最深:

  1. 人类文明发展是一个不短加速的过程,每一次加速都会让已有产业与新结束结合形成形成新的产业,赶上这个浪潮的会以数量级的优势领先,赶不上或者不愿拥抱变革的则会被无情的淘汰。
  2. 大数据和AI是当前最有可能成为下一个时代的蒸汽机和电。超越时代是困难的,但是从思维方式上则是可以刻意练习大数据和AI思维的。对于程序员而言,这尤为重要——有很大可能性,这决定了当前的你是成为为工业时代的码农,还是智能时代的工程师。

六月会迎来自己在两个月前设定的一个deadline, 从目前看来,不容乐观。可能当时在设定这个目标的时候,其实内心的真实独白就已经是法乎其中则得其下,法乎其上则得其中。但是,总的来说,过去的两个月无论是还在发生还是已经发生的事,多少带来了一丝丝改变。

期待六月,不负好时光。

热眼旁观:老罗与锤子

第一次对老罗的全面了解是本科时候的那趟南戴河毕业旅行:出发的时候随便抓了一本书塞书包里——《我的奋斗》。一群毕业屌丝自然坐的是最便宜,但如今想来却最有意思的绿皮火车去的南戴河。在路上和车站上看完了这本书。

与其说这是一本书,不如说这是老罗在新东方几年上课和演讲的语录。即使按照当时的认知,也不会把它归为推荐一类的书。但这并不重要,重要的是,对这个彪悍且还在继续活跃的胖子有了一个相对完整的了解。如今依然不知道那本书是从哪里来,后来遗失到了哪里去。很多事情,奇妙的偶然性让我知道了这个世界上有个有趣并值得持续关注的灵魂。

一共参加过两次老罗的现场演讲。

第一次是研究生一年级,老罗来学校做一个理想主义者的创业故事II的演讲。那时候老罗还没有开始做手机,演讲中用来插科打诨的公司主体是他的英语培训机构。那时候的自己对商业也没有清晰认识和完整理解。只是自己当时也在参与创业,对「理想主义」和「创业者」这两个词是没有任何抵抗力的。仿佛找到了一起参与一场改变世界革命的战友,惺惺相惜,感同身受。当然,也可见当时万众创业对在校学生荼毒之深😂。

那次分享的很多故事依然记忆犹新,但是要说最深刻的应该是:学会站着赚钱。理所当然的道理遇到浮躁的年代,往往知难行易。感谢老罗的那次演讲,深刻地影响了自己后来创业、工作时候处理棘手事情的底限和原则。

第二次是锤子科技在成都举行的 2017 秋季新品发布会。刚得知消息的时候,还是非常意外的,因为的确没有想到在从帝都回到成都工作的第四个年头,能够在成都听一场老罗的现场相声。

也许近两年成都的热度太高了吧,偌大的成都大魔方演艺厅票务放出即售罄。最后还是因为前东家是锤子科技的股东,因此老同事给置办了一张内部票才得以入场,也算是听相声路上的一段小插曲。那次的发布会发布了两个产品,一个是坚果 Pro 2——目的非常明确,坚果 Pro 是锤子迄今为止最畅销的手机,Pro 2 意图百尺竿头,更进一步; 一个是与成都雾霾非常应景的空气净化器——畅呼吸,算是在产品品类上横向扩充的尝试。

两个产品都很棒,但是即使凭借门票有优惠和优先购买权、手上的 iPhone 6 Plus 廉颇老矣的情况下,自己也没有购买这次发布会上的任何一款产品。很多人说,锤子的产品历来都是叫好不叫座,也许是有原因的😅

回到几天前的鸟巢发布会。有人说坚果 R1 中规中矩,毫无亮点;与之结合的 TNT 工作站就是一个笑话。我不想参与这个结论的辩论,一来自己还没有体验过这两个产品,讨论结论为时过早,毫无意义;二来我想我可能是一群人的典型代表:关注老罗和锤子,只因为这是一家老罗牵头的公司。

R1 是创新吗?当然不是。在手机如此同质化的今天,无论是小厂锤子科技还是巨厂苹果,这几年出的产品整体上都达无法满足大家的心理预期。R1 不过是众多改进型产品中的一员。至于老罗说的全世界第一,哎哟喂,都是成年人了,大家平时都在吹牛X,你不能自己吹完不让别人吹不是?

TNT 是创新吗?当然是。我会购买吗?现在当然不会。

Touch and Talk 的交互方式会是次时代的主流人机沟通方式吗?我不确定,这里没有和稀泥的意思。这个问题可以反过来看:如今嘴里含着触屏手机出生的一代,他们对于 touch 的交互几乎是与生俱来的一种操作直觉,如今的键鼠操作一定不是这一代人所有场景下的最终交互归宿,在很多场景下,一定会有新的交互方式来代替当前的这种操作交互。那么它会是 Touch and Talk 吗?这个真的不知道。超越时代是困难的,而在超越时代的产品真正出现的时候,在大众眼中,这种超越往往又是荒诞的。

我知道很多程序员都会调侃说 TNT 写代码可能会是这样的:

关于这一点,我想说,也许应用在现实的生产工具场景中,也许它……真的就是这样的。但是,这其实杠精了。回想一下老罗介绍的几个 TNT 应用场景:表格数据自动处理、keynote 语音辅助编辑、闪念胶囊自动找图和幻灯片生成…… 没有一个是要取代如今的密集生产工具场景。虽然现场 demo 状况频出,老罗也是汗如雨下,但是,我想你不会否认这么几点:老罗的敏感使其非常善于发现问题,他提到的问题和场景你一定不会陌生,他给出的解决方案也许不是你会选择的方法,但是一定是解决了特定用户群的痛点。

有人说,TNT 工作站这种手机 + Dock 的方式根本不是老罗首创,因为好几年前的摩托罗拉和当前的三星都有对应的产品。单从产品形态来说,TNT 的确没有重新发明轮子。甚至加上 Touch 和语音,整个解决方案也必然不是业界首创。

在这一点上,我比较同意吴军老师的第三眼美女以及发明是以最后一个发明家的名字命名的理论。老罗和他的 TNT 会是这个垂直品类的最后一个发明者以及第三眼美女吗?说实话,我并不确定,但是我愿意将一半的可能性投给老罗。

因为,对于老罗,我想我可能是关注老罗若干群体中的某个群体的典型代表:每次老罗的相声都不会错过,但是却几乎未曾真正触动自己的钱包;表面是老罗的粉丝,但是却自诩为独立思考,不是任何人的拥趸;关注老罗,更多的是他所言所行符合了某一群理想主义者的价值观,视其位精神的知己,行动的马前卒;将老罗及其挂帅的锤子科技看做浮躁创业环境下的一股清流,并由衷乐见其成;经常感怀自己,如果老罗失败了,是这个社会对价值的多元筛选和宽容失败了。

TNT 最终会怎样 boom 我们交给时间评判。但是,老罗一直都是那个埋头做事的人,同时更是平地吹B三丈起的奇才。我依然会是一个旁观者,如同观看一场带有完全主观倾向的比赛,向他投与这场比赛所有的希冀和祝福。

:吴军老师第三眼美女理论

第三眼美女当然是相对第一眼美女和第二眼的美女。第一眼美女有什么特点呢?首先,一眼看上去就很漂亮,但是不属于大众。这里面有很多原因,或许是因为她们本身就认为自己是精英人群而非主流人群,抑或是这些人光芒太扎眼,一般人想接近也接近不了她们。总之结果是,大众只能在远距离去欣赏她们了。其次,人有时会看走眼,乍一看很漂亮,接近以后如果发现没有内涵,看到第二眼、第三眼时,未必还能有最初的好印象了。

第二眼美女未必有第一眼的那么天生丽质,因此她们常常需要更懂得时尚细节才能引来周围人欣赏的眼光,但是这样一来,和一个第二眼美女交往的成本就比较高,大众即使心里痒痒的,未必能得到。即使得到了,第二眼美女的脾气未必好,因此双方的蜜月期一过,可能也就形同陌路了。

第三眼美女是属于大众的,她们未必那么显眼,但是如果仔细观察,她们还是不错的。更重要的是,正是因为她们可能没有光鲜的外表,如果依然能够吸引人,那么必定有某种美德或者价值。而对于欣赏这种美德,或者看重这种价值的人来讲,他们对第三眼美女的喜欢会持续很久,除非这种美德和价值不再存在或者过时了。

樱桃红了

北上念书,对帝都的古迹早已没有了兴趣,对其自然风光更是嗤之以鼻,哪怕是一年中最好的时节也是扬尘满天,鸡毛柳絮,实在毫无踏青的兴致。

在帝都的七年里,跟大多数不谙世事、人生懵懂的大学生一样,耗着最好的年华,度着乏善可陈的无聊。只是,一直放不下的是南国的春天,尤其是院子外的那两颗樱桃树。

求学的那几年,平日的活动范围很难超出学校方圆十里。即使是难得的春光,也是经常是躲在实验室磨着看似忙碌的无聊。心里唯一念念不忘的是这个时节的一味水果——樱桃。

那时也还算没有到囊中羞涩到买不起水果。但是却一直没有在帝都寻觅到真正的樱桃。路边倒是有很多小贩叫卖着一种叫车厘子的大樱桃。虽然价格似乎比樱桃贵上不少,但是在鄙人心中,那是一味除了能看,绝非能食的水果外,别无其他什么特点。况且,车厘子其实是可以从智利通过轮渡远洋运输的,并不算什么当季难得的水果。

帝都七年,没有尝过一颗南国的樱桃。

学未有所成,惭愧归来。生活所迫逐渐多过了理所当然的无知与轻率。喜欢周末网老家跑,春天更是如此。搬出一个方凳,对这院子外的樱桃树坐着,晒着太阳发呆。

按我们当地种地人的标准,我们家应该都是不合格的农人。因此,院子里的樱桃树并非亲自所植。而是在自己约摸快上小学的时候,爷爷的兄弟,我的幺叔公为自己移栽的。印象中,幺叔公比爷爷要高大,但是身体没有爷爷硬朗,平日里有点轻微的喘。而在栽下这棵树以前,每到春天,我心心念念的就是幺叔公家后院的那颗樱桃树。那颗树非常高,基本覆盖了那一块空地。由于位置隐蔽,又背靠一条灌水渠,那棵树几乎从来没有让自己失望过。这是印象中最好吃的樱桃的锚点:颗颗肥硕饱满,色泽红润,深邃而通透,入口前味清甜,中后味醇厚悠长。别说从树上掉下来,就是摘的时候忘记了轻重也可能把樱桃捏坏了。

院子前的樱桃树栽好的时候,幺叔公跟我说,过两年就可以吃樱桃啦。鄙人高兴得不知所错。过了一会,问道,一定要过两年吗?两年好长呀,一年可以吗?幺叔公只是一直笑,没有回答我。我并没有放弃寻找这个问题的答案。加之幺叔公的制作竹子相关农具的手艺很好,我更是每天一边观赏其干活,一边重复问这个答案。孩提时代的两年也许真的不短,但是,一辈子有真的长吗?

那个时候,幺叔公跟我说得最多的一句话是,如果他年老吃不动了,生活也不能自理了,来到我家的时候,我还会善待他吗?我小手一挥,毫不犹豫的说,那还用说,我加门前的两棵樱桃树就是左右护法。

北上以后,没有了这最好的一味水果相伴,记忆却更加深刻。

一日,跟妈通电话。老妈非常遗憾的说起幺叔公进来身体每况愈下。唯一安心一点的是,在这件事上,爸妈真的是尽心尽力,医院、手续以及农村固有的陋习带来的阻力都是他们对待至亲一般的落实、解决。那年夏天,暑假回来。每次去爷爷奶奶家,都会顺带路过幺叔公家。北上前,跟父母要了一点零花钱,仗着并不熟悉驾驶经验,驱车去县里买了一些伴手礼,聊表心意。那也是最后一次见到幺叔公,虽然卧病在床,但是精神很好,思维也非常清晰。依然忘不了用他年老我是否还能如此孝顺来洗刷我,我结婚的时候会不会邀请他最八仙桌的上桌……那年的我只是笑,虽然那时已经有了若,但是却不知道该不该告诉他。

岁月无声有痕。门前的两棵殷桃树如今也亭亭如盖,幺叔公坟头周围也长满了茱萸草。挂果时节,我依然喜欢爬上树去吃。坦率讲,我们家栽树就没有好手。所以,这两棵树的樱桃在鄙人如此耐受酸味的前提下也是很酸的。只是,偶尔能觅到一颗不是那么酸的果子的时候,让人一下又找回了幺叔公后院那棵树上的感觉。

北上求学那几年,无法亲自采摘。有那么一两回,是老爸把若接回老家提我完成这件事。虽然我知道若其实并不知道我的心结,她更多是帮我扮演吃货的角色,但是,好歹她总能拍出几张应景入心的照片发与我看。这是当初的若,也是如今的若,俏皮、善良、漂亮。想起幺叔公当年的调侃,看看若,仿佛这还算是件骄傲的事呢。

回成都以后,若的好友经常邀去简阳樱桃沟采摘。对于这件事,我是毫无抵抗力的。这几年来,几乎从未缺席。他们都笑我每次去只知道吃樱桃,我也不装得像个巨婴一样,因为,对于我来说,这一味水果是真的稀罕。

又是一年,樱桃红了。依然稀罕,粒粒珍惜。

阳春三月在成都

这是一篇月末总结,如果你是不小心被标题吸引误入,那么我更加建议你把这篇月志读完。既来之,则安之😁

三月工作上发生了挺大变化,很多老朋友离开了,很多新同学加入了。无论是离开还是加入的人,里面都有自己喜欢和欣赏的人。成都的圈子只有这么大,鄙人都祝福他们。

自己又回到3年前的类似位置,做着类似的事情,此乃物是。三年前犯过的错误、苦恼纠结的事情,无一缺席,但是已经没有了当初面对类似事情的不确定性,此乃人非。

这个月重新找到了写字的愉悦感。在某些平台居然还被特别推荐过,我跟妞儿说这是一件funny的事情,没有开心的成分。算是把延期了好几年的课程补上了一小步。

在这个过程中,鄙人也开始逐渐体会到了自己订阅的公众号作者写作的内心感受。写作的目的可能有很多种,但是对于某一类人来说,写作就是一种习惯性的分配特定时间进行思考。这种思考可能在写作开始之前已经在潜意识中发生。写作就是讲这些思考的尘埃落定和梳理的过程。另外,根据我的观察,一般擅长写作的人,大多都是逻辑清晰且沟通起来很愉快的人。

三月挺幸运,包括今天。接触到了很多交流起来很愉快的人。鄙人跟妞儿说,遇到聪明的人的时候,有一种发自内心的欣赏和热爱。这种热爱跟基情没有关系,对于没有那么熟络的人,珍惜交流的效率和时间;对于熟悉的人,会聊很多平时不会聊的话题和看法。要知道,其实这个世界上,能够一起沟通和讨论一些深度想法的人太少太少了,遇到一个,怎能不喜?有那么一群人,注定就是寂寞。

马上而立之年了,责任随不至于越来越重,但是却只越来越真实。这个月似乎也硬着头皮处理了很多家庭上的事情。也许有时候过于追求效率,以及当面揭穿一些人的套路伎俩吧,给人的感觉是脾气太冲,经常惹恼很多人,包括家人。在这个点上,我从来不抱怨什么路长且累呀,什么不理解呀。我挺同意韩路的观点:如果你对目标足够清晰和强壮,那么做一些事的时候必然会有取舍和付出。想通了这件事,很多事情就是理所当然的,哪里还有什么抱怨。我花了挺长时间想清楚这个问题,也会有心情郁闷的时候,也会有到家以后,熄火在车里闷两分钟的时候,但是只要踏入家门,我必然是女儿的好爸爸,妞儿的好丈夫,父母的好儿子,而这一切,内心是举重若轻,毫无纠结的。

三月:

  • 最喜欢的物品:飞行棋手机架
  • 最喜欢的书:《原则》
  • 最惺惺相惜的人:至今不知道他的名字,但是却有过一次非常深入、愉快的交流
  • 财务目标一个都没有达到,且存在浮亏的款项
  • 独立项目新开了一个项目,看能走多远吧

三月开始,南方逐渐进入一年中最好的日子。在期待四月中,2018已经过去25%.

论一个合格的车载手机支架的自我修养之什么是最好的车载手机支架

作为一个酷爱驾驶的人和一名手机党,给座驾安装一个手机支架是非常重要的事情。然而,要寻找一个还用的车载手机支架真的是一件比想象要困难的多的事情。

手机支架自己买过很多了。但是都在使用一段时间后(甚至体验几分钟后)发现不能接受的设计问题而丢垃圾桶。

大概5年前,购买了第一个手机支架,他大概长这样(姑且称之为第一代车载手机支架吧):

这个手机架使用上没有什么太大问题,功能上一直表现不错,因此使用了挺长一段时间。但是,后来换的手机屏幕更大了,继续使用这个手机支架的话会使得手机上沿会挡住汽车自己的一部分显示屏。此外,这个手机支架下端只依靠一个塑料架作为之巅,把出风口的下方已经磕出了历史的痕迹😅

于是换了当时最流行的磁吸式车载手机支架:

这种支架需要在手机或者手机壳上安装一个贴片(也叫做引磁片),然后往空调出风口上的支架一放就可以靠磁力吸住。应该说,使用传统的支架换到这个磁吸支架的头两天,心里还是默默为发明这种之家的人点赞的。

它的支架结构非常简单,在车里非常简洁,而且调整引磁片的位置是的手机固定在一个自己最喜欢的位置。但是,这种简洁却是以牺牲手机的外观为代价的。且不说很多引磁片上面有品牌logo, 且logo极丑,关键是无论多么薄的引磁片贴在手机上以后都让以前光滑平整的手机背面不在平整,非常变扭。

另一方面,在使用一段时间以后,发现指南针和陀螺仪都不再准确,不过导航似乎一直都还正常。推测是这两个部件因为长期使用这种强磁设备影响已经被磁化。作为一个相信科学的人居然在这里翻车,想来也是挺自嘲的一件事情。

然而,事实是「磁吸式手机之间是否影响手机」这个事情目前都没有定论。我在购买前也担心过这个事情。专门在蛤乎还转了一圈,我估计大家找到的大概也是这个答案:

然是有影响的,表面磁力超过一定范围.现在市面上的手机基本上都用到了磁铁,其内部隔磁都做到了200GS以下,越好的手机内部隔磁越好.

所以,对于手机支架,表面磁力(接触手机的部分)超过200GS就会对手机产生影响,甚至损害.就现在市面上的磁性手机支架而言,都在2000GS以上,是标准值的10倍以上.

这样导致的结果就是干扰手机的电磁信号和磁化手机内部的含铁部件,逐渐导致收集导航的失准和失灵、通话音质的失真、运行卡顿、数据丢失、屏幕花屏或者黑屏、间歇性死机,直至报废。

以上的现象一般会在1到4个月内相继出现,根据手机的不同也会出现不同的情况。这个也有专业的测试机构,但鉴定需要最少一年的时间才能判别对手机的影响到底多大,只有专业的磁铁生产厂家和手机生产厂家能自己用仪器测出具体值。

建议购买磁性支架应选择盖上铁片(贴在手机背面的那片,没有不行)后表面磁力在200GS以下的。

给一个简单的测试方法:在手机支架上盖上铁在手机背面的铁片,把一圆硬币从上面滚落,不会吸住的基本上都只有几百GS,过大的都会吸住硬币,则不建议购买。

但是,请相信我,我也做过这个硬币实验,硬币滚落了,于是我以为自己购买的产品是OK的。但是现实却是打脸的……

我看过很多所谓汽车媒体的评测,结论这种磁铁不影响手机。理由无非亮点:

  1. 厂家使用了多颗磁铁,形成的是闭合磁场,因此厂家肯定是做了充分测试的。
  2. 苹果的smart cover外设也是使用磁铁作为屏幕感应开关的,因此磁铁是不会影响电子设备的。

最开始,我还挺相信上面的结论的。但是,后来一想两个理由都有点滑稽:

  1. 学过初中物理的都应该知道,这个所谓的闭合磁场是限制磁场在一个立体区域,引磁片才一毫米厚,闭合个屁呢。另外,生产这这些支架的厂商都是一些作坊而已,这里根本没有什么信用可以背书。
  2. 苹果的确在smart cover中使用了磁铁,但是你发现那个磁铁在iPad屏幕的最右侧边缘位置了吗?而你的引磁片是贴在近乎手机中心的位置……而长期跟磁场如此近距离接触,含铁的零件被磁化无非是快慢的事罢了

抛弃了磁吸式手机支架后,我有购买了最时尚的重力感应手机支架:

第一眼看到的时候,有没有觉得创意很赞呀?最开始我也是这样认为的。于是我一股脑买了两个。但是……拿到手后就不得不说,这什么鬼呀……

所谓成也重力,败也重力。因为使用重力收紧两边的手臂夹紧手机,因此三个臂都是可以活动的。由于,手机重量一般只有一百多克,因此三个臂基本没有阻尼。这就意味着,整个手机支架非常松垮。尤其是你没有在支架上放手机的时候,开起来的过坑洼路段的时候感觉整个车都要散架一样。

这个结构的手机支架跟品牌没有关系,设计原理导致它的噪音是无解的。淘宝上到处刷评的第一卫一样垃圾,一样有这个问题。

找呀找呀找支架,于是我找到了这个目前为止最满意的车载手机支架

有没有感觉又回到了最古老的那款手机支架的感觉?这就对了。那款支架我使用时间最长,而这款支架把我之前认为的设计缺陷都解决掉了:

  1. 从结构看,它把第一款手机支架的固定方式修改为与磁吸式的固定方式一致,加入了软性塑料的夹子及不会产生异响,同时也不会在支架下部支点位置处留下历史的痕迹。
  2. 将固定手机的方框结构修改为圆形无下部支脚的弹力感应结构,这就解决了老款支架手机上下不可调,导致遮挡车载屏幕的问题。

总结下来就是,它既有普通支架的通用可靠,同时又有磁吸式支架的简洁灵活,并且不需要在手机上安装引磁片,免去磁铁影响手机的担忧。

这款手机支架使用已经有段时间,等过段时间有一次长途体验以后在来盖棺定论。

100行代码实现基于 QUIC 的 http 代理

本站开启支持 QUIC 的方法与配置后,主观感觉从国内访问快了很多。看了一下Chrome的timing, 大部分建立连接都能够做到0-RTT:

既然这样,顺手实现一个基于QUIC的http代理,把平时查资料时使用的网络也顺带加速一下。(对了,前两天看到Google发布了Outline, 看来这项运动从来都不缺少运动员哪……)

http 代理原理

http 代理处理http和https请求的方式有所不同。对于http请求:

  1. 浏览器与代理服务器建立TCP连接后,将http请求发送给代理服务器。
  2. 代理服务器将http请求发送给目标服务器。
  3. 代理服务器获取到相应结果以后,将结果发送给浏览器。

这里有一个细节需要注意,浏览器向代理服务器发送的http请求URI与直接访问有所不同。

浏览器直接访问 GET http://www.yahoo.com 的http请求格式为:

GET / HTTP/1.1
User-Agent: Quic-Proxy
...

而向代理服务器发送的http请求格式为:

GET http://www.yahoo.com HTTP/1.1
User-Agent: Quic-Proxy
...

也就是浏览器想代理服务器发送的http请求URI中包含了scheme和host,目的是为了让代理服务器知道这个代理请求要访问的目标服务器地址。

对于https请求,一般是通过CONNECT建立隧道:

  1. 浏览器向代理服务器建立TCP连接,发送CONNECT请求。
  2. 代理服务器根据CONNECT请求中包含的host信息,向目标服务器建立TCP连接,然后向浏览器返回200连接成功的响应。
  3. 这时代理服务器同时维持着连接浏览器和目标服务器的TCP连接。
  4. 从浏览器的角度看,相当于建立了一条直连目标服务器的TCP隧道。然后直接在该隧道上进行TLS握手,发送http请求即可实现访问目标服务器的目的。

QUIC Proxy的设计与实现

QUIC Proxy 部署结构图

QUIC Proxy的部署结构与上面http代理原理稍微有所不同。主要区别是增加了qpclient。主要原因是应用程序与代理服务器支架的请求是明文传输(http请求代理是全明文,https请求代理时的CONNECT头会泄露目标服务器信息)。我们是要隐私的人(虽然小扎可能并不care),因此,在应用程序与qpserver之间加了一个qpclient,之间使用QUIC作为传输层。

实现

QUIC Proxy使用Go实现,猴急的同学可以直接到github看源码:Quic Proxy, a http/https proxy using QUIC as transport layer.

代码比较简单,基于标准库的http.Server根据http代理的原理进行了一点http请求的修改。然后,因为qpclientqpserver之间使用QUIC作为transport,而QUIC上的每一个connection都是可以多路复用(multiplexing)的,因此,对于qpserver需要自己实现一个传入http.Server的listener:

type QuicListener struct {
    quic.Listener
    chAcceptConn chan *AcceptConn
}

type AcceptConn struct {
    conn net.Conn
    err  error
}

func NewQuicListener(l quic.Listener) *QuicListener {
    ql := &QuicListener{
        Listener:     l,
        chAcceptConn: make(chan *AcceptConn, 1),
    }
    go ql.doAccept()
    return ql
}

func (ql *QuicListener) doAccept() {
    for {
        sess, err := ql.Listener.Accept()
        if err != nil {
            log.Error("accept session failed:%v", err)
            continue
        }
        log.Info("accept a session")

        go func(sess quic.Session) {
            for {
                stream, err := sess.AcceptStream()
                if err != nil {
                    log.Error("accept stream failed:%v", err)
                    sess.Close(err)
                    return
                }
                log.Info("accept stream %v", stream.StreamID())
                ql.chAcceptConn <- &AcceptConn{
                    conn: &QuicStream{sess: sess, Stream: stream},
                    err:  nil,
                }
            }
        }(sess)
    }
}

func (ql *QuicListener) Accept() (net.Conn, error) {
    ac := <-ql.chAcceptConn
    return ac.conn, ac.err
}

同样的,qpclientqpserver建立连接也需要考虑到多路复用的问题,实现实现一个基于QUIC的dialer:

type QuicStream struct {
    sess quic.Session
    quic.Stream
}

func (qs *QuicStream) LocalAddr() net.Addr {
    return qs.sess.LocalAddr()
}

func (qs *QuicStream) RemoteAddr() net.Addr {
    return qs.sess.RemoteAddr()
}

type QuicDialer struct {
    skipCertVerify bool
    sess           quic.Session
    sync.Mutex
}

func NewQuicDialer(skipCertVerify bool) *QuicDialer {
    return &QuicDialer{
        skipCertVerify: skipCertVerify,
    }
}

func (qd *QuicDialer) Dial(network, addr string) (net.Conn, error) {
    qd.Lock()
    defer qd.Unlock()

    if qd.sess == nil {
        sess, err := quic.DialAddr(addr, &tls.Config{InsecureSkipVerify: qd.skipCertVerify}, nil)
        if err != nil {
            log.Error("dial session failed:%v", err)
            return nil, err
        }
        qd.sess = sess
    }

    stream, err := qd.sess.OpenStreamSync()
    if err != nil {
        log.Info("[1/2] open stream from session no success:%v, try to open new session", err)
        qd.sess.Close(err)
        sess, err := quic.DialAddr(addr, &tls.Config{InsecureSkipVerify: true}, nil)
        if err != nil {
            log.Error("[2/2] dial new session failed:%v", err)
            return nil, err
        }
        qd.sess = sess

        stream, err = qd.sess.OpenStreamSync()
        if err != nil {
            log.Error("[2/2] open stream from new session failed:%v", err)
            return nil, err
        }
        log.Info("[2/2] open stream from new session OK")
    }

    log.Info("addr:%s, stream_id:%v", addr, stream.StreamID())
    return &QuicStream{sess: qd.sess, Stream: stream}, nil
}

好吧,我承认实现代码似乎在200行左右……但是,我们实现了一个client和一个server, 平均下来基本控制在100行左右,对吧……(😂逃……)

部署

:需要golang版本 >= 1.9

1. 在远程服务器上安装 qpserver

go get -u github.com/liudanking/quic-proxy/qpserver

2. 启动qpserver:

qpserver -v -l :3443 -cert YOUR_CERT_FILA_PATH -key YOUR_KEY_FILE_PATH

3. 在本地安装 qpclient

go get -u github.com/liudanking/quic-proxy/qpclient

4. 启动 qpclient:

qpclient -v -k -proxy http://YOUR_REMOTE_SERVER:3443 -l 127.0.0.1:18080

5. 设置应用程序代理:

以 Chrome with SwitchyOmega 为例:

Enjoy!

本站开启支持 QUIC 的方法与配置

在越来越讲究用户体验的今天,网络带宽的提高已经很难有显著的页面加载改善,而低延迟的优化往往能够起到意想不到的效果。在《TLS1.3/QUIC 是怎样做到 0-RTT 的》中我们分析了TLS1.3和QUIC在低延迟方面的原理和低延迟优势。在从源代码编译 nginx docker 镜像开启 TLS 1.3中我们已经把玩了TLS1.3,没有理由不把玩一下QUIC,对吧?

起初以为,在普及程度上,QUIC因为主要是Google主导,会曲高和寡。但是,查了一下,发现腾讯早在2017年就在生产环境应用了QUIC:让互联网更快的协议,QUIC在腾讯的实践及性能优化. 结果显示:

灰度实验的效果也非常明显,其中 quic 请求的首字节时间 (rspStart) 比 http2 平均减少 326ms, 性能提升约 25%; 这主要得益于 quic 的 0RTT 和 1RTT 握手时间,能够更早的发出请求。

此外 quic 请求发出的时间 (reqStart) 比 h2 平均减少 250ms; 另外 quic 请求页面加载完成的时间 (loadEnd) 平均减少 2s,由于整体页面比较复杂, 很多其它的资源加载阻塞,导致整体加载完成的时间比较长约 9s,性能提升比例约 22%。

既然大厂都已经发车,我司也就可以考虑跟进了。稳妥起见,决定先在自己的博客开启QUIC,然后再逐步在线上业务进行推广。

方案概览

方案非常简单:不支持QUIC的浏览器依旧通过nginx tcp 443访问;支持QUIC的浏览器通过caddy udp 443访问。

由于nginx近期没有支持QUIC的计划, 作为一名gopher, 因此这里选择caddy作为QUIC的反向代理。后面会介绍caddy的具体安装和配置方法。

对于支持QUIC的浏览器来说,第一次访问支持QUIC的网站时,会有一次服务发现的过程。服务发现的流程在QUIC Discovery
有详细介绍。概括来说,主要有以下几步:

  1. 通过TLS/TCP访问网站,浏览器检查网站返回的http header中是否包含alt-svc字段。
  2. 如果响应中含有头部:alt-svc: 'quic=":443"; ma=2592000; v="39"',则表明该网站的UDP 443端口支持QUIC协议,且支持的版本号是draft v39; max-age为2592000秒。
  3. 然后,浏览器会发起QUIC连接,在该连接建立前,http 请求依然通过TLS/TCP发送,一旦QUIC连接建立完成,后续请求则通过QUIC发送。
  4. 当QUIC连接不可用时,浏览器会采取5min, 10min的间隔检查QUIC连接是否可以恢复。如果无法恢复,则自动回落到TLS/TCP。

这里有一个比较坑的地方:对于同一个域名,TLS/TCP和QUIC必须使用相同的端口号才能成功开启QUIC。没有什么特殊的原因,提案里面就是这么写的。具体的讨论可以参见Why MUST a server use the same port for HTTP/QUIC?

从上面QUIC的发现过程可以看出,要在网站开启QUIC,主要涉及两个动作:

  1. 配置nginx, 添加alt-svc头部。
  2. 安装和配置QUIC反向代理服务。

配置nginx, 添加alt-svc头部

一行指令搞定:

安装QUIC反向代理服务器caddy

上面我们提到对于同一个域名,TLS/TCP和QUIC必须使用相同的端口号才能成功开启QUIC。然而,caddy服务器的QUIC特性无法单独开启,必须与TLS一起开启,悲剧的是TLS想要使用的TCP 443端口已经被nginx占用了😂

场面虽然有点尴尬,但是我们有docker:将caddy安装到docker中,然后只把本地的UDP 443端口映射到容器中即可。

于是我们创建了一个docker-caddy项目。Dockerfile 10行内搞定:

caddy 服务配置文件/conf/blog.conf:

启动docker:

开启Chrome浏览器QUIC特性

chrome://flags/中找到Experimental QUIC protocol, 设置为Enabled. 重启浏览器生效。

测试QUIC开启状态

重新访问本站https://liudanking.com, 然后在浏览器中打开:chrome://net-internals/#quic。如果你看到了QUIC sessins,则开启成功:

当然,你也可以给Chrome安装一个HTTP/2 and SPDY indicator(An indicator button for HTTP/2, SPDY and QUIC support by each website) 更加直观的观察网站对http/2, QUIC的支持情况。

理工男是如何选择iPhone 8 (Plus)和iPhone X的

上一台手机是iPhone 6 Plus 64G, 使用到现在将近快3年半。6p在升级iOS 10以后,速度就算不上流程了,使用微信和支付宝的时候尤其明显。特别是使用支付宝付款的时候,经常卡在story board画面,没少受后面排队哥们白眼。

现在(2018.03)并不算是购买苹果这代手机最好的时机,因为一般最好的价格都是出现在双十一。而且本着早买早享受的原则,现在入手其实体验周期也间接变短了。这次考虑换机原因只有一个:6p实在是不能战斗下去了。虽然并不喜欢这代的iPhone,但是,也只能矮子里面拔将军了:选iPhone 8 (Plus)还是iPhone X?

习惯性的打开哈乎看了一下,纠结着两个选项的人好像还挺多的:

  • 你会买 iPhone 8 还是 iPhone X?
  • 你会选择买iPhone X还是iPhone 8,或者其他手机?
  • iPhone 8 (Plus) 和 iPhone X 你会买哪个?为什么?
  • iphone X 和 iphone 8/8plus 哪个更值得买?
  • iPhone 8/8p/X,不知道选哪个?

其实上面的问题只会越看越不知道自己选什么,因为每个人都有自己的需求和背景。

这里我就不卖关子,直接给出自己的选择:iPhone 8 Plus, 容量看自己的需要,因为自己的6p是64G且容量将满,因此选的是256G。主要理由如下:

face id解锁交互用在了错误的场景

face id解锁是一个自己无论如何也无法接受的交互方式。我仔细体验过同事的iPhone X,成功率尚可,但是做不到「存在于无形之中」。人脸解锁如果做不到这一点,就是一个半成品。

这一点类似于汽车行业之前大规模使用触屏代替物理按键(呃,好吧,马斯克同学还在坚持在特斯拉车型中大规模使用触屏……),不是说触屏控制本身存在技术障碍,而是在这个场景中使用了一个高成本且不合适的技术。

我甚至无法想象晚上在被窝中要解锁iPhone X时,必须要让手机的主动探照光闪瞎狗眼的酸辣情景。那么,什么场景是适合face id的呢?我认为是在结合其他id因子的安全领域,比如在大额支付时,使用touch id和face id进行双重校验。

iPhone X的全面屏是个笑话,全面屏最终都会变成笑话

齐刘海问题肯定算不上优点,但是问题并不大(好吧,其实苹果开发者还是挺吐槽这个刘海带来的额外开发工作量的……)。重点是,全面屏在当前阶段并不是一个手机尺寸上能够提升用户体验的发展方向。

以前手机尺寸的发展方向是变薄,有些厂家甚至以此作为卖点。但是几年下来,还有哪个厂家将自家机器薄作为卖点?就连以前乔帮主健在时,发布会现场直击从文件袋里拿出来的iPad Air也不在突出它薄的数值了。

空口无凭,我们看一下苹果历代16款手机的厚度数据对比:

结果是不是很意外?

  • 从iPhone 5开始,苹果手机的厚度基本就没有太大变化。
  • iPhone 6是迄今为止,苹果最薄的手机。
  • iPhone 6开始,苹果手机厚度是逐年上升的!
  • 更加有意思的是,iPhone X是水果这5年来最厚的手机😂

那么这个问题跟全面屏有什么关系呢?我们先看一下全面屏的定义:

全面屏从字面上解释就是手机的正面全部都是屏幕,手机的四个边框位置都是采用无边框设计,追求接近100%的屏占比。但其实到目前为止没有一个严格意义上的全面屏幕定义,而比较主流的看法是要有18:9的屏幕,正面取消任何实体或者触摸按键,只有一块完整的屏幕,而且屏幕占比至少要达到80%以上。显然目前有很多手机虽然也号称全面屏幕,但屏幕占比却没有达到这个要求,所以看上去边框很宽,并没有特别好的效果。

所以,仔细想想,你就发现,在现阶段各家手机屏占比越做越高的今天,全面屏已经基本成为或接近事实,并不需要为了迎合这个概念而调整手机本来已经验证多年的设计,尤其是正面的按键和触感器排列。这一点上,锤子手机罗永浩是深谙其道的,他发布的坚果 2 Pro手机宣传语就很准确「Almost 全面屏」😝

因此,当前阶段,行业对全面屏的疯狂追捧跟当年恨不得手机能够用来切菜的追逐是如此神似。当然,也有头脑清醒的人只是跟着喊口号,但是并没有真的跳进坑里,比如老罗。

手机是需要握持使用的,两边的边框是一定会有所保留的,无论是iPhone X还是小米的MIX系列。有意思的是,这两个厂家疯狂宣传他们全面屏的同时,却没有告诉你,他们把手机两旁的边框做了轻微加高和立面加厚处理,当你进行从屏幕边缘划入屏幕操作时,感觉手指被一个圆铁丝顶了一下一样,还不如以前「正常屏」手机顺滑。而iPhone X的全面屏还有一个额外的槽点:话说你提高屏占比是为了显示更多的信息吧,但是你看看iPhone X现在的屏幕显示情况,很多情况下屏幕下方很大一截都是空的,要这铁棒有何用?

小结苹果的全面屏:

  • 全面屏是一个伪概念,在当今屏占比越来越高的今天,全面屏就是个概念和幌子。
  • 在屏占比提到到一定程度以后,对用户体验影响的提高就很小了,甚至会降低用户体验。
  • 如果一定要为了全面屏而改变正面的一些布局,无疑是舍本逐末、因小失大的做法。

iPhone X的优点

但并不是说iPhone X就全无优点。

结合其深度传感器使用的Animoji可能如当初Emoji一样影响整个行业,前提是深度传感器能够迅速降低成本并普及。

摄像头双重防抖对于喜欢拍照的同学来说更是锦上添花。

OLED屏幕显示效果主观观感的确非常赞。

选择iPhone 8 Plus的困扰

  • 没有辨识度。其实换手机有好几天了,但是同事根本没有发现我换了手机。但是,iPhone早就过了可能装X的年代了,所以也没觉得损失什么。
  • 多年使用同一个设计ID的产品。6p用了三年多,8p估计也会用2年以上,意味着6年都用着一个外观的产品。这点自己并不排斥,要知道设计不是一直向前的,有时候还会轻微倒退。正如我一直觉得ThinkPad T61/T400的设计是一代经典,因此我现在还在用着这10年前的笔记本电脑做一些简单的文档处理和网页浏览。
  • 手机重量历代之最,用久了手腕疼。但是有些人觉得这才是质感
  • 玻璃后盖易碎。但是有些人认为这才叫温润如玉。

其实上面说的都谈不上困扰,只是你选了8p以后需要考虑能否接受的小妥协。

为什么不选择iPhone 7 (Plus)?

选iPhone 7 (Plus)的唯一原因当然是价格。但是,作为一名理工男+参数党,通过研究苹果这一代的芯片发现,iPhone 7可能并不适合自己。

从上图可以发现,这两代CPU的制程一个是16nm, 一个是10nm. (苹果历代CPU参数可以参见这里。 无论是理论还是实际测试,A11芯片都比A10芯片能效更高,续航时间更长。而iPhone 8 Plus更是做到了在电池容量比iPhone 7 Plus少10%左右的情况下,续航反而增加了一个多小时。

另一方面,A11采用的是两个高性能+4个能效核的设计,比A10多两个效能核。理论上A11的CPU跑分会更高。使用Geekbench 4也应证了这一点:

单核性能提升23%,多核性能提升76%!

之于iPhone 8 Plus加入的无线充电,聊胜于无吧。倒是快充功能在紧急场合能够发挥一点作用。

另一方面,由于自己换机频率不高,因此,对于自己而言,iPhone 8 Plus更加适合自己。而且这几天购买正好碰到3.8女王节活动,直降1000,性价比也不算低。

总结

不选iPhone X有很多理由,选择iPhone 8 Plus则是因为被动选择。虽然当前的iPhone X只是一个半成品,但是库克肯定认为它是以后苹果手机的发展方向,自己对下一代iPhone X也没有任何期待。因此iPhone 8就是苹果以iPhone 6为设计语言的一代产品的绝唱了,这也算是自己选8的一个原因。

macOS无法打开app应用程序的解决办法

macOS Sierra以后(EI Capitan, High Sierra),默认只允许打开来自Mac App Store或苹果的有效开发者发布的应用。你可以在「System Preferences -> Security & Privacy」查看当前状态:

如果是从网络下载安装的app,在打开时可能会遇到这两个问题:

  • YOUR_APP is from an unidentified developer, Are you sure you want to open is?
  • YOUR_APP is damaged and can’t be opened. You should move it to the Trash.

可以尝试在终端中执行sudo spctl --master-disable(需要输入密码)以允许打开任意来源的app:

如果执行成功,「Anywhere」选项为选中状态。「Security & Privacy」状态如下:

如果需要关闭「Anywhere」选项,则在终端执行sudo spctl --master-disable.

注:打开该「Anywhere」选项有安全风险,请确保你在打开该选项是了解app的来源,只信任自己清楚来源的app.