每日新闻

每日新闻

GoCN每日新闻资讯
有问必答

有问必答

Go相关的问题,技术相关的问题
文章分享

文章分享

技术文章分享,让知识传播给更多的人
招聘应聘

招聘应聘

为Gopher服务的招聘应聘平台

beego 1.7.1 新版本发布

开源程序astaxie 发表了文章 • 1 个评论 • 2315 次浏览 • 2016-10-10 16:47 • 来自相关话题

# beego 1.7.1 新增功能: 1. access log增加IP [#2156](https://github.com/astaxie/beego/pull/2156) 2. orm增加新接口ReadForUpda ...查看全部
# beego 1.7.1
新增功能:
1. access log增加IP [#2156](https://github.com/astaxie/beego/pull/2156)
2. orm增加新接口ReadForUpdate [#2158](https://github.com/astaxie/beego/pull/2158)
3. 参数bind支持数组form,columns[0].Data=foo&columns[1].Data=bar&columns[2].Data=baz [#2111](https://github.com/astaxie/beego/pull/2111)
4. 自定义recover函数,增加配置`beego.BConfig.RecoverFunc`,默认和原来保持一致,但是用户可以自己定义 [#2004](https://github.com/astaxie/beego/issues/2004)
5. memcache cache 同时支持byte和string的存储,这样就可以通过gob保存struct [#1521](https://github.com/astaxie/beego/issues/1521)
6. ORM delete支持按照指定条件删除 (#1802)[https://github.com/astaxie/beego/issues/1802]
7. swagger的支持输出yaml (#2162)[https://github.com/astaxie/beego/pull/2162]
8. 增加RunController 和 RunMethod,让用户自定义路由规则 (#2017)[https://github.com/astaxie/beego/issues/2017]

修复bug:
1. 静态目录如果已经存在index.html,当访问目录的时候不会自动添加/, 例如访问/swagger不会跳转到/swagger/,这样会导致相对的css和js访问不存在 [#2142](https://github.com/astaxie/beego/issues/2142)
2. beego admin ui里面访问时间排序没有安装us,ms排序,而是安装字符排序 [#1877](https://github.com/astaxie/beego/issues/1877)
3. captcha生产图片的时候,自定义height和width crash [#2161](https://github.com/astaxie/beego/issues/2161)
4. DELETE请求下开启了CopyBody情况下,如果body为空panic [#1656](https://github.com/astaxie/beego/issues/1656)


# beego 1.7.0
新增改进功能:
1. Filter访问速度提升7.5倍以上 [#1799](https://github.com/astaxie/beego/pull/1799)
2. Gzip压缩的时候支持不同的level [#1808](https://github.com/astaxie/beego/pull/1808)
3. ORM PK支持负数 [#1810](https://github.com/astaxie/beego/pull/1810)
4. ORM 支持自定义自增ID的值 [#1826](https://github.com/astaxie/beego/pull/1826)
5. Context 下载文件函数改进:下载文件之前先检查是否存在 [#1827](https://github.com/astaxie/beego/pull/1827)
6. log增加 `GetLogger`函数,可以增加相应的前缀 [#1832](https://github.com/astaxie/beego/pull/1832)

关于Go tools的比较有用的flags

Golangitfanr 发表了文章 • 0 个评论 • 6995 次浏览 • 2016-10-10 16:45 • 来自相关话题

你刚接触Go tools吗?或者你想扩展下你的知识面?这篇文章是关于Go tools的flags,这些flags每个人都应该知道。 免责声明:这篇文件可能有一些偏见。这是我个人常用的flags集合。我周边的人很难找到这些falgs的参 ...查看全部

你刚接触Go tools吗?或者你想扩展下你的知识面?这篇文章是关于Go tools的flags,这些flags每个人都应该知道。

免责声明:这篇文件可能有一些偏见。这是我个人常用的flags集合。我周边的人很难找到这些falgs的参考文档。如果你有更好的主意,可以在[Twitter][1]上私信我。

# $ go build -x


`-x`列出了go build触发的所有命令。

如果你对Go的工具链、使用跨平台编译器比较好奇,或者对传入外部编译器的flags不清楚,或者怀疑链接器有bug,那么使用`-x`来查看所有的触发。

# $ go build -x


```
WORK=/var/folders/00/1b8h8000h01000cxqpysvccm005d21/T/go-build600909754
mkdir -p $WORK/hello/perf/_obj/
mkdir -p $WORK/hello/perf/_obj/exe/
cd /Users/jbd/src/hello/perf
/Users/jbd/go/pkg/tool/darwin_amd64/compile -o $WORK/hello/perf.a -trimpath $WORK -p main -complete -buildid bbf8e880e7dd4114f42a7f57717f9ea5cc1dd18d -D _/Users/jbd/src/hello/perf -I $WORK -pack ./perf.go
cd .
/Users/jbd/go/pkg/tool/darwin_amd64/link -o $WORK/hello/perf/_obj/exe/a.out -L $WORK -extld=clang -buildmode=exe -buildid=bbf8e880e7dd4114f42a7f57717f9ea5cc1dd18d $WORK/hello/perf.a
mv $WORK/hello/perf/_obj/exe/a.out perf
```


# $go build -gcflags

用来给Go编译器传入参数。`go tool compile -help`列出了可以被传入编译器的所有的参数列表。

比如,为了禁止编译器优化和内联,你可以使用下面的gcfalgs:

```
$ go build -gcflags="-N -l"
```


# $go test -v


它提供了非正式的测试输出,打印了测试的名字、状态(通过或者失败)、耗时、测试用例的日志等。

不带有`-v`flag的go test命令非常安静,我经常把`-v`开关打开。比如输出如下:

```
$ go test -v context
=== RUN TestBackground
--- PASS: TestBackground (0.00s)
=== RUN TestTODO
--- PASS: TestTODO (0.00s)
=== RUN TestWithCancel
--- PASS: TestWithCancel (0.10s)
=== RUN TestParentFinishesChild
--- PASS: TestParentFinishesChild (0.00s)
=== RUN TestChildFinishesFirst
--- PASS: TestChildFinishesFirst (0.00s)
=== RUN TestDeadline
--- PASS: TestDeadline (0.16s)
=== RUN TestTimeout
--- PASS: TestTimeout (0.16s)
=== RUN TestCanceledTimeout
--- PASS: TestCanceledTimeout (0.10s)
...
PASS
ok context 2.426s
```

# $go test -race

[Go竞争检测工具][2]可以通过`--race`使用。go test也支持这个flag并且报告竞争。在开发阶段使用这个flag可以检测竞争。


# $go test -run

使用`-run`flag,你可以通过正则过滤测试用例。下面的命令会只测试[test examples][3]:

```
$ go test -run=Example
```

# $go test -coverprofile

你可以输出一个覆盖信息,如果你在测试一个包,然后使用go tool来在浏览器上实现可视化:


```
$ go test -coverprofile=c.out && go tool cover -html=c.out
```
上面的命令会创建一个覆盖信息,然后在浏览器上打开结果页面。可视化后的结果会类似下面的页面:

![此处输入图片的描述][4]


# $go test -exec

这是一个鲜为人知的特性,使用`-exec`这个flag,你可以用另外的程序和tools交互。这个flag允许你使用Go tool把一些工作代理到另外的程序。

使用这个flag常用的需求场景是:当你需要做更多的事情,而不是仅仅执行宿主机的程序。Go的[Android build][5],使用了`-exec`来推送测试二进制文件到Android设备(通过使用`adb`),并收集测试结果。可以作为一个参考。

# $go get -u

如果你执行go-test命令来获取一个已经在GOPATH中的包,那么go-get不好更新包到最新版本,而`-u`会强制tool同步这个仓库的最新的版本。

如果你是一个library的作者,那么你可能喜欢写你的安装说明通过`-u`flag,比如,[golin][6]t这样的方式:

```
$ go get -u github.com/golang/lint/golint
```

# $go get -d

如果你只想clone一个repo到GOPATH,跳过编译和安装过程,那么使用`-d`。它会下载包,然后在尝试编译和安装之前停止。

我经常使用它,作为git clone的替代命令,使用虚假的URLs,因为它会克隆这个repo到它合适的GOPATH。比如:

```
$ go get -d golang.org/x/oauth2/...
```

会克隆包到`$GOPATH/src/golang.org/x/ouath2`。给出的`golang.org/x/oauth2`是一个虚假的URL,go-get这个仓库是很有用的,而不是尝试知道知己的repo是什么(go.googlesource.com/oauth2)。

# $go get -t

如果你的包需要额外的包来测试,`-t`会允许你在go-get过程中下载它们。如果你不传入`-t`参数,go get会只下载非测试代码的依赖。

# $ go list -f

允许你下载Go包以一种自定义的格式。对写bash脚本非常有用。下面的命令会输出runtime包的依赖:

```
$ go list -f '{{.Deps}}' runtime
[runtime/internal/atomic runtime/internal/sys unsafe]
```

更多的格式化信息可以在[Dave Cheney的文章][7]的go list章节得到。

[英文原文][8]

[1]: https://twitter.com/rakyll
[2]: https://blog.golang.org/race-detector
[3]: https://blog.golang.org/examples
[4]: https://raw.githubusercontent.com/itfanr/articles-about-golang/master/2016-09/2016-09-27-1-1.png
[5]: https://github.com/golang/go/blob/master/misc/android/go_android_exec.go
[6]: https://github.com/golang/lint#installation
[7]: http://dave.cheney.net/2014/09/14/go-list-your-swiss-army-knife
[8]: http://golang.rakyll.org/go-tool-flags/

使用Go语言每分钟处理1百万请求

技术讨论astaxie 发表了文章 • 5 个评论 • 5526 次浏览 • 2016-10-10 16:25 • 来自相关话题

# 使用Go语言每分钟处理1百万请求(译) --- 在[Malwarebytes ][1]我们经历了显著的增长,自从我一年前加入了硅谷的公司,一个主要的职责成了设计架构和开发一些系统来支持一个快速增长的信息 ...查看全部
# 使用Go语言每分钟处理1百万请求(译)

---

在[Malwarebytes ][1]我们经历了显著的增长,自从我一年前加入了硅谷的公司,一个主要的职责成了设计架构和开发一些系统来支持一个快速增长的信息安全公司和所有需要的设施来支持一个每天百万用户使用的产品。我在反病毒和反恶意软件行业的不同公司工作了12年,从而我知道由于我们每天处理大量的数据,这些系统是多么复杂。

有趣的是,在过去的大约9年间,我参与的所有的web后端的开发通常是通过Ruby on Rails技术实现的。不要错怪我。我喜欢Ruby on Rails,并且我相信它是个令人惊讶的环境。但是一段时间后,你会开始以ruby的方式开始思考和设计系统,你会忘记,如果你可以利用多线程、并行、快速执行和小内存开销,软件架构本来应该是多么高效和简单。很多年期间,我是一个c/c++、Delphi和c#开发者,我刚开始意识到使用正确的工具可以把复杂的事情变得简单些。

作为首席架构师,我不会很关心在互联网上的语言和框架战争。我相信效率、生产力。代码可维护性主要依赖于你如何把解决方案设计得很简单。

# 问题

当工作在我们的匿名遥测和分析系统中,我们的目标是可以处理来自于百万级别的终端的大量的POST请求。web处理服务可以接收包含了很多payload的集合的JSON数据,这些数据需要写入Amazon S3中。接下来,map-reduce系统可以操作这些数据。

按照习惯,我们会调研服务层级架构,涉及的软件如下:

- Sidekiq
- Resque
- DelayedJob
- Elasticbeanstalk Worker Tier
- RabbitMQ
- and so on…

搭建了2个不同的集群,一个提供web前端,另外一个提供后端处理,这样我们可以横向扩展后端服务的数量。

但是,从刚开始,在 讨论阶段我们的团队就知道我们应该使用Go,因为我们看到这会潜在性地成为一个非常庞大( large traffic)的系统。我已经使用了Go语言大约2年时间,我们开发了几个系统,但是很少会达到这样的负载(amount of load)。

我们开始创建一些结构,定义从POST调用得到的web请求负载,还有一个上传到S3 budket的函数。

```
type PayloadCollection struct {
WindowsVersion string `json:"version"`
Token string `json:"token"`
Payloads []Payload `json:"data"`
}

type Payload struct {
// [redacted]
}

func (p *Payload) UploadToS3() error {
// the storageFolder method ensures that there are no name collision in
// case we get same timestamp in the key name
storage_path := fmt.Sprintf("%v/%v", p.storageFolder, time.Now().UnixNano())

bucket := S3Bucket

b := new(bytes.Buffer)
encodeErr := json.NewEncoder(b).Encode(payload)
if encodeErr != nil {
return encodeErr
}

// Everything we post to the S3 bucket should be marked 'private'
var acl = s3.Private
var contentType = "application/octet-stream"

return bucket.PutReader(storage_path, b, int64(b.Len()), contentType, acl, s3.Options{})
}
```

# 本地Go routines方法

刚开始,我们采用了一个非常本地化的POST处理实现,仅仅尝试把发到简单go routine的job并行化:

```
func payloadHandler(w http.ResponseWriter, r *http.Request) {

if r.Method != "POST" {
w.WriteHeader(http.StatusMethodNotAllowed)
return
}

// Read the body into a string for json decoding
var content = &PayloadCollection{}
err := json.NewDecoder(io.LimitReader(r.Body, MaxLength)).Decode(&content)
if err != nil {
w.Header().Set("Content-Type", "application/json; charset=UTF-8")
w.WriteHeader(http.StatusBadRequest)
return
}

// Go through each payload and queue items individually to be posted to S3
for _, payload := range content.Payloads {
go payload.UploadToS3() // <----- DON'T DO THIS
}

w.WriteHeader(http.StatusOK)
}
```

对于中小负载,这会对大多数的人适用,但是大规模下,这个方案会很快被证明不是很好用。我们期望的请求数,不在我们刚开始计划的数量级,当我们把第一个版本部署到生产环境上。我们完全低估了流量。

上面的方案在很多地方很不好。没有办法控制我们产生的go routine的数量。由于我们收到了每分钟1百万的POST请求,这段代码很快就崩溃了。

# 再次尝试

我们需要找一个不同的方式。自开始我们就讨论过, 我们需要保持请求处理程序的生命周期很短,并且进程在后台产生。当然,这是你在Ruby on Rails的世界里必须要做的事情,否则你会阻塞在所有可用的工作 web处理器上,不管你是使用puma、unicore还是passenger(我们不要讨论JRuby这个话题)。然后我们需要利用常用的处理方案来做这些,比如Resque、 Sidekiq、 SQS等。这个列表会继续保留,因为有很多的方案可以实现这些。

所以,第二次迭代,我们创建了一个缓冲channel,我们可以把job排队,然后把它们上传到S3。因为我们可以控制我们队列中的item最大值,我们有大量的内存来排列job,我们认为只要把job在channel里面缓冲就可以了。

```
var Queue chan Payload

func init() {
Queue = make(chan Payload, MAX_QUEUE)
}

func payloadHandler(w http.ResponseWriter, r *http.Request) {
...
// Go through each payload and queue items individually to be posted to S3
for _, payload := range content.Payloads {
Queue <- payload
}
...
}
```

接下来,我们再从队列中取job,然后处理它们。我们使用类似于下面的代码:

```
func StartProcessor() {
for {
select {
case job := <-Queue:
job.payload.UploadToS3() // <-- STILL NOT GOOD
}
}
}
```

说实话,我不知道我们在想什么。这肯定是一个满是Red-Bulls的夜晚。这个方法不会带来什么改善,我们用了一个 有缺陷的缓冲队列并发,仅仅是把问题推迟了。我们的同步处理器同时仅仅会上传一个数据到S3,因为来到的请求远远大于单核处理器上传到S3的能力,我们的带缓冲channel很快达到了它的极限,然后阻塞了请求处理逻辑的queue更多item的能力。

我们仅仅避免了问题,同时开始了我们的系统挂掉的倒计时。当部署了这个有缺陷的版本后,我们的延时保持在每分钟以常量增长。

![此处输入图片的描述][2]

# 最好的解决方案

我们讨论过在使用用Go channel时利用一种常用的模式,来创建一个二级channel系统,一个来queue job,另外一个来控制使用多少个worker来并发操作JobQueue。

想法是,以一个恒定速率并行上传到S3,既不会导致机器崩溃也不好产生S3的连接错误。这样我们选择了创建一个Job/Worker模式。对于那些熟悉Java、C#等语言的开发者,可以把这种模式想象成利用channel以golang的方式来实现了一个worker线程池,作为一种替代。

```
var (
MaxWorker = os.Getenv("MAX_WORKERS")
MaxQueue = os.Getenv("MAX_QUEUE")
)

// Job represents the job to be run
type Job struct {
Payload Payload
}

// A buffered channel that we can send work requests on.
var JobQueue chan Job

// Worker represents the worker that executes the job
type Worker struct {
WorkerPool chan chan Job
JobChannel chan Job
quit chan bool
}

func NewWorker(workerPool chan chan Job) Worker {
return Worker{
WorkerPool: workerPool,
JobChannel: make(chan Job),
quit: make(chan bool)}
}

// Start method starts the run loop for the worker, listening for a quit channel in
// case we need to stop it
func (w Worker) Start() {
go func() {
for {
// register the current worker into the worker queue.
w.WorkerPool <- w.JobChannel

select {
case job := <-w.JobChannel:
// we have received a work request.
if err := job.Payload.UploadToS3(); err != nil {
log.Errorf("Error uploading to S3: %s", err.Error())
}

case <-w.quit:
// we have received a signal to stop
return
}
}
}()
}

// Stop signals the worker to stop listening for work requests.
func (w Worker) Stop() {
go func() {
w.quit <- true
}()
}
```

我们已经修改了我们的web请求handler,用payload创建一个Job实例,然后发到JobQueue channel,以便于worker来获取。

```
func payloadHandler(w http.ResponseWriter, r *http.Request) {

if r.Method != "POST" {
w.WriteHeader(http.StatusMethodNotAllowed)
return
}

// Read the body into a string for json decoding
var content = &PayloadCollection{}
err := json.NewDecoder(io.LimitReader(r.Body, MaxLength)).Decode(&content)
if err != nil {
w.Header().Set("Content-Type", "application/json; charset=UTF-8")
w.WriteHeader(http.StatusBadRequest)
return
}

// Go through each payload and queue items individually to be posted to S3
for _, payload := range content.Payloads {

// let's create a job with the payload
work := Job{Payload: payload}

// Push the work onto the queue.
JobQueue <- work
}

w.WriteHeader(http.StatusOK)
}
```

在web server初始化时,我们创建一个Dispatcher,然后调用Run()函数创建一个worker池子,然后开始监听JobQueue中的job。

```
dispatcher := NewDispatcher(MaxWorker)
dispatcher.Run()
```

下面是dispatcher的实现代码:

```
type Dispatcher struct {
// A pool of workers channels that are registered with the dispatcher
WorkerPool chan chan Job
}

func NewDispatcher(maxWorkers int) *Dispatcher {
pool := make(chan chan Job, maxWorkers)
return &Dispatcher{WorkerPool: pool}
}

func (d *Dispatcher) Run() {
// starting n number of workers
for i := 0; i < d.maxWorkers; i++ {
worker := NewWorker(d.pool)
worker.Start()
}

go d.dispatch()
}

func (d *Dispatcher) dispatch() {
for {
select {
case job := <-JobQueue:
// a job request has been received
go func(job Job) {
// try to obtain a worker job channel that is available.
// this will block until a worker is idle
jobChannel := <-d.WorkerPool

// dispatch the job to the worker job channel
jobChannel <- job
}(job)
}
}
}
```

注意到,我们提供了初始化并加入到池子的worker的最大数量。因为这个工程我们利用了Amazon Elasticbeanstalk带有的docker化的Go环境,所以我们常常会遵守[12-factor][3]方法论来配置我们的生成环境中的系统,我们从环境变了读取这些值。这种方式,我们控制worker的数量和JobQueue的大小,所以我们可以很快的改变这些值,而不需要重新部署集群。

```
var (
MaxWorker = os.Getenv("MAX_WORKERS")
MaxQueue = os.Getenv("MAX_QUEUE")
)
```

# 直接结果

我们部署了之后,立马看到了延时降到微乎其微的数值,并未我们处理请求的能力提升很大。

![此处输入图片的描述][4]

Elastic Load Balancers完全启动后,我们看到ElasticBeanstalk 应用服务于每分钟1百万请求。通常情况下在上午时间有几个小时,流量峰值超过每分钟一百万次。

我们一旦部署了新的代码,服务器的数量从100台大幅 下降到大约20台。

![此处输入图片的描述][5]

我们合理配置了我们的集群和自动均衡配置之后,我们可以把服务器的数量降至4x EC2 c4.Large实例,并且Elastic Auto-Scaling设置为如果CPU达到5分钟的90%利用率,我们就会产生新的实例。

![此处输入图片的描述][6]


# 总结

在我的书中,简单总是获胜。我们可以使用多队列、后台worker、复杂的部署设计一个复杂的系统,但是我们决定利用Elasticbeanstalk 的auto-scaling的能力和Go语言开箱即用的特性简化并发。

我们仅仅用了4台机器,这并不是什么新鲜事了。可能它们还不如我的MacBook能力强大,但是却处理了每分钟1百万的写入到S3的请求。

处理问题有正确的工具。当你的 Ruby on Rails 系统需要更强大的web handler时,可以考虑下ruby生态系统之外的技术,或许可以得到更简单但更强大的替代方案。

[文章原文][7]


[1]: http://www.malwarebytes.org/
[2]: https://raw.githubusercontent.com/itfanr/articles-about-golang/master/2016-10/1-1.png
[3]: http://12factor.net/
[4]: https://raw.githubusercontent.com/itfanr/articles-about-golang/master/2016-10/1-2.png
[5]: https://raw.githubusercontent.com/itfanr/articles-about-golang/master/2016-10/1-3.png
[6]: https://raw.githubusercontent.com/itfanr/articles-about-golang/master/2016-10/1-4.png
[7]: http://marcio.io/2015/07/handling-1-million-requests-per-minute-with-golang/

谁知道有用go写的开源项目,用来扫描IP和端口的?

回复

有问必答astaxie 回复了问题 • 2 人关注 • 1 个回复 • 3235 次浏览 • 2016-10-14 10:44 • 来自相关话题

10.10 每日早报

技术讨论astaxie 发表了文章 • 0 个评论 • 1441 次浏览 • 2016-10-10 09:12 • 来自相关话题

新闻: 1.华为发布国内首份Android 7.0应用兼容报告,数千款经典应用中83%兼容 2.万达旅业整体并入同程,同程管理团队增资10亿保持投票权第一 3.Fill耳机音乐平台碎 ...查看全部
新闻:

1.华为发布国内首份Android 7.0应用兼容报告,数千款经典应用中83%兼容

2.万达旅业整体并入同程,同程管理团队增资10亿保持投票权第一

3.Fill耳机音乐平台碎乐App上线,集合部分分答与弹幕视频App功能

4.IT人才培训平台51CTO完成过亿元人民币B轮融资,华开投资领投
5.三星Gear VR中国区应用商店上线,Gear VR入华长路漫漫

6.爱回收正式开启品牌升级战略,推出估价、隐私和环保等三大体系

7.苹果欧洲首家iOS开发者学院开学,首批100名学生开始接受免费培训

8.食品一站式平台格格家完成1亿元A+轮融资,广发信德领投

资源:

2016年全球快速消费品电商研究报告 http://www.kantarworldpanel.com/dwl.php?sn=news_downloads&amp;id=1197  
2016年美国五大产业风险投资报告 http://martinprosperity.org/media/Startup-US-2016_Industrial-Clusters.pdf

注:上述内容来源于互联网,由EGO整理

第二届 Gopher China 大会

Golang傅小黑 发表了文章 • 0 个评论 • 1851 次浏览 • 2016-10-09 21:44 • 来自相关话题

又一次,和同事去参加 GopherChina 2016 大会,了解 Go 语言相关的最新动态。和一年前不同,Go 语言已经受到许多企业青睐。一些知名企业开始使用 Go 语言开发。因而,本届大会更多的内容注重在 Go 实现的业务场景和架构上。 ...查看全部
又一次,和同事去参加 GopherChina 2016 大会,了解 Go 语言相关的最新动态。和一年前不同,Go 语言已经受到许多企业青睐。一些知名企业开始使用 Go 语言开发。因而,本届大会更多的内容注重在 Go 实现的业务场景和架构上。

Go 与 高并发服务

Go 语言的 goroutine 特性,非常适合开发高并发网络服务。大会有几个题目聊起相关的内容。

百度前端接入团队分享了《Go在百度BFE的应用》。相关的内容其实在InfoQ有过分享。百度的服务体量太过巨大(日均千亿),代码优化手段 + Go的版本更新 对服务整体的提升作用不大,只能用特殊的措施 ———— 车轮大战。关闭 runtime 的GC,由代码根据目前程序的运行情况判断是否手动 runtime.GC()。以 master-worker的模式轮换正在服务和正在GC的程序。这种架构估计只有百度这种规模才用得上吧。但是私下的交流来说,小伙伴还是觉得 nginx + C 模块更适合。况且BFE之前那套也就是C写的,有足够的技术实力。

对比的来看是,吴小伟(skoo)的《Go在阿里云CDN系统的应用》。阿里 CDN 的网络接入系统还是 C 语言写的。CDN 的日志系统、调度系统和刷新系统是 Go 写的。这些业务对 Go 语言的 GC 不敏感,加上 Go 比 C 更简洁的语法特性,更快的开发效率,开发周围系统是很适合的。这里可以看到,同样是大流量系统,思考的角度也有不同。顺便说一下,skoo 是比较早研究 Go 语言的技术大神之一,博客有一些关于 Go 核心原理的文章。

Go 与 分布式服务

大会里的几个 Go 开发的分布式服务涉及数据库,存储,搜索。

刘奇的《Go在分布式数据库中的应用》主要是分享他主导开发的 TiDB 分布式数据库。TiDB 是基于 kv 存储的 SQL 分布式数据库。想一下,必然有 KV 存储层,KV 到 SQL 的转换,SQL 连接协议,以及分布式相关的模块。首先,使用 rust 开发了 TiKV 分布式 kv 存储系统,类似 HBase。然后使用 Go 开发兼容各种 kv 存储的 API 层,SQL 处理层, MySQL 协议层 和 分布式管理。TiDB 最核心的部分是 Placement Driver 分布式管理模块,负责路由数据存储的region,存储region的schema和region扩容复制及删除。关于数据库开发我没有什么经验,只能听听参考思路。

毛剑的《Go在数据存储上面的应用》参考 Facebook 的 Haystack paper 实现自己的小文件存储系统 bfs。存储系统一般的结构包括目录路由和存储节点。目录路由负责定位资源实际的存储位置。存储节点负责实际数据存储过程处理,比如合并。bfs 再加上了对外统一的 API 层 ———— 暴露简单的操作接口屏蔽细节, pitchfork 心跳监控层 ———— 保证节点可用性。毛大很细节的讲了各个模块的实现,及存储数据的流动过程、副本复制和节点灾备的问题。内容充足有条理,听的比较好而且可以参考学习的细节较多。

陈辉聊了一下《Go 与人工智能》。题目很大,主要的内容是分词算法、搜索引擎、抓取工具和机器学习。算是一般大数据研究需要的基础智能技术。wukong 搜索 和 sego 分词已经很早以前放出了。wukong 搜索是一个搜索引擎架子,有很好的定制化能力。除了基本的分词、索引和排序,还可以自己添加算法进行筛选。wukong搜索目前是全数据都加载到内存的。希望以后可以更方便的支持实时数据落地和读取,减小内存占用。

Go 与 容器

Docker 是 Go 的明星产品,但是已经形成自己的生态。单纯聊 Docker,就有很大一系列内容。大会的两个题目主要聊的内容是 Go 在 Docker 集群中的作为外在工具的角色。

小米高步双《Go在小米商城运维平台的应用与实践》很大的篇幅在说使用 Docker 搭建了 MAE(Mall App Engine) 集群满足小米商城的业务需求。Go 语言开发了 Docker 中的模块,集群 Router 和 Monitor。Docker 的 API 对 Go 友善。使用 Go 开发管理工具更方便控制 Docker 集群。演讲中提到了 fasthttp,比 net/http 性能更高。我以前有篇 文章 分析 fasthttp,它并不适合做 HTTP 长连接服务。另外还提及了一下 TCP 的 Multiplex Connection,令我想起了 HTTP2。

Daocloud 的孙宏亮对 Docker 有深刻的研究。更多的说 Daocloud 关于公共 Docker 集群的架构,关于 Go 的内容聊的比较少。孙老师对于 Go 操作进程和系统命令的能力很满意,使用 Go 开发了 Daocloud 很多的辅助功能工具,但是可惜没有深入介绍。

另一个容器化的明星是 CoreOS。大会上邓洪超的《Go在CoreOS分布式系统的性能调试和优化》很热情的介绍基于 CoreOS 的容器隔离体系。我并不熟悉 CoreOS 体系,这次的内容权当是科普。容器化的世界不仅仅是 Docker 的世界。去看看更多的技术开眼界也是极好的。

Go 与 Web

Go 的 HTTP 包已经足够实现 Web API 服务。但是 html/template 包的诡异语法对实现繁多复杂页面的 Web 站点并不是很好的选择。米嘉的演讲《Go build web》利用代码生成来满足对应的需求。路由部分继承 Gin 和 Goji 的中间件思路,数据库操作部分使用go generate命令自动生成结构体操作的代码,再开发了一个工具做start-kit的boilerplate做前端资源、热更新等的支持,这几个部分组成了完善的 Go Web 技术栈工具。其实目前很多新手是从 Web 方面开始学习 Go 语言。熟练使用或者自己参照实现类似的技术工具,还是很不靠谱的。如果真要学习,还可以去看看 goa 这个利用 Go 做 DSL 的代码生成工具。

Go 与 移动开发

沈晟沈老板为我们带来了《Golang在移动客户端开发中的应用》。沈老板在比较多的是说团队对 [gomobile] 的探索和尝试。目前 Go 参与 mobile 开发的方式是将一些算法库或者逻辑库编译成 c-archive 或者 .so 嵌入到 app 中,并不是代替 Obj-C 或者 Java 作为主力开发的角色。介绍的内容还都是概念性的演示,还没有实际案例。GopherCon India 有几个关于 mobile 演讲比这次更加激进一些,有兴趣的同学可以去 看看。

Go 的细节

Go 开发组的两位外国友人在更加细致的尺度上描述了 Go 语言的一些功能和特性。

Marcel van Lohuizen 主要介绍了 《I18n and L13n for Go using x/text》,golang.org/x/text 库的功能和计划。我并不熟悉文本编码方面的知识,但是看作者在各种字符集之间操作正确处理本地化差异的时候,所做的工作,感到由衷的敬佩。针对某个库某一些功能做了细致入微的研究,是国外技术人员很优秀的品质。而且演讲的内容丰富,很多细节很有意思,我觉得很有趣很好玩,从来没想过本地化和国际化还有这么多门道。

Dave Cheney 是 GB 版本管理工具的作者,Go 语言的 linker 的主要维护者,对 Go 语言细节有很深的认识。这次《Writing High Performance Go》从代码书写、调试、测试的层面帮助使用者提升技巧。比如如何避免 string 和 []byte 转换时的内存拷贝的影响,预想创建适当长度的 slice 避免扩容浪费,多使用 bufio 来操作字节流,思考和明确代码中 goroutine 的生命周期,使用队列池等方式控制 goroutine 的数量等。Slide 中列举了很多需要考虑的细节和使用注意,非常的赞。而且 slide 是使用 Go 的 present 工具生成的,一边展示一边直接操作代码,非常的直观。而且为了介绍 net/http/pprof 竟然在 present 工具开启了 pprof 为我们展示,表现力超赞。

这里可以发现很多时候我们不仅仅是需要面对各种复杂业务的规划和架构能力,还需要对使用的工具细致入微的操控能力。

Go 的持续集成

Grab 的 赵畅 的 《Golang项目的测试,持续集成以及部署策略》也是我觉得非常赞的一组内容。不仅描述 Go 的开发,测试,持续集成和部署,而且介绍创业公司对各种不同领域的云服务的应用。使用 gometalinter 检查代码规范和代码质量,使用 testify 简化测试逻辑,从 Travis CI 到自己搭建 Jenkins 的集成服务演进之路,还有 Scalyr 日志分析,Slack 团队项目管理。国外的团队对各种第三方的辅助工具有非常充分的利用,而国内创业企业还不愿意去尝试这些方式。

技术沙龙

第一天的大会后晚上举行了技术沙龙,有 Go vs Rust 和 Docker vs VM 的两场大战。

Go 和 Rust 的战斗集中在代码风格和社区文化上。Go 和 Rust 虽然都是近些年发展的语言,但是很多的思考看得出现在的语言的发展。 Go 秉承降低心智负担的基本原则,以最简单直接以至于简陋的方式来实现,比如 if err != nil 到处都是。这样逼迫使用者仔细思考每一步的问题,把error认为正常逻辑里的一环去深入考虑。而 Rust 使用 try(fn()) 和 fn()? 更优雅的处理错误也是大多数程序员希望的事情。谁也不愿意反复的写if err的语句。而且 try 这种方式也是借鉴其他语言的成功经验的。可以认为大多数不是心智太低,不需要像 Go 强制让你仔仔细细思考清楚的,还是更愿意接受稍微复杂了一点的 try 方式。Go 的 error 是 value 不是错误,这个哲学使它将 error 和其他正常值等价考虑,才有这样繁琐了一些的操作。try 的使用意味着这里在操作一个特殊的值 error,让大家注意。到底该如何对待 error,估计还是会有持久的论战。

Docker 容器化是新兴的分布式系统部署方案,VM 部署已经是成熟的解决方案。使用新兴的方案未必会带来足够好的效益,也是大家考虑担忧的一点。我觉得 Docker 适应微服务架构,重点除了更清晰的架构划分,更细粒度的资源控制外,还有就是可描述的架构模型。Docker 容器体系的编排和调度工具,就是对大规模应用有了一种可以描述的方式,就是编排和调度配置。这对于以后如何控制大规模的集群有重要意义。

很多大神们

BetaGo 和 毛剑 之前就认识,只是他俩坐下来就是在聊妹子,我呵呵呵呵呵。Asta谢作为主办者,还是一直忙前忙后没有停歇。刘奇 大神光亮的头顶非常的好辨认,郝林比我看到的照片里感觉更胖了哈哈。

我只是个普通的程序员,并不是大神。参加大会还是了解和学习,帮助自己做好工作,我就满足了。

Go编码规范指南

技术讨论astaxie 发表了文章 • 1 个评论 • 12623 次浏览 • 2016-10-09 21:09 • 来自相关话题

# Go编码规范指南 ## 序言 看过很多方面的编码规范,可能每一家公司都有不同的规范,这份编码规范是写给我自己的,同时希望我们公司内部同事也能遵循这个规范来写Go代码。 如果你的代 ...查看全部
# Go编码规范指南

## 序言

看过很多方面的编码规范,可能每一家公司都有不同的规范,这份编码规范是写给我自己的,同时希望我们公司内部同事也能遵循这个规范来写Go代码。

如果你的代码没有办法找到下面的规范,那么就遵循标准库的规范,多阅读标准库的源码,标准库的代码可以说是我们写代码参考的标杆。

## 格式化规范

go默认已经有了gofmt工具,但是我们强烈建议使用goimport工具,这个在gofmt的基础上增加了自动删除和引入包.

```
go get golang.org/x/tools/cmd/goimports

```

不同的编辑器有不同的配置, sublime的配置教程:[http://michaelwhatcott.com/gosublime-goimports/](http://michaelwhatcott.com/gosublime-goimports/)

LiteIDE默认已经支持了goimports,如果你的不支持请点击属性配置->golangfmt->勾选goimports

保存之前自动fmt你的代码。

## 行长约定

一行最长不超过80个字符,超过的请使用换行展示,尽量保持格式优雅。

## go vet

vet工具可以帮我们静态分析我们的源码存在的各种问题,例如多余的代码,提前return的逻辑,struct的tag是否符合标准等。

```
go get golang.org/x/tools/cmd/vet

```

使用如下:

```
go vet .

```

## package名字

保持package的名字和目录保持一致,尽量采取有意义的包名,简短,有意义,尽量和标准库不要冲突。

## import 规范

import在多行的情况下,goimports会自动帮你格式化,但是我们这里还是规范一下import的一些规范,如果你在一个文件里面引入了一个package,还是建议采用如下格式:

```
import (
"fmt"
)

```

如果你的包引入了三种类型的包,标准库包,程序内部包,第三方包,建议采用如下方式进行组织你的包:

```
import (
"encoding/json"
"strings"

"myproject/models"
"myproject/controller"
"myproject/utils"

"github.com/astaxie/beego"
"github.com/go-sql-driver/mysql"
)

```

有顺序的引入包,不同的类型采用空格分离,第一种实标准库,第二是项目包,第三是第三方包。

在项目中不要使用相对路径引入包:

```
// 这是不好的导入
import “../net”

// 这是正确的做法
import “github.com/repo/proj/src/net”

```

## 变量申明

变量名采用驼峰标准,不要使用`_`来命名变量名,多个变量申明放在一起

```
var (
Found bool
count int
)

```

在函数外部申明必须使用var,不要采用`:=`,容易踩到变量的作用域的问题。

## 自定义类型的string循环问题

如果自定义的类型定义了String方法,那么在打印的时候会产生隐藏的一些bug

```
type MyInt int
func (m MyInt) String() string {
return fmt.Sprint(m) //BUG:死循环
}

func(m MyInt) String() string {
return fmt.Sprint(int(m)) //这是安全的,因为我们内部进行了类型转换
}

```

## 避免返回命名的参数

如果你的函数很短小,少于10行代码,那么可以使用,不然请直接使用类型,因为如果使用命名变量很
容易引起隐藏的bug

```
func Foo(a int, b int) (string, ok){

}

```

当然如果是有多个相同类型的参数返回,那么命名参数可能更清晰:

```
func (f *Foo) Location() (float64, float64, error)

```

下面的代码就更清晰了:

```
// Location returns f's latitude and longitude.
// Negative values mean south and west, respectively.
func (f *Foo) Location() (lat, long float64, err error)

```

## 错误处理

错误处理的原则就是不能丢弃任何有返回err的调用,不要采用`_`丢弃,必须全部处理。接收到错误,要么返回err,要么实在不行就panic,或者使用log记录下来

### error 信息

error的信息不要采用大写字母,尽量保持你的错误简短,但是要足够表达你的错误的意思。

## 长句子打印或者调用,使用参数进行格式化分行

我们在调用`fmt.Sprint`或者`log.Sprint`之类的函数时,有时候会遇到很长的句子,我们需要在参数调用处进行多行分割:

下面是错误的方式:

```
log.Printf(“A long format string: %s %d %d %s”, myStringParameter, len(a),
expected.Size, defrobnicate(“Anotherlongstringparameter”,
expected.Growth.Nanoseconds() /1e6))

```

应该是如下的方式:

```
log.Printf(
“A long format string: %s %d %d %s”,
myStringParameter,
len(a),
expected.Size,
defrobnicate(
“Anotherlongstringparameter”,
expected.Growth.Nanoseconds()/1e6,
),


```

## 注意闭包的调用

在循环中调用函数或者goroutine方法,一定要采用显示的变量调用,不要再闭包函数里面调用循环的参数

```
fori:=0;i go func(){ DoSomething(i) }() //错误的做法
go func(i int){ DoSomething(i) }(i)//正确的做法
}

```

[http://golang.org/doc/articles/race_detector.html#Race_on_loop_counter](http://golang.org/doc/articles/race_detector.html#Race_on_loop_counter)

## 在逻辑处理中禁用panic

在main包中只有当实在不可运行的情况采用panic,例如文件无法打开,数据库无法连接导致程序无法
正常运行,但是对于其他的package对外的接口不能有panic,只能在包内采用。

强烈建议在main包中使用log.Fatal来记录错误,这样就可以由log来结束程序。

## 注释规范

注释可以帮我们很好的完成文档的工作,写得好的注释可以方便我们以后的维护。详细的如何写注释可以
参考:[http://golang.org/doc/effective_go.html#commentary](http://golang.org/doc/effective_go.html#commentary)

### bug注释

针对代码中出现的bug,可以采用如下教程使用特殊的注释,在godocs可以做到注释高亮:

```
// BUG(astaxie):This divides by zero.
var i float = 1/0

```

[http://blog.golang.org/2011/03/godoc­documenting­go­code.html](http://blog.golang.org/2011/03/godoc%C2%ADdocumenting%C2%ADgo%C2%ADcode.html)

## struct规范

### struct申明和初始化格式采用多行:

定义如下:

```
type User struct{
Username string
Email string
}

```

初始化如下:

```
u := User{
Username: "astaxie",
Email: "astaxie@gmail.com",
}

```

### recieved是值类型还是指针类型

到底是采用值类型还是指针类型主要参考如下原则:

```
func(w Win) Tally(playerPlayer)int //w不会有任何改变
func(w *Win) Tally(playerPlayer)int //w会改变数据

```

更多的请参考:[https://code.google.com/p/go-wiki/wiki/CodeReviewComments#Receiver_Type](https://code.google.com/p/go-wiki/wiki/CodeReviewComments#Receiver_Type)

### 带mutex的struct必须是指针receivers

如果你定义的struct中带有mutex,那么你的receivers必须是指针

## 参考资料

1. [https://code.google.com/p/go-wiki/wiki/CodeReviewComments](https://code.google.com/p/go-wiki/wiki/CodeReviewComments)

2. [http://golang.org/doc/effective_go.html](http://golang.org/doc/effective_go.html)

golang的goroutine是如何实现的?

回复

技术讨论astaxie 回复了问题 • 5 人关注 • 1 个回复 • 5562 次浏览 • 2016-10-09 20:26 • 来自相关话题

golang在处理网络高并发方面有哪些优势?

有问必答astaxie 回复了问题 • 2 人关注 • 1 个回复 • 2733 次浏览 • 2016-10-09 20:06 • 来自相关话题