FreeWheel基于Go的实践经验漫谈——GC是大坑(关键业务场景不用),web框架尚未统一,和c++性能相比难说
摘自:http://www.infoq.com/cn/news/2017/06/freewheel-experience-on-go
Go语言是FreeWheel公司目前主要力推的一个方向,在其看来,面向服务的架构的大环境中,Go非常适合做一些功能相对独立、功能比较明确的微服务的语言。在结合已有的各种编程语言,计算框架(如Hadoop、Java、Ruby、C++)的基础上,FreeWheel把Go语言定位成用来实现轻量级服务或API的缺省编程语言,将之与用来完成更小粒度工作的Python结合在一起,就构成了FreeWheel的整个技术语言栈 。
FreeWheel在Go上所经历的“坑”
虽然从2012年Go 1.0发布到团队相继采用Go来编写项目,这中间经历了大致三年左右的时间,但由于在GC等许多问题的克服上需要Go本身去做一部分迭代,FreeWheel也需要把技术对客户的影响控制在一个可控的范围内,因此作为一家B-to-B企业,其采用了更为渐进的方式将Go语言应用到自身的生产平台上。
在这个过程中,FreeWheel也经历过两个较为重要的“坑”。
- GC的问题
如多数人所知道的一样,Go语言垃圾回收器存在一定的缺陷,特别是容易导致整个进程不可预知的间歇性停顿。像某些大型后台服务程序,如游戏服务器、APP容器等,由于占用内存巨大,其内存对象数量极多,GC完成一次回收周期,可能需要数秒甚至更长时间,这段时间内,整个服务进程是阻塞的、停顿的,在外界看来就是服务中断、无响应。FreeWheel在使用Go 1.4版本时也遇到过类似问题:广告预测团队用Go来实现调度器,平常运行的时候没有问题,但一旦触发1.4版本下GC的时候,该系统的downgrade非常厉害,导致任务的堆积非常严重,触发报警,同时其处理性会下降很多,也会影响其他上下游系统的正常运转。
于是,FreeWheel在当时主要采取了三种对策:一、并不把Go用在非常关键的、对服务进程稳定性要求较高的系统里;二、引入Kafka之类的能够持久化的消息队列,能够缓存和重释这样的方式去解决这个问题,使系统能扛住冲击,并在后面把它消化掉;三、尽量复用已经创建的对象,防止Go频繁的创建了回收对象。
Go 1.5到1.7版本相继出来后,GC的系统性能得到不断改进和持续提升(从秒级到毫秒级)。对于目前FreeWheel内生存环境不太关键的系统来说,Go 1.7之后的GC已经可以达到可接受的范围和程度以内。
- 内置数据结构的变化
很多人都知道,Go语言提供的字典类型并不是并发安全的,此外由于Go语言发展较快,有些内置的数据结构如Map的行为也发生了变化。因为Map为引用类型,所以即使函数传值调用,参数副本依然指向映射m,所以多个goroutine并发写同一个映射m。例如,如果map由多协程同时读和写就会出现 fatal error:concurrent map read and map write的错误。
Go 1.6版本之前Map可以支持并发读写,但FreeWheel开发的程序在升级到1.6之后也就发现Map产生了读写竞争的问题。
对于这一问题,常用的有两种解决方案,一是如上所说的加锁(包括通用锁和读写锁),二是利用channel串行化处理。FreeWheel的做法也主要靠两方面,其一是将锁粒度设计的更细,使得并发的依赖更少;另外是在不同的数据结构中,选择性能更高的一方。比如array和slice中,前者就是更优选择。
FreeWheel首席架构师刘昊植认为,并发的时候不能假设Go能完美处理所有的工作,工程师需要结合并借鉴传统(成熟)的编程语言,比如Java或者C对并发的经验,在动手之前就想清楚并发的规模、锁的粒度等,并对系统会如何运行有非常明确的设计和理解。
同时,刘昊植也坦言,随着Go的快速发展阶段,FreeWheel内部也有多种不同的声音,例如运维团队就会觉得快速发展阶段的语言稳定性不够,它的特性和数据结构会因为版本升级等原因产生很大变化,并且部分变化不能完全保持向前兼容。在这个过程中FreeWheel的总结是:不管Go怎样实现,都要对系统并发做很好的支持,在应用层面做保护和控制,这样才保证这个系统能够正常的运行。
为什么FreeWheel没有全部用系统重写Go
从编程范式的角度来说, Go语言是变革派,而不是改良派。对于C++、Java和C#等语言为代表的面向对象的思想体系,总体来说全球范围内许多公司对Go语言的态度更为保守,多数持有限吸收的观念(这可从下图中Go的热度分布情况看出)。

