Go开发关键技术指南:Go2 Transition

Go2 Transition

Go2的设计草案在Go 2 Draft Designs或者这里,而Go1如何迁移到Go2也是我个人特别关心的问题,Python2和Python3的那种不兼容的迁移方式简直就是噩梦一样的记忆。Go的提案中,有一个专门说了迁移的问题,参考Go2 Transition

Go2 Transition还不是最终方案,不过它也对比了各种语言的迁移,还是很有意思的一个总结。这个提案描述了在非兼容性变更时,如何给开发者挖的坑最小。

目前Go1的标准库是遵守兼容性原则的,参考Go 1 compatibility guarantee,这个规范保证了Go1没有兼容性问题,几乎可以没有影响的升级比如从Go1.2升级到Go1.11。几乎的意思,是很大概率是没有问题,当然如果用了一些非常冷门的特性,可能会有坑,我们遇到过json解析时,内嵌结构体的数据成员也得是exposed的才行,而这个在老版本中是可以非exposed;还遇到过cgo对于链接参数的变更导致编译失败,这些问题几乎很难遇到,都可以算是兼容的吧,有时候只是把模糊不清的定义清楚了而已。

Go2在语言和标准库上,会打破Go1的兼容性规范,也就是和Go1不再兼容。不过Go是分布式开源社区在维护,不能依赖于flag day,还是要容许不同Go版本写的package的互操作性。先了解下各个语言如何考虑兼容性:

  • C是严格向后兼容的,很早写的程序总是能在新的编译器中编译。另外新的编译器也支持指定之前的标准,比如-std=c90使用ISO C90标准编译程序。关键的特性是编译成目标文件后,不同版本的C的目标文件,能完美的链接成执行程序。C90实际上是对之前K&R C版本不兼容的,主要引入了volatile关键字,还有整数精度问题,还引入了trigraphs,最糟糕的是引入了undefined行为比如数组越界和整数溢出的行为未定义。从C上可以学到的是:后向兼容非常重要;非常小的打破兼容性也问题不大特别是可以通过编译器选项来处理;能将不同版本的目标文件链接到一起是非常关键的;undefined行为严重困扰开发者容易造成问题。
  • C++也是ISO组织驱动的语言,和C一样也是向后兼容的。C++和C一样坑爹的地方坑到吐血,比如undefined行为等等。尽管一直保持向后兼容,但是新的C++代码比如C++11看起来完全不同,这是因为有新的改变的特性,比如很少会用裸指针,比如range代替了传统的for循环,这导致熟悉老C++语法的程序员看新的代码非常难受甚至看不懂。C++毋庸置疑是非常流行的,但是新的语言标准在这方面没有贡献。从C++上可以学到的新东西是:尽管保持向后兼容,语言的新版本可能也会带来巨大的不同的感受(保持向后兼容并不能保证能持续看懂)。
  • Java也是向后兼容的,是在字节码层面和语言层面都向后兼容,尽管语言上不断的新增了关键字。Java的标准库非常庞大,也不断的在更新,过时的特性会被标记为deprecated并且编译时会有警告,理论上一定版本后deprecated的特性会不可用。Java的兼容性问题主要在JVM解决,如果用新的版本编译的字节码,得用新的JVM才能执行。Java还做了一些前向兼容,这个影响了字节码啥的(我本身不懂Java,作者也不说自己不是专家,我就没仔细看了)。Java上可以学到的新东西是:要警惕因为保持兼容性而限制语言未来的改变。
  • Python2.7是2010年发布的,目前主要是用这个版本。Python3是2006年开始开发,2008年发布,十年后的今天还没有迁移完成,甚至主要是用的Python2而不是Python3,这当然不是Go2要走的路。看起来是因为缺乏向后兼容导致的问题,Python3刻意的和之前版本不兼容,比如print从语句变成了一个函数,string也变成了Unicode(这导致和C调用时会有很多问题)。没有向后兼容,同时还是解释型语言,这导致Python2和3的代码混着用是不可能的,这以为着程序依赖的所有库必须支持两个版本。Python支持from __future__ import FEATURE,这样可以在Python2中用Python3的特性。Python上可以学到的东西是:向后兼容是生死攸关的;和其他语言互操作的接口兼容是非常重要的;能否升级到新的语言是由调用的库支持的。
  • Perl6是2000年开始开发的,15年后才正式发布,这也不是Go2应该走的路。这么漫长的主要原因包括,刻意没有向后兼容,只有语言的规范没有实现而这些规范不断的修改。Perl上可以学到的东西是:不要学Perl;设置期限按期交付;别一下子全部改了。

