了解一下http2和h2c (HTTP/2 over TCP,HTTP/2 without TLS)。
http/1.1 的服務器
我們經常會在代碼中啟動一個http服務器,最簡單的http/1.1服務器如下所示:
http.Handle("/foo", fooHandler) http.HandleFunc("/bar", func(w http.ResponseWriter, r *http.Request) { fmt.Fprintf(w, "Hello, %q", html.EscapeString(r.URL.Path)) }) log.Fatal(http.ListenAndServe(":8080", nil))
使用Go開發web服務非常的簡單,快速。
http/1.1 的服務器 with TLS
如果想讓http/1.1服務器支持TLS, 可以使用如下的代碼:
http.Handle("/foo", fooHandler) http.HandleFunc("/bar", func(w http.ResponseWriter, r *http.Request) { fmt.Fprintf(w, "Hello, %q", html.EscapeString(r.URL.Path)) }) log.Fatal(http.http.ListenAndServeTLS(":443", "server.crt", "server.key",nil))
至於 server.crt 和 server.key ,你可以使用你從CA購買的證書,你也可以使用下面的測試證書。
為了測試,你可以創建CA證書和你的服務器使用的證書。
1、 創建CA證書
$ openssl genrsa -out rootCA.key 2048 $ openssl req -x509 -new -nodes -key rootCA.key -days 1024 -out rootCA.pem
然后把 rootCA.pem 加到你的瀏覽器的證書中
2、 創建證書
$ openssl genrsa -out server.key 2048 $ openssl req -new -key server.key -out server.csr $ openssl x509 -req -in server.csr -CA rootCA.pem -CAkey rootCA.key -CAcreateserial -out server.crt -days 500
免費證書
如果你不想從CA花錢購買證書, 也不想配置測試證書,那么你可以使用 let's encrypt 的免費證書, 而且 let's encrypt 目前支持通配符證書,使用也是很方便的。
Go的擴展包中提供了 let's encrypt 的支持。
package main import ( "crypto/tls" "log" "net/http" "golang.org/x/crypto/acme/autocert" ) func main() { certManager := autocert.Manager{ Prompt: autocert.AcceptTOS, HostPolicy: autocert.HostWhitelist("example.com"), Cache: autocert.DirCache("certs"), } http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { w.Write([]byte("Hello world")) }) server := &http.Server{ Addr: ":443", TLSConfig: &tls.Config{ GetCertificate: certManager.GetCertificate, }, } go http.ListenAndServe(":80", certManager.HTTPHandler(nil)) log.Fatal(server.ListenAndServeTLS("", "")) //Key and cert are coming from Let's Encrypt }
或者更簡單的:
log.Fatal(http.Serve(autocert.NewListener("example.com"), handler))
看上面的例子, 把 example.com 換成你的域名,證書暫存在 certs 文件夾。autocert會定期自動刷新,避免證書過期。它會自動申請證書,並進行驗證。
不過比較遺憾的是, autocert目前不支持通配符域名。
HostWhitelist returns a policy where only the specified host names are allowed. Only exact matches are currently supported. Subdomains, regexp or wildcard will not match.
通配符(ACME v2)的支持也已經完成了,但是遲遲未通過review,所以你暫時還不能使用這個特性。 ( issue#21081 )
HTTP/2
Go 在 1.6的時候已經支持 HTTP/2 了, 1.8 開始支持 PUSH 功能,你什么時候開始采用 HTTP/2 的呢?
Go的http/2使用也非常簡單,但是必須和TLS一起使用。
package main import ( "log" "net/http" "time" "golang.org/x/net/http2" ) const idleTimeout = 5 * time.Minute const activeTimeout = 10 * time.Minute func main() { var srv http.Server //http2.VerboseLogs = true srv.Addr = ":8972" http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { w.Write([]byte("hello http2")) }) http2.ConfigureServer(&srv, &http2.Server{}) go func() { log.Fatal(srv.ListenAndServeTLS("server.crt", "server.key")) }() select {} }
http2 封裝並隱藏了http/2的處理邏輯,對於用戶來說,可以不必關心內部的具體實現,想http/1.1一樣簡單的使用即可。
這里的證書可以使用上面提到證書,或者你購買的1證書,或者免費 let's encrypt 證書。
h2c
上面我們說Go的http/2必須使用TLS是不嚴謹的,如果你想不使用TLS,你可以使用最近添加的h2c功能。
package main import ( "fmt" "log" "net/http" "golang.org/x/net/http2" "golang.org/x/net/http2/h2c" ) func main() { mux := http.NewServeMux() mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { fmt.Fprint(w, "Hello h2c") }) s := &http.Server{ Addr: ":8972", Handler: mux, } http2.ConfigureServer(s, &http2.Server{}) log.Fatal(s.ListenAndServe()) }
使用起來也很簡單,單數目前瀏覽器對http/2都是采用TLS的方式,所以用瀏覽器訪問這個服務的話會退化為http/1.1的協議,測試的話你可以使用Go實現客戶端的h2c訪問。
客戶端代碼如下:
package main import ( "crypto/tls" "fmt" "log" "net" "net/http" "golang.org/x/net/http2" ) func main() { client := http.Client{ // Skip TLS dial Transport: &http2.Transport{ AllowHTTP: true, DialTLS: func(network, addr string, cfg *tls.Config) (net.Conn, error) { return net.Dial(network, addr) }, }, } resp, err := client.Get("http://localhost:8972") if err != nil { log.Fatal(fmt.Errorf("error making request: %v", err)) } fmt.Println(resp.StatusCode) fmt.Println(resp.Proto) }
這個功能的討論2016年就開始了( issue#14141 ),最終5月份的這個功能加上了,這樣你就可以在沒有TLS使用http/2高性能的特性了。
雖然有一些第三方的實現,但是顯然使用官方庫是更好的選擇。
了解一下http2和h2c (HTTP/2 over TCP,HTTP/2 without TLS)。
