這是一個建立於 的文章,其中的資訊可能已經有所發展或是發生改變。
譯自 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
)會優雅地關閉。
1234567891011121314151617181920212223242526272829303132333435363738394041424344 |
package mainimport ("context""io""log""net/http""os""os/signal""time")func main() {// subscribe to SIGINT signalsstopChan := make(chan os.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}go func() {// service connectionsif err := srv.ListenAndServe(); err != nil {log.Printf("listen: %s\n", err)}}()<-stopChan // wait for SIGINTlog.Println("Shutting down server...")// shut down gracefully, but wait no longer than 5 seconds before haltingctx, _ := context.WithTimeout(context.Background(), 5*time.Second)srv.Shutdown(ctx)log.Println("Server gracefully stopped")} |
一旦收到SIGINT
訊號,伺服器會立即停止接受新的串連,srv.ListenAndServe()
會返回http.ErrServerClosed
。srv.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
:
123456789101112131415161718192021222324 |
package mainimport "net/http"func main() {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.Pusherif p, ok := w.(http.Pusher); ok {p.Push("/static/gopher.png", nil}}// load the main pagew.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 訪問的結果:
明顯地可以在 Initiator 那一列中看到 gopher.png
被 Push
,你也可以看到標藍色的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多多,我甚至不能寫一個正常的程式來測試它,但是假定它的使用應該如下面的樣子:
1234567891011 |
// hexify.gopackage mainimport "encoding/hex"func Hexify(in string) string {return hex.EncodeToString([]byte(in))}$ go build -buildmode=shared hexify.go// produces hexify.so |
1234567891011 |
// main.gopackage mainimport "plugin"func main() {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已經被廣泛地使用,將所有的使用者的代碼統一轉換很困難,因此允許這兩個包通用很有必要。
別名的定義如下:
這個文法定義 Foo
是 pkg.Bar
別名。Foo
可以用在任何pkg.Bar
出現的地方。以上個例子為例,任何需要類型golang.org/x/net/context
的地方都可以用標準庫context
代替,它們是等價的。
別名也可以用在常量、變數、函數等類型上。
這是一個很有爭議的特性,可以參考issue 16339和golang-dev post 看大家的討論。因為它從Go 1.8中移除了,大家可以暫時不用關注這個特性了。
新的slice排序API
統一的slice排序由新的 sort.Slice 函數實現。它允許任意的slice都可以被排序,只需提供一個回調比較函數即可,而不是像以前要提供一個特定的sort.Interface
的實現。這個函數沒有傳回值。想其它的排序函數一樣,它提供了原地的排序。
下面的例子根據海拔高度排序知名山峰的slice。
12345678910111213141516171819202122 |
type Peak struct {Name stringElevation 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 firstsort.Slice(peaks, func(i, j int) bool {return peaks[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現在有了strict 模式.
- 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
提供了更多的逾時設定,比如ReadHeaderTimeout
、IdleTimeout
。
一個完整的改動列表:Go 1.8