特别说明的是,非常高兴的是Go2不会重新走Python3的老路子,当初被Python的版本兼容问题坑得不要不要的。

虽然上面只是列举了各种语言的演进,确实也了解得更多了,有时候描述问题本身,反而更能明白解决方案。C和C++的向后兼容确实非常关键,但也不是他们能有今天地位的原因,C++11的新特性到底增加了多少DAU呢,确实是值得思考的。另外C++11加了那么多新的语言特性,比如WebRTC代码就是这样,很多老C++程序员看到后一脸懵逼,和一门新的语言一样了,是否保持完全的兼容不能做一点点变更,其实也不是的。

应该将Go的语言版本和标准库的版本分开考虑,这两个也是分别演进的,例如alias是1.9引入的向后兼容的特性,1.9之前的版本不支持,1.9之后的都支持。语言方面包括:

  • Language additions新增的特性,比如1.9新增的type alias。这些向后兼容的新特性,并不要求代码中指定特殊的版本号,比如用了alias的代码不用指定要1.9才能编译,用之前的版本会报错。向后兼容的语言新增的特性,是依靠程序员而不是工具链来维护的,要用这个特性或库升级到要求的版本就可以。
  • Language removals删除的特性。比如有个提案#3939去掉string(int),字符串构造函数不支持整数,假设这个在Go1.20版本去掉,那么Go1.20之后这种string(1000)代码就要编译失败了。这种情况没有特别好的办法能解决,我们可以提供工具,将代码自动替换成新的方式,这样就算库维护者不更新,使用者自己也能更新。这种场景引出了指定最大版本,类似C的-std=C90,可以指定最大编译的版本比如-lang=go1.19,当然必须能和Go1.20的代码链接。指定最大版本可以在go.mod中指定,这需要工具链兼容历史的版本,由于这种特性的删除不会很频繁,维护负担还是可以接受的。
  • Minimum language version最小要求版本。为了可以更明确的错误信息,可以允许模块在go.mod中指定最小要求的版本,这不是强制性的,只是说明了这个信息后编译工具能明确的给出错误,比如给出应该用具体哪个版本。
  • Language redefinitions语言重定义。比如Go1.1时,int在64位系统中长度从4字节变成了8字节,这会导致很多潜在的问题。比如#20733修改了变量在for中的作用域,看起来是解决潜在的问题,但也可能会引入问题。引入关键字一般不会有问题,不过如果和函数冲突就会有问题,比如error: check。为了让Go的生态能迁移到Go2,语言重定义的事情应该尽量少做,因为我们不再能依赖编译器检查错误。虽然指定版本能解决这种问题,但是这始终会导致未知的结果,很有可能一升级Go版本就挂了。我觉得对于语言重定义,应该完全禁止。比如#20733可以改成禁止这种做法,这样就会变成编译错误,可能会帮助找到代码中潜在的BUG。
  • Build tags编译tags。在指定文件中指定编译选项,是现有的机制,不过是指定的release版本号,它更多是指定了最小要求的版本,而没有解决最大依赖版本问题。
  • Import go2导入新特性。和Python的特性一样,可以在Go1中导入Go2的新特性,比如可以显示的导入import "go2/type-aliases",而不是在go.mod中隐式的指定。这会导致语言比较复杂,将语言打乱成了各种特性的组合。而且这种方式一旦使用,将无法去掉。这种方式看起来不太适合Go。

如果有更多的资源来维护和测试,标准库后续会更快发布,虽然还是6个月的周期。标准库方面的变更包括:

  • Core standard library核心标准库。有些和编译工具链相关的库,还有其他的一些关键的库,应该遵守6个月的发布周期,而且这些核心标准库应该保持Go1的兼容性,比如os/signalreflectruntimesynctestingtimeunsafe等等。我可能乐观的估计net, os, 和syscall不在这个范畴。
  • Penumbra standard library边缘标准库。它们被独立维护,但是在一个release中一起发布,当前核心库大部分都属于这种。这使得可以用go get等工具来更新这些库,比6个月的周期会更快。标准库会保持和前面版本的编译兼容,至少和前面一个版本兼容
  • Removing packages from the standard library去掉一些不太常用的标准库,比如net/http/cgi等。

