GO隨筆-搭建一個Web服務器

用GO搭建一個Web服務器

有人曾和我說過,一門語言應該可以本身實現一個HTTP服務。PHP作不到,但GO卻垂手可得。
只須要幾行代碼,GO就能夠實現一個簡單的HTTP服務。nginx

package main

import (
    "fmt"
    "net/http"
)

func IndexHandler(w http.ResponseWriter, r *http.Request) {
    fmt.Fprintln(w, "hello world")
}

func main() {
    http.HandleFunc("/", IndexHandler)
    http.ListenAndServe(":8001", nil)
}

這樣,便可監聽8001端口,而且當有請求訪問的時候完成響應。(這幾乎就完成了nginx的主要功能,不過沒有作負載平衡)
觀察這段代碼,咱們基本可以從命名大概瞭解每一個函數的做用。服務器

http.HandleFunc("/", IndexHandler)

這至關於綁定url的函數,當請求的url是"/"時,那麼將這個請求交給IndexHandler來處理。cookie

http.ListenAndServe(":8001", nil)

顧名思義,監聽8001端口而且啓動服務。併發

func IndexHandler(w http.ResponseWriter, r *http.Request) {
    fmt.Fprintln(w, "hello world")
}

處理函數,當有請求的時候輸出hello world。
經過以上分析,GO搭建一個HTTP服務只需用到一個net/http包便可。同時結合上篇文章,要完成http服務須要如下幾點tcp

  • Request 用戶請求的信息,用來解析用戶的請求信息,包括post、get、cookie、url等信息
  • Response 服務端反饋給客戶端的信息
  • Conn 用戶每次請求的鏈接
  • Handler 處理請求和生產返回信息的處理邏輯

前3點是一個HTTP服務必需要的結構,第4點是每個HTTP服務的核心所在。
咱們只須要了解3個問題,就知道GO是如何將Web服務運做起來。函數

  • 如何監聽接口
  • 如何接受客戶端請求
  • 如何分配handle

如何監聽接口

GO是經過一個函數 ListenAndServe作到的。
首先初始化一個server對象,而後調用net.Listen("tcp", addr)底層創建TCP鏈接,監聽咱們設置的端口。高併發

如何接收客戶端請求

這塊的源碼post

func (srv *Server) Serve(l net.Listener) error {
    defer l.Close()
    var tempDelay time.Duration // how long to sleep on accept failure
    for {
        rw, e := l.Accept()
        if e != nil {
            if ne, ok := e.(net.Error); ok && ne.Temporary() {
                if tempDelay == 0 {
                    tempDelay = 5 * time.Millisecond
                } else {
                    tempDelay *= 2
                }
                if max := 1 * time.Second; tempDelay > max {
                    tempDelay = max
                }
                log.Printf("http: Accept error: %v; retrying in %v", e, tempDelay)
                time.Sleep(tempDelay)
                continue
            }
            return e
        }
        tempDelay = 0
        c, err := srv.newConn(rw)
        if err != nil {
            continue
        }
        go c.serve()
    }
}

這段代碼首先起了一個for()。接收請求Accept(),如有請求,則創建一個Conn鏈接,並用協程啓動服務。這時再次進入下一個循環,如有請求則新創建一個鏈接並啓動一個協程服務。若沒有請求,則休息一段時間後再次循環。正是利用了go協程特性,用戶的每個請求都有一個新的goroutine去服務,互不影響,達到了自然支持高併發特性。url

如何分配handle

在創建conn以後,conn會首先解析request,c.readRequest(),而後得到相應的handle:handler := c.server.Handler,也就是咱們剛纔在調用函數ListenAndServe時候的第二個參數,咱們前面例子傳遞的是nil,也就是爲空,那麼默認獲取handler = DefaultServeMux,那麼這個變量用來作什麼的呢?對,這個變量就是一個路由器,它用來匹配url跳轉到其相應的handle函數,那麼這個咱們有設置過嗎?有,咱們調用的代碼裏面第一句不是調用了http.HandleFunc("/", IndexHandler)嘛。這個做用就是註冊了請求/的路由規則,當請求uri爲"/",路由就會轉到函數IndexHandlerDefaultServeMux會調用ServeHTTP方法,這個方法內部其實就是調用IndexHandler自己,最後經過寫入response的信息反饋到客戶端。code

相關文章
相關標籤/搜索