在线时间:8:00-16:00
迪恩网络APP
随时随地掌握行业动态
扫描二维码
关注迪恩网络微信公众号
近期看了一篇关于go产品开发最佳实践的文章,go-in-procution。作者总结了他们在用go开发过程中的非常多实际经验,我们非常多事实上也用到了。鉴于此,这里就简单的写写读后感,兴许我也争取能将这篇文章翻译出来。后面我用soundcloud来指代原作者。 开发环境在soundcloud,每一个人使用一个独立的GOPATH,而且在GOPATH直接依照go规定的代码路径方式clone代码。
对于go来说,通常的project管理应该是例如以下的文件夹结构:
然后我们在GOPATH里面将proj的路径设置上去。这样就能够进行编译执行了。 这本来没啥,可是假设我们要将其代码提交到github。并同意另外的开发人员使用。我们就不能将整个proj的东西提交上面,假设提交了,就非常蛋疼了。外面的开发人员可能这么引用:
可是我们自己在代码里面就能够直接:
假设外面的开发人员须要依照去掉src的引用方式,仅仅能把GOPATH设置到proj文件夹,假设import的多了,会让人崩溃的。 我曾今也被这事情给折腾了好久,最终再看了vitess的代码之后。发现了上面这样的方式。认为非常不错。 project文件夹结构假设一个项目中文件数量不是非常多,直接放在main包里面即可了,不须要在拆分成多个包了。譬如:
假设真的有公共的类库,在拆分成单独的包处理。 有时候,一个project可能会包含多个二进制应用。 譬如,一个job可能须要一个server,一个worker或者一个janitor,在这样的情况下,建立多个子文件夹作为不同的main包,分别放置不同的二进制应用。 同一时候使用另外的子文件夹实现公共的函数。
这点我的做法略微有一点不一样,主要是參考vitess,我喜欢建立一个总的cmd文件夹,然后再在里面设置不同的子文件夹,这样外面就不须要推測这个文件夹是库还是应用。 代码风格代码风格这没啥好说的,直接使用gofmt解决,通常我们也约定gofmt的时候不带不论什么其它參数。 最好将你的编辑器配置成保存代码的时候自己主动进行gofmt处理。 Google近期公布了go的代码规范,soundcloud做了一些改进:
假设一个函数有多个參数,而且单行长度非常长,须要拆分。最好不用java的方式:
而是使用:
相似的,当构造一个对象的时候,最好在初始化的时候就传入相关參数,而不是在后面设置:
假设一些变量是兴许通过其它操作才干获取的,我认为就能够在兴许设置了。 配置soundcloud使用go的flag包来进行配置參数的传递,而不是通过配置文件或者环境变量。 flag的配置是在main函数里面定义的。而不是在全局范围内。
关于使用flag作为配置參数的传递。我持保留意见。 假设一个应用须要特别多的配置參数,使用flag比較让人蛋疼了。这时候,使用配置文件反而比較好,我个人倾向于使用json作为配置。原因在这里。 日志soundcloud使用的是go的log日志。他们也说明了他们的log并不须要太多的其它功能,譬如log分级等。对于log,我參考python的log写了一个,在这里。该log支持log级别。支持自己定义loghandler。 soundcloud还提到了一个telemetry的概念。我真没好的办法进行翻译,据我的了解可能就是程序的信息收集,包含响应时间。QPS,内存执行错误等。 通常telemetry有两种方式,推和拉。 推模式就是主动的将信息发送给特定的外部系统,而拉模式则是将其写入到某一个地方。同意外部系统来获取该数据。 这两种方式都有不同的定位。假设须要及时,直观的看到数据,推模式是一个非常好的选择,可是该模式可能会占用过多的资源,尤其是在数据量大的情况以下。会非常消耗CPU和带宽。 soundcloud貌似採用的是拉模型。 关于这点我是深表赞同,我们有一个服务。须要将其信息发送到一个统计平台共兴许的信息,開始的时候,我们使用推模式。每产生一条记录,我们直接通过http推给后面的统计平台。最终,随着压力的增大。整个统计平台被我们发挂了。拒绝服务。 最终,我们採用了将数据写到本地,然后通过还有一个程序拉取再发送的方式解决。 測试soundcloud使用go的testing包进行測试,然后也使用flag的方式来进行集成測试,例如以下:
由于go test也支持相似go build那种flag传递,它会默认合成一个main package,然后在里面进行flag parse处理。 这样的方式我如今没有採用。我都是在測试用例里面直接写死了一个全局的配置,主要是为了方便的在根文件夹进行 go test ./...处理。 只是使用flag的方式我认为灵活性非常大,后面假设有可能会考虑。 go的testing包提供的功能并不强。譬如没有提供assert_equal这类东西,可是我们能够通过reflect.DeepEqual来解决。 依赖管理这块事实上也是我非常想解决的。 如今我们的代码就是非常暴力的用go get来解决依赖问题。这个事实上非常有风险的,假设某一个依赖包更改了接口,那么我们go get的时候可能会出问题了。 soundcloud使用了一种vendor的方式进行依赖管理。事实上非常easy。就是把依赖的东西所有复制到自己的project以下,当做自己的代码来使用。 只是这个就须要定期的维护依赖包的更新了。 假设引入的是一个可执行包。在自己的project文件夹以下建立一个_vendor文件夹(这样go的相关tool比如go test就会忽略该文件夹的东西)。 把_vendor作为单独的GOPATH,比如,拷贝github.com/user/dep到_vendor/src/github.com/user/dep以下。 然后将_vendor增加自己的GOPATH中,例如以下:
假设引入的是一个库。那么将其放入vendor文件夹中。将vendor作为package的前缀,比如拷贝github.com/user/dep到vendor/user/dep,并更改所有的相关import语句。 由于我们并不须要频繁的对这些引入的project进行go get更新处理。所以大多数时候这样做都非常值。 我開始的时候也採用的是相似的做法,仅仅只是我不叫vendor,而叫做3rd,后来为了方便还是决定改成直接go get,尽管知道这样风险比較大。没准兴许使用godep可能是一个不错的解决的方法。 构建和部署soundcloud在开发过程中直接使用go build来构建系统,然后使用一个Makefile来处理正式的构建。 由于soundcloud主要部署非常多无状态的服务,相似Heroku提供了非常easy的一种方式:
这方面,我们直接使用一个简单的Makefile来构建系统,例如以下:
应用程序的公布採用最原始的scp到目标机器在重新启动的方式,只是如今正在測试使用salt来公布应用。而对于应用程序的启动,停止这些,我们则使用supervisor来进行管理。 总结总的来说,这篇文章非常具体的解说了用go进行产品开发过程中的非常多经验。希望对大家有帮助。 版权声明:本文博客原创文章。博客,未经同意,不得转载。 |
请发表评论