即使FreeWheel在实践中发现Go比其他类编程语言具有许多更为明显的优势,如在写并行上,相比Python这样的解释语言要高一个数量级;如前期由Python开发的很多轻量级API,因为全局解释锁GIL的关系而面临着进程间通信带来额外开销,所以就把轻量级API迁移到Go上;又比如Go在并发上的优势,适合面向多用户同时上传、同时调用API的场景……但其内部团队也并没有用Go来重写全部系统。原因主要有两点:
第一,FreeWheel有很多已有的算法实现,想全部切入到Go上会面临巨大的开销和成本;第二,相比C或者C++,Go在高性能方面还没有完全的证明自己。在Web服务器端,它目前也没有一个特别好的像RoR、Django、或者PHP的流行框架。对于FreeWheel来说,整个广告服务器是不允许出现明显的downgrade情况(尤其是当GC时),所以对这种非常关键的系统,目前还不能完全用Go去写。所以刘昊植也认为,Go在扩展使用场景层面可能还需要做一些较大变革。
此外,FreeWheel基于Go的Web程序,目前使用的是Gorilla框架。但从Martini、Revel、Gocraft/web等几款主流框架的使用和评价上看(可以下图github上的数据做个参考),Go社区还没有一款处于统治地位的Web框架。如果Go想把它的触角伸得更长,这可能是其未来发力的一个方向。

