近期看了一篇关于go产品开发最佳实践的文章,go-in-procution。作者总结了他们在用go开发过程中的非常多实际经验,我们非常多事实上也用到了。鉴于此,这里就简单的写写读后感,兴许我也争取能将这篇文章翻译出来。后面我用soundcloud来指代原作者。

开发环境

在soundcloud,每一个人使用一个独立的GOPATH,而且在GOPATH直接依照go规定的代码路径方式clone代码。

$ mkdir -p $GOPATH/src/github.com/soundcloud
$ cd $GOPATH/src/github.com/soundcloud
$ git clone git@github.com:soundcloud/roshi

对于go来说,通常的project管理应该是例如以下的文件夹结构:

proj/
src/
modulea/
a.go
moudleb/
b.go
app/
main.go
pkg/
bin/

然后我们在GOPATH里面将proj的路径设置上去。这样就能够进行编译执行了。

这本来没啥,可是假设我们要将其代码提交到github。并同意另外的开发人员使用。我们就不能将整个proj的东西提交上面,假设提交了,就非常蛋疼了。外面的开发人员可能这么引用:

import "github.com/yourname/proj/src/modulea"

可是我们自己在代码里面就能够直接:

import "github.com/yourname/proj/modulea"

假设外面的开发人员须要依照去掉src的引用方式,仅仅能把GOPATH设置到proj文件夹,假设import的多了,会让人崩溃的。

我曾今也被这事情给折腾了好久,最终再看了vitess的代码之后。发现了上面这样的方式。认为非常不错。

project文件夹结构

假设一个项目中文件数量不是非常多,直接放在main包里面即可了,不须要在拆分成多个包了。譬如:

github.com/soundcloud/simple/
README.md
Makefile
main.go
main_test.go
support.go
support_test.go

假设真的有公共的类库,在拆分成单独的包处理。

有时候,一个project可能会包含多个二进制应用。

譬如,一个job可能须要一个server,一个worker或者一个janitor,在这样的情况下,建立多个子文件夹作为不同的main包,分别放置不同的二进制应用。

同一时候使用另外的子文件夹实现公共的函数。

github.com/soundcloud/complex/
README.md
Makefile
complex-server/
main.go
main_test.go
handlers.go
handlers_test.go
complex-worker/
main.go
main_test.go
process.go
process_test.go
shared/
foo.go
foo_test.go
bar.go
bar_test.go

这点我的做法略微有一点不一样,主要是參考vitess,我喜欢建立一个总的cmd文件夹,然后再在里面设置不同的子文件夹,这样外面就不须要推測这个文件夹是库还是应用。

代码风格

代码风格这没啥好说的,直接使用gofmt解决,通常我们也约定gofmt的时候不带不论什么其它參数。

最好将你的编辑器配置成保存代码的时候自己主动进行gofmt处理。

Google近期公布了go的代码规范,soundcloud做了一些改进:

  • 避免命名函数返回值,除非能明白的表明含义。
  • 尽量少用make和new。除非真有必要。或者预先知道须要分配的大小。
  • 使用struct{}作为标记值,而不是bool或者interface{}。

    譬如set我们就用map[string]struct{}来实现。而不是map[string]bool。

假设一个函数有多个參数,而且单行长度非常长,须要拆分。最好不用java的方式:

// Don't do this.
func process(dst io.Writer, readTimeout,
writeTimeout time.Duration, allowInvalid bool,
max int, src <-chan util.Job) {
// ...
}

而是使用:

func process(
dst io.Writer,
readTimeout, writeTimeout time.Duration,
allowInvalid bool,
max int,
src <-chan util.Job,
) {
// ...
}

相似的,当构造一个对象的时候,最好在初始化的时候就传入相关參数,而不是在后面设置:



f := foo.New(foo.Config{

Site: "zombo.com",

Out:  os.Stdout,

Dest: conference.KeyPair{

Key:   "gophercon",

Value: 2014,

},

})