如果上述的工作做得很好的话,开发者会感觉不到有个大版本叫做Go2,或者这种缓慢而自然的变化逐渐全部更新成了Go2。甚至我们都不用宣传有个Go2,既然没有C2.0为何要Go2.0呢?主流的语言比如C、C++和Java从来没有2.0,一直都是1.N的版本,我们也可以模仿他们。事实上,一般所认为的全新的2.0版本,若出现不兼容性的语言和标准库,对用户也不是个好结果,甚至还是有害的。

Links

由于简书限制了文章字数,只好分成不同章节:

  • Overview 为何Go有时候也叫Golang?为何要选择Go作为服务器开发的语言?是冲动?还是骚动?Go的重要里程碑和事件,当年吹的那些牛逼,都实现了哪些?
  • Could Not Recover 君可知,有什么panic是无法recover的?包括超过系统线程限制,以及map的竞争写。当然一般都能recover,比如Slice越界、nil指针、除零、写关闭的chan等。
  • Errors 为什么Go2的草稿3个有2个是关于错误处理的?好的错误处理应该怎么做?错误和异常机制的差别是什么?错误处理和日志如何配合?
  • Logger 为什么标准库的Logger是完全不够用的?怎么做日志切割和轮转?怎么在混成一坨的服务器日志中找到某个连接的日志?甚至连接中的流的日志?怎么做到简洁又够用?
  • Interfaces 什么是面向对象的SOLID原则?为何Go更符合SOLID?为何接口组合比继承多态更具有正交性?Go类型系统如何做到looser, organic, decoupled, independent, and therefore scalable?一般软件中如果出现数学,要么真的牛逼要么装逼。正交性这个数学概念在Go中频繁出现,是神仙还是妖怪?为何接口设计要考虑正交性?
  • Modules 如何避免依赖地狱(Dependency Hell)?小小的版本号为何会带来大灾难?Go为什么推出了GOPATH、Vendor还要搞module和vgo?新建了16个仓库做测试,碰到了9个坑,搞清楚了gopath和vendor如何迁移,以及vgo with vendor如何使用(毕竟生产环境不能每次都去外网下载)。
  • Concurrency & Control 服务器中的并发处理难在哪里?为什么说Go并发处理优势占领了云计算开发语言市场?什么是C10K、C10M问题?如何管理goroutine的取消、超时和关联取消?为何Go1.7专门将context放到了标准库?context如何使用,以及问题在哪里?
  • Engineering Go在工程化上的优势是什么?为什么说Go是一门面向工程的语言?覆盖率要到多少比较合适?什么叫代码可测性?为什么良好的库必须先写Example?
  • Go2 Transition Go2会像Python3不兼容Python2那样作吗?C和C++的语言演进可以有什么不同的收获?Go2怎么思考语言升级的问题?
  • SRS & Others Go在流媒体服务器中的使用。Go的GC靠谱吗?Twitter说相当的靠谱,有图有真相。为何Go的声明语法是那样?C的又是怎样?是拍的大腿,还是拍的脑袋?
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
禁止转载,如需转载请通过简信或评论联系作者。
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 195,783评论 5 462
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 82,360评论 2 373
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 142,942评论 0 325
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,507评论 1 267
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,324评论 5 358
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,299评论 1 273
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,685评论 3 386
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,358评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,652评论 1 293
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,704评论 2 312
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,465评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,318评论 3 313
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,711评论 3 299
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,991评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,265评论 1 251
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,661评论 2 342
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,864评论 2 335

推荐阅读更多精彩内容

  • Others 关于Go,还有哪些重要的技术值得了解的,下面详细分享。 GC GC一般是C/C++程序员对于Go最常...
    winlinvip阅读 1,355评论 1 0
  • 今天感恩节哎,感谢一直在我身边的亲朋好友。感恩相遇!感恩不离不弃。 中午开了第一次的党会,身份的转变要...
    迷月闪星情阅读 10,534评论 0 11
  • 彩排完,天已黑
    刘凯书法阅读 4,168评论 1 3
  • 没事就多看看书,因为腹有诗书气自华,读书万卷始通神。没事就多出去旅游,别因为没钱而找借口,因为只要你省吃俭用,来...
    向阳之心阅读 4,765评论 3 11
  • 表情是什么,我认为表情就是表现出来的情绪。表情可以传达很多信息。高兴了当然就笑了,难过就哭了。两者是相互影响密不可...
    Persistenc_6aea阅读 123,277评论 2 7