剑客
关注科技互联网

[译]Go 1.8 新特性

译自 tylerchr 的 What’s Coming in Go 1.8

随着Go 1.8 新特性的开发工作已经冻结,Go 1.8 将在2017年2月左右发布,现在让我们看一些在Go 1.8更有趣的API的改变。

HTTP server connection draining

Brad Fitzpatrick最近 关闭了一个将近四年的issue ,这个issue请求实现 http.Server 的连接耗尽(draining)的功能。现在可以调用 srv.Close 可以立即停止 http.Server ,也可以调用 srv.Shutdown(ctx) 等待已有的连接处理完毕(耗尽,draining, github.com/tylerb/graceful 的用户应该熟悉这个特性)。

下面这个例子中,服务器当收到 SIGINT 信号后( ^C )会优雅地关闭。

packagemain

import(
"context"
"io"
"log"
"net/http"
"os"
"os/signal"
"time"
)

funcmain() {

// subscribe to SIGINT signals
 stopChan := make(chanos.Signal)
 signal.Notify(stopChan, os.Interrupt)

 mux := http.NewServeMux()

 mux.Handle("/", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
 time.Sleep(5* time.Second)
 io.WriteString(w, "Finished!")
 }))

 srv := &http.Server{Addr: ":8081", Handler: mux}

gofunc() {
// service connections
iferr := srv.ListenAndServe(); err !=nil{
 log.Printf("listen: %s/n", err)
 }
 }()

 <-stopChan // wait for SIGINT
 log.Println("Shutting down server...")

// shut down gracefully, but wait no longer than 5 seconds before halting
 ctx, _ := context.WithTimeout(context.Background(),5*time.Second)
 srv.Shutdown(ctx)

 log.Println("Server gracefully stopped")

}

一旦收到 SIGINT 信号,服务器会立即停止接受新的连接, srv.ListenAndServe() 会返回 http.ErrServerClosedsrv.Shutdown 会一直阻塞,直到所有未完成的request都被处理完以及它们底层的连接被关闭。

更复杂的处理可以通过 context 实现,例如使用 context.Timeout 实现最大的关闭等待时间。你可以尝试复制 https://github.com/tylerchr/examples/tree/master/draining 中的例子并实现它。

通过 http.Pusher 实现 HTTP/2.0 server push

HTTP/2.0 包含 Server Push 特性, 允许 HTTP/2 服务器主动地发送额外的 HTTP response 给客户端,即使客户端没有发送请求。目标是在客户端无需请求的情况下,服务器可以及时地将客户端所需的资源推送给客户端。可以查看wiki HTTP/2 Server Push 看具体的例子。

如果一个服务器支持 HTTP/2 , 提供给 handler 的 http.ResponseWriter 会实现 http.Pusher 接口。Handler 可以使用这个功能区触发Server Push, 虚拟的请求(synthetic request)可以被注册的 http.Server Handler所处理。

下面的程序处理 /index.html , 可以push一个 /static/gopher.png :

packagemain

import"net/http"

funcmain() {

 http.Handle("/static/", http.StripPrefix("/static/", http.FileServer(http.Dir("./static"))))

 http.Handle("/index.html", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {

// server push is available if w implements http.Pusher
ifp, ok := w.(http.Pusher); ok {
 p.Push("/static/gopher.png",nil}
 }

// load the main page
 w.Header().Set("Content-Type","text/html")
 w.Write([]byte(`<img src="/static/gopher.png" />`))

 }))

 http.ListenAndServeTLS(":4430","cert.pem","key.pem",nil)

}

你可以从 https://github.com/tylerchr/examples/serverpush 克隆这个例子,下面是在 Chrome 54 访问的结果:

[译]Go 1.8 新特性

明显地可以在 Initiator 那一列中看到 gopher.pngPush ,你也可以看到标蓝色的 gopher.png 先于 /index.html 被接收过来,这表明这个资源先于请求之前被推送到客户端。HTML下载完后 <img> 可以显示。

有人可能会问如何写一个测试用来校验实现 Server Push的 Handler。因为 http.NewTLSServer 没有启动 HTTP/2 服务器, httptest.ResponseRecorder 也没有实现 http.Pusher 。我的解决方案是包装 httptest.ResponseRecorder 实现 Pusher 接口,这里有个 例子

database/sql

database/sql 包有几个主要的改变,可以让用户更好的控制数据库查询,允许用户更好的利用数据库的特性。

  • 查询可以使用 context.Context 取消查询
  • 纯数据库列类型可以通过 sql.ColumnType 得到
  • 如果底层数据库支持,查询可以使用命名参数

