Golang開啟http服務的三種方式
前言
都說go標準庫實用,Api設計簡潔。這次就用go 標準庫中的net/http包實現一個簡潔的http web伺服器,包括三種版本。
v1最簡單版
直接使用http.HandleFunc(partern,function(http.ResponseWriter,
*http.Request){})
HandleFunc接受兩個引數,第一個為路由地址,第二個為處理方法。
//v1 func main() { http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { w.Write([]byte("httpserver v1")) }) http.HandleFunc("/bye", sayBye) log.Println("Starting v1 server ...") log.Fatal(http.ListenAndServe(":1210", nil)) } func sayBye(w http.ResponseWriter, r *http.Request) { w.Write([]byte("bye bye ,this is v1 httpServer")) }
v2自定義Handler
檢視標準庫原始碼,v1版本實際上是呼叫了handle方法,傳入的HandlerFunc實現了Handler的ServeHTTP方法,實際上是ServeHTTP在做http請求處理。

HandleFunc呼叫.png

HandleFunc實現Handler.png

Handler介面定義.png
由此我們可以自定義自己的Handler,v2版本程式碼如下:
// v2 func main() { mux := http.NewServeMux() mux.Handle("/", &myHandler{}) mux.HandleFunc("/bye", sayBye) log.Println("Starting v2 httpserver") log.Fatal(http.ListenAndServe(":1210", mux)) } type myHandler struct{} func (*myHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { w.Write([]byte("this is version 2")) } func sayBye(w http.ResponseWriter, r *http.Request) { w.Write([]byte("bye bye ,this is v2 httpServer")) }
v3自定義server配置
前面對Handler開了一次刀,下面我們看看http.ListenAndServe()中有些什麼祕密。

ListenAndServe.png
原來這裡可以自定義http伺服器配置,都在Server這個結構體中,這個物件能配置監聽地址埠,配置讀寫超時時間,配置handler,配置請求頭最大位元組數...,所有稍微改造一下v2的程式得到v3版:
// v3 func main() { mux := http.NewServeMux() mux.Handle("/", &myHandler{}) mux.HandleFunc("/bye", sayBye) server := &http.Server{ Addr:":1210", WriteTimeout: time.Second * 3,//設定3秒的寫超時 Handler:mux, } log.Println("Starting v3 httpserver") log.Fatal(server.ListenAndServe()) } type myHandler struct{} func (*myHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { w.Write([]byte("this is version 3")) } func sayBye(w http.ResponseWriter, r *http.Request) { // 睡眠4秒上面配置了3秒寫超時,所以訪問 “/bye“路由會出現沒有響應的現象 time.Sleep(4 * time.Second) w.Write([]byte("bye bye ,this is v3 httpServer")) }
拓展一下(如何平滑關閉http服務)
在go1.8中新增了一個新特性,利用Shutdown(ctx context.Context) 優雅地關閉http服務。
文件中描述:
Shutdown 將無中斷的關閉正在活躍的連線,然後平滑的停止服務。處理流程如下:
- 首先關閉所有的監聽;
- 然後關閉所有的空閒連線;
- 然後無限期等待連線處理完畢轉為空閒,並關閉;
- 如果提供了 帶有超時的Context,將在服務關閉前返回 Context的超時錯誤;
利用這個特性改造一下v3版本的程式,實現一個關閉http的提示
// 主動關閉伺服器 var server *http.Server func main() { // 一個通知退出的chan quit := make(chan os.Signal) signal.Notify(quit, os.Interrupt) mux := http.NewServeMux() mux.Handle("/", &myHandler{}) mux.HandleFunc("/bye", sayBye) server = &http.Server{ Addr:":1210", WriteTimeout: time.Second * 4, Handler:mux, } go func() { // 接收退出訊號 <-quit if err := server.Close(); err != nil { log.Fatal("Close server:", err) } }() log.Println("Starting v3 httpserver") err := server.ListenAndServe() if err != nil { // 正常退出 if err == http.ErrServerClosed { log.Fatal("Server closed under request") } else { log.Fatal("Server closed unexpected", err) } } log.Fatal("Server exited") } type myHandler struct{} func (*myHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { w.Write([]byte("this is version 3")) } // 關閉http func sayBye(w http.ResponseWriter, r *http.Request) { w.Write([]byte("bye bye ,shutdown the server"))// 沒有輸出 err := server.Shutdown(nil) if err != nil { log.([]byte("shutdown the server err")) } }
嘗試訪問 ofollow,noindex">http://localhost:1210/bye 在控制檯會得到以下提示結果,平滑關閉http服務成功:

成功平滑關閉.png