Go Channel 详解

Channel是Go中的一个核心类型,你可以把它看成一个管道,通过它并发核心单元就可以发送或者接收数据进行通讯(communication)。

它的操作符是箭头 <-

1
2
ch <- v // 发送值v到Channel ch中
v := <-ch // 从Channel ch中接收数据,并将数据赋值给v

(箭头的指向就是数据的流向)

就像 map 和 slice 数据类型一样, channel必须先创建再使用:

1
ch := make(chan int)

阅读全文

Go泛型提案

Go泛型特性已经被讨论了多次了1, Andrew Gerrand现在把这个提案加入到了Go的issue列表中2,并标记为Go2标签。当然这不代表着要将泛型加入到Go中,而是演示一个完整的提案应该是什么样子。

阅读全文

使用Golang实现Futures 和 Promises

其它语言中Future和Promise的概念大量存在, 比如Node.js、Scala、Java、C#、C++ 11、Scheme、Swift等,可以方便的实现异步执行和回调。但是在Go语言的世界里,我们是通过goroutine/channel实现这种类似的功能呢,goroutine之间可以通过channel进行通讯, 但是,如果我们还是想使用Future/Promise的功能的话,该如何实现呢?

Future,Promise或Delay是用于并发编程的一种设计模式。它们表示一个对象,这个对象用来作为一次计算结果的代理,而该结果开始的时候是未知的,因为计算还没有完成。Promise与Future的区别在于,Future是Promise的一个只读的视图,也就是说Future没有设置任务结果的方法,只能获取任务执行结果或者为Future添加回调函数。

阅读全文

谁是最快的Go Web框架

前几天我写了一篇文章: 超全的Go Http路由框架性能比较,利用Julien Schmidt实现的benchmark测试框架对几乎所有的go web框架的路由功能进行了比较。我本来以为对Go web框架的性能考察就告以段落了,直到我写了一段简单的代码测试Irsi,用来模拟实际产品中的处理,才发现了Julien Schmidt测试框架的问题。

这段代码是这样的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
package main
import (
"fmt"
"os"
"strconv"
"time"
"github.com/kataras/iris"
)
func main() {
api := iris.New()
api.Get("/rest/hello", func(c *iris.Context) {
sleepTime, _ := strconv.Atoi(os.Args[1])
if sleepTime > 0 {
time.Sleep(time.Duration(sleepTime) * time.Millisecond)
}
c.Text("Hello world")
})
api.Listen(":8080")
}

当我将实际业务的处理时间模拟为10毫秒的时候,使用100并发进行测试:
wrk -t16 -c100 -d30s http://127.0.0.1:8080/rest/hello,Iris吞吐率才达到97 requests/second。详细介绍可以看我的文章: iris 真的是最快的Golang 路由框架吗?

虽然Iris的作者很快做了修改,临时解决了这个问题,但是也促使我重新审视Julien Schmidt测试框架,也促使我实现了一个测试Go web framework benchmak的框架: Go web framework benchmark

2016/04/12 updated: 现在Iris已经改成了fasthttp实现,性能超级好。

阅读全文

iris 真的是最快的Golang 路由框架吗?

依照我的前一篇文章(超全的Go Http路由框架性能比较)对各种Go http路由框架的比较, Iris明显胜出,它的性能远远超过其它Golang http路由框架。

但是,在真实的环境中,Iris真的就是最快的Golang http路由框架吗?

2016-04-05 更新: 我已经提交了一个Bug, 作者Makis已经做了一个临时的解决方案,性能已经恢复,所以准备使用Iris的读者不必担心。
根据我的测试,最新的Iris的测试如下:

  1. 在业务逻辑需要10毫秒时,吞吐率可以达到9281 request/s
  2. 在业务逻辑需要1000毫秒时,吞吐率可以达到95 request/s
    性能已经很不错了。

我会做一个其它路由框架的测试,看看其它的框架是否也有本文所说的问题。

阅读全文

如何得到goroutine 的 id?

使用Java的时候很容易得到线程的名字, 比如"Thread.currentThread().getName",这样就可以进行一些监控操作或者设置线程相关的一些数据。当转向Golang开发的时候,却发现Go语言并没有提供获取当前goroutine id的操作。这是Golang的开发者故意为之,避免开发者滥用goroutine id实现goroutine local storage (类似java的"thread-local" storage), 因为goroutine local storage很难进行垃圾回收。因此尽管以前暴露出了相应的方法,现在已经把它隐藏了。

Please don't use goroutine local storage. It's highly discouraged. In fact, IIRC, we used to expose Goid, but it is hidden since we don't want people to do this.

Potential problems include:

  1. when goroutine goes away, its goroutine local storage won't be GCed. (you can get goid for the current goroutine, but you can't get a list of all running goroutines)
  2. what if handler spawns goroutine itself? the new goroutine suddenly loses access to your goroutine local storage. You can guarantee that your own code won't spawn other goroutines,
    but in general you can't make sure the standard library or any 3rd party code won't do that.

thread local storage is invented to help reuse bad/legacy code that assumes global state, Go doesn't have legacy code like that, and you really should design your code so that state is passed explicitly and not as global (e.g. resort to goroutine local storage)

当然Go的这种隐藏的做法还是有争议的,有点因噎废食。在debug log的时候goroutine id是很好的一个监控信息。本文介绍了两种获取goroutine id的方法。

阅读全文

超全的Go Http路由框架性能比较

使用Go开发Web应用非常方便,它自己的路由器default request multiplexer超级简单,但是功能也有限,所幸net/http库的设计非常好,很容易实现自己定义的路由器,所以你如果在github搜一下,会找到很多的第三方的路由库。

但是这些路由库良莠不齐,尤其是早期实现的路由器,有些实现了很差的路由算法,有些没有仔细考虑内存的分配,导致垃圾回收的问题。

Julien Schmidt在实现HttpRouter库的时候将测试代码抽象出一个测试框架,用来测试Go的各种的路由器,测试的库相当的全。这个测试框架放在了github上。

对于架构师和Go Web开发人员来说,这个测试确实是一份值得参考的资料,在选择一款路由框架的时候非常有帮助。

路由是Go Web框架的一个功能,它可以将不同的URL映射到相应的处理方法上。一些库只实现了路由的功能,也有一些库实现了完整的Web框架的特性,如上下文管理,Session的维护,模版的处理,ORM等。本文只比较路由的性能。

阅读全文