// Don't do this.
f := &Foo{} // or, even worse: new(Foo)
f.Site = "zombo.com"
f.Out = os.Stdout
f.Dest.Key = "gophercon"
f.Dest.Value = 2014

假设一些变量是兴许通过其它操作才干获取的,我认为就能够在兴许设置了。

配置

soundcloud使用go的flag包来进行配置參数的传递,而不是通过配置文件或者环境变量。

flag的配置是在main函数里面定义的。而不是在全局范围内。

func main() {
var (
payload = flag.String("payload", "abc", "payload data")
delay = flag.Duration("delay", 1*time.Second, "write delay")
)
flag.Parse()
// ...
}

关于使用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的方式来进行集成測试,例如以下:

// +build integration

var fooAddr = flag.String(...)

func TestToo(t *testing.T) {
f, err := foo.Connect(*fooAddr)
// ...
}

由于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中,例如以下:

GO ?= go
GOPATH := $(CURDIR)/_vendor:$(GOPATH) all: build build:
$(GO) build

假设引入的是一个库。那么将其放入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的一种方式:

$ git push bazooka master
$ bazooka scale -r <new> -n 4 ...
$ # validate
$ bazooka scale -r <old> -n 0 ...

这方面,我们直接使用一个简单的Makefile来构建系统,例如以下:

all: build 

build:
go install ${SRC} clean:
go clean -i ${SRC} test:
go test ${SRC}

应用程序的公布採用最原始的scp到目标机器在重新启动的方式,只是如今正在測试使用salt来公布应用。而对于应用程序的启动,停止这些,我们则使用supervisor来进行管理。

总结

总的来说,这篇文章非常具体的解说了用go进行产品开发过程中的非常多经验。希望对大家有帮助。

版权声明:本文博客原创文章。博客,未经同意,不得转载。

读取生产环境go语言的最佳实践展示的更多相关文章

  1. 探寻ASP.NET MVC鲜为人知的奥秘(3):寻找多语言的最佳实践方式

    如果你的网站需要被世界各地的人访问,访问者会使用各种不同的语言和文字书写习惯,那么创建一个支持多语言的网站就是十分必要的了,这一篇文章就讲述怎么快速合理的创建网站对多语言的支持.接下来通过一个实例来讲 ...

  2. 15分钟从零开始搭建支持10w+用户的生产环境(四)

    上一篇文章,介绍了这个架构中,WebServer的选择,以及整个架构中扩展时的思路. 原文地址:15分钟从零开始搭建支持10w+用户的生产环境(三)   五.架构实践 前边用了三篇文章,详细介绍了这个 ...

  3. 阿里巴巴发布最佳实践 | 阿里巴巴DevOps实践指南

    编者按:本文源自阿里云云效团队出品的<阿里巴巴DevOps实践指南>,扫描上方二维码或前往:https://developer.aliyun.com/topic/devops,下载完整版电 ...

  4. 读生产环境下go语言最佳实践有感

    最近看了一篇关于go产品开发最佳实践的文章,go-in-procution.作者总结了他们在用go开发过程中的很多实际经验,我们很多其实也用到了,鉴于此,这里就简单的写写读后感,后续我也争取能将这篇文 ...

  5. Nacos: Namespace 和 Endpoint 在生产环境下的最佳实践

    随着使用 Nacos 的企业越来越多,遇到的最频繁的两个问题就是:如何在我的生产环境正确的来使用 namespace 以及 endpoint.这篇文章主要就是针对这两个问题来聊聊使用 nacos 过程 ...

  6. 生产环境容器落地最佳实践 --JFrog 内部K8s落地旅程

    引言 Kubernetes已经成为市场上事实上领先的编配工具,不仅对技术公司如此,对所有公司都是如此,因为它允许您快速且可预测地部署应用程序.动态地伸缩应用程序.无缝地推出新特性,同时有效地利用硬件资 ...

  7. Kubernetes生产环境最佳实践

    点击上方"开源Linux",选择"设为星标" 回复"学习"获取独家整理的学习资料! 众所周知,Kubernetes很难! 以下是在生产中使用 ...

  8. Dubbo Mesh 在闲鱼生产环境中的落地实践

    本文作者至简曾在 2018 QCon 上海站以<Service Mesh 的本质.价值和应用探索>为题做了一次分享,其中谈到了 Dubbo Mesh 的整体发展思路是“借力开源.反哺开源” ...

  9. 中小研发团队架构实践之生产环境诊断工具WinDbg 三分钟学会.NET微服务之Polly 使用.Net Core+IView+Vue集成上传图片功能 Fiddler原理~知多少? ABP框架(asp.net core 2.X+Vue)模板项目学习之路(一) C#程序中设置全局代理(Global Proxy) WCF 4.0 使用说明 如何在IIS上发布,并能正常访问

    中小研发团队架构实践之生产环境诊断工具WinDbg 生产环境偶尔会出现一些异常问题,WinDbg或GDB是解决此类问题的利器.调试工具WinDbg如同医生的听诊器,是系统生病时做问题诊断的逆向分析工具 ...