但总的来说,Go在高并发、开发效率等特性上的优势,决定了Go在FreeWheel内的采用程度会越来越深。刘昊植说:“除了一些已有业务依赖于Hadoop、Spark这样的基础设施,对于新增的业务和功能,Go语言会是我们的首选。”
另外,FreeWheel希望其使用的编程语言是能够得到跨大陆、跨时区、受所有工程师共同认可的,所以Go或许会是其最好的选择。这个过程中,FreeWheel也评估过很多其他语言如Scala、Rust等,但最终因为Go在学习成本、统一实践、社区规模等方面的优势而胜出。
FreeWheel基于Go的实践经验漫谈——GC是大坑(关键业务场景不用),web框架尚未统一,和c++性能相比难说的更多相关文章
- CI Weekly #6 | 再谈 Docker / CI / CD 实践经验
CI Weekly 围绕『 软件工程效率提升』 进行一系列技术内容分享,包括国内外持续集成.持续交付,持续部署.自动化测试. DevOps 等实践教程.工具与资源,以及一些工程师文化相关的程序员 Ti ...
- 根据实践经验,讲述些学习Java web能少走的弯路,内容摘自java web轻量级开发面试教程
在和不少比较上进的初级程序员打交道的过程中,我们总结出了一些能帮到合格程序员尽快进阶的经验,从总体上来讲,多学.多实践不吃亏.本文来是从 java web轻量级开发面试教程从摘录的. 1 哪些知识点 ...
- 华为云对Kubernetes在Serverless Container产品落地中的实践经验
华为云容器实例服务,它基于 Kubernetes 打造,对最终用户直接提供 K8S 的 API.正如前面所说,它最大的优点是用户可以围绕 K8S 直接定义运行应用. 这里值得一提是,我们采用了全物理机 ...
- 领域驱动设计(DDD)的实践经验分享之ORM的思考
原文:领域驱动设计(DDD)的实践经验分享之ORM的思考 最近一直对DDD(Domain Driven Design)很感兴趣,于是去网上找了一些文章来看看,发现它确实是个好东西.于是我去买了两本关于 ...
- 领域驱动设计(DDD)的实践经验分享之持久化透明
原文:领域驱动设计(DDD)的实践经验分享之持久化透明 前一篇文章中,我谈到了领域驱动设计中,关于ORM工具该如何使用的问题.谈了很多我心里的想法,大家也对我的观点做了一些回复,或多或少让我深深感觉到 ...
- Java 8 Optional类使用的实践经验
前言 Java中空指针异常(NPE)一直是令开发者头疼的问题.Java 8引入了一个新的Optional类,使用该类可以尽可能地防止出现空指针异常. Optional 类是一个可以为null的容器对象 ...
- 关于Flask使用Celery的实践经验分享
最近大Boss反馈Celery经常出现问题,几经实践终于把问题解决了!于是乎有了这篇博客的诞生,算是一个实践经验的分享吧! 软件版本如下: Celery () Flask () RabbitMQ( ...
- Surging实践经验
背景 在去年9月份的时候,我入职一家做航空软件产品的公司.当时公司部门领导决定构建一个技术平台(或称为技术中台),通过该技术平台进而孵化各个业务系统.说白了就是需要通过一个分布式框架或是微服务框架提高 ...
- 基于SCRUM方法实践的西油计科党建设计与实现
基于SCRUM方法实践的西油计科党建设计与实现 序言 所属课程 https://edu.cnblogs.com/campus/xnsy/2019autumnsystemanalysisanddesig ...
随机推荐
- 编写高质量的js之恰当选用if和switch
switch结构中存在很多限制,存在这些限制的主要目的是提高多重分支结构的执行效率.因此,如果能够使用switch结构,就不要选择if结构. 无论是使用if结构,还是使用switch结构,应该确保下面 ...
- Java_Web三大框架之Hibernate 入门(一)
一.Hibernate简介: Hibernate作者——Gavin King Hibernate创始人 < Hibernate in action >作者 EJB 3.0的Entity b ...
- B+树知识点
B+树介绍 目录 B+树 B+树的插入操作 B+树的删除操作 回到顶部 B+树 B+树和二叉树.平衡二叉树一样,都是经典的数据结构.B+树由B树和索引顺序访问方法(ISAM,是不是很熟悉?对,这也 ...
- 1 JSONP
一.什么是跨域访问举个栗子:在A网站中,我们希望使用Ajax来获得B网站中的特定内容.如果A网站与B网站不在同一个域中,那么就出现了跨域访问问题.你可以理解为两个域名之间不能跨过域名来发送请求或者请求 ...
- Linux自动化之Cobbler补鞋匠安装
cobbler介绍: 快速网络安装linux操作系统的服务,支持众多的Linux发行版:Red Hat. Fedora.CentOS.Debian.Ubuntu和SuSE,也可以支持网络安装w ...
- HTML学习笔记之标签基础
目录 1.基本标签 2.链接 3.图像 4.表格 5.列表 6.块与布局 1.基本标签 (1)标题与段落 标签 <h1> ~ <h6> 分别用于定义一至六级标题,标签 < ...
- npm和gulp学习
npm的使用 node Node.js 是一个基于 Chrome V8 引擎的 JavaScript 运行环境,是一种JavaScript语言运行平台,和浏览器这个运行平台是同一个概念. npm np ...
- JavaScript基本知识----操作符,流程控制,循环,字符串方法,数组方式,补充方法
操作符 算术运算 <!DOCTYPE html> <html lang="en"> <head> <meta charset=" ...
- noip模拟赛 排序
分析:因为序列是不严格单调的,所以挪动一个数其实就相当于把这个数给删了.如果a[i] < a[i-1],那么可以删掉a[i],也可以删掉a[i-1](!如果没考虑到这一点就只有90分),删后判断 ...
- 清北学堂模拟赛d4t6 c
分析:这道题比较有难度. 观察题目,发现只有当一行翻了奇数次后才会产生黑色格子,设有x行被翻了奇数次,y列被翻了偶数次,那么x*m + y*n - 2*x*y = s,接下来就要解方程了.对于二元一次 ...