更多的细节可以阅读Daniel Theophanes的文章 What is new in database/sql? ,他实现了大部分的改变。

plugin包实现动态插件

新增加的标准库 plugin 提供了初步的插件支持,它允许程序可以在运行的时候动态的加载插件。

但是这个库看起来还是bug多多,我甚至不能写一个正常的程序来测试它,但是假定它的使用应该如下面的样子:

// hexify.go
packagemain

import"encoding/hex"

funcHexify(instring)string{
returnhex.EncodeToString([]byte(in))
}

$ gobuild -buildmode=shared hexify.go
// produces hexify.so
// main.go
packagemain

import"plugin"

funcmain() {
 p, _ = plugin.Open("hexify.so")
 f := p.Lookup("Hexify")
 fmt.Println(f.(func(string)string)("gopher"))
// 676f70686572
}

在这个例子中, hexify.go 实现了 Hexify 函数,它被编译成一个共享库,第二个程序动态加载它。这允许Go程序可以不在编译的时候也能调用其它的库。

别名

别名(aliasing)曾被增加到 Go 1.8 的语言规范中,但是现在又被移除了,看这个说明: this post from Russ Cox ,有可能会出现在 Go 1.9中。

这个特性也引起了很多的争议,

指示符别名(Identifier aliasing)用来定义多个类型为同一个类型的语法。一个用处用来重构复杂的代码的时候,允许重新划分包而不必带来类型的不一致。 Ian Lance Taylor举了一个[例子](https://groups.google.com/d/msg/golang-dev/OmjsXkyOQpQ/OrcHWiGUBAAJ):

举个具体的例子,将扩展包 golang.org/x/net/context 移动到标准库 context 的过程。因为context已经被广泛地使用,将所有的用户的代码统一转换很困难,因此允许这两个包通用很有必要。

别名的定义如下:

typeFoo => pkg.Bar

这个语法定义 Foopkg.Bar 别名。 Foo 可以用在任何 pkg.Bar 出现的地方。以上个例子为例,任何需要类型 golang.org/x/net/context 的地方都可以用标准库 context 代替,它们是等价的。

别名也可以用在常量、变量、函数等类型上。

这是一个很有争议的特性,可以参考 issue 16339golang-dev post 看大家的讨论。因为它从Go 1.8中移除了,大家可以暂时不用关注这个特性了。

新的slice排序API

统一的slice排序由新的 sort.Slice 函数实现。它允许任意的slice都可以被排序,只需提供一个回调比较函数即可,而不是像以前要提供一个特定的 sort.Interface 的实现。这个函数没有返回值。想其它的排序函数一样,它提供了原地的排序。

下面的例子根据海拔高度排序知名山峰的slice。

typePeakstruct{
 Name string
 Elevation int// in feet
}

peaks := []Peak{
 {"Aconcagua",22838},
 {"Denali",20322},
 {"Kilimanjaro",19341},
 {"Mount Elbrus",18510},
 {"Mount Everest",29029},
 {"Mount Kosciuszko",7310},
 {"Mount Vinson",16050},
 {"Puncak Jaya",16024},
}

// does an in-place sort on the peaks slice, with tallest peak first
sort.Slice(peaks, func(i, jint)bool{
returnpeaks[i].Elevation >= peaks[j].Elevation
})

// peaks is now sorted

通过 sort.Interface 类型的 Len()Swap(i, j int) 提供了抽象的排序类型,这是以前的排序方法,而 Less(i, j int) 作为一个比较回调函数,可以简单地传递给 sort.Slice 进行排序。

其它

  • 87b1aaa encoding/base64 encoder现在有了严格模式.
  • 6ba5b32 expvar 暴露出来,可以用在其它的mux中.
  • 003a598 伪随机码可以通过 rand.Uint64() 产生 (先前仅支持uint32).
  • 67ea710 增加了一个新的 time.Until 函数,和 time.Since 对应.

net/http 故意只实现了使用TLS的HTTP/2,你可以查看[issue 14141] https://github.com/golang/go/issues/14141()了解细节。

sort.SliceStable 提供了稳定的slice排序,就像以前的 sort.Stable 一样。

译者增加的内容

Go 1.8 一个很大的特性就是性能的提升,包括二进制文件的大小、编译速度和运行速度。

并且非常大的提升就是提供小于100us GC暂停。

net/http 提供了更多的超时设置,比如 ReadHeaderTimeoutIdleTimeout

一个完整的改动列表: Go 1.8

分享到:更多 ()

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址