随机推荐

  1. 日积月累:ScrollView嵌套ListView只显示一行

    在开发的过程当中,由于手机屏幕的大小的限制,我们经常需要使用滑动的方式,来显示更多的内容.在最近的工作中,遇见一个需求,需要将ListView嵌套到ScrollView中显示.于是乎有了如下布局: & ...

  2. 求助(VC++) 隐藏Console窗体无效

    [逝去的100~~ 2014/10/07 20: 20] 程序想要实现控制台窗体的隐藏,可是窗体每次执行总会弹出来.为什么呢? 代码例如以下: // Mini.cpp : 定义控制台应用程序的入口点. ...

  3. Qt for Android 部署流程分析

    原地址:http://blog.csdn.net/foruok/article/details/17796017 今天为了测试使用 Qt Creator 3.0.0 开发的纯 C 工程,利用了 Win ...

  4. GDI+ Tutorial for Beginners

    原文 GDI+ Tutorial for Beginners GDI+ is next evolution of GDI. Using GDI objects in earlier versions ...

  5. 微信公 众平台开发,用于个人技术交流,有兴趣的加QQ群432921500

    微信公 众平台开发,用于个人技术交流,有兴趣的加QQ群432921500

  6. python 网络爬虫(二) BFS不断抓URL并放到文件中

    上一篇的python 网络爬虫(一) 简单demo 还不能叫爬虫,只能说基础吧,因为它没有自动化抓链接的功能. 本篇追加如下功能: [1]广度优先搜索不断抓URL,直到队列为空 [2]把所有的URL写 ...

  7. NYOJ 1085 数单词 (AC自己主动机模板题)

    数单词 时间限制:1000 ms  |  内存限制:65535 KB 难度:4 描写叙述 为了可以顺利通过英语四六级考试,如今大家每天早上都会早起读英语. LYH本来以为自己在6月份的考试中能够通过六 ...

  8. hdoj 2066 一个人的旅行 【多源多汇最短路】

    题目:hdoj 2066 一个人的旅行 方法:缩点 + 最短路 分析:看了大神的一篇博客,讲冗余压缩的,然后就想找一个多源最短路练练手. 这个题目就是典型的多源多汇最短路 方法:把全部的源点压缩成一个 ...

  9. CPU 球迷助威清理灰尘图形的全过程

    主机因为使用时间长的电源风扇,风扇轴承石油枯竭,导致拒绝或不转的风扇转速,热量使电源不能得到有效排除,往往会造成电脑死机,有几种方法来解决. 单省钱的办法例如以下: 1.把电源从主机上拆下,例如以下图 ...

  10. 屏幕编程 F4的帮组用法

    PROCESS ON VALUE-REQUEST. * 设置帮助(工作中心)  FIELD wa_zppt026-arbpl MODULE mdl_arbpl_f4. *&---------- ...