99re热这里只有精品视频,7777色鬼xxxx欧美色妇,国产成人精品一区二三区在线观看,内射爽无广熟女亚洲,精品人妻av一区二区三区

Go 如何使得Web工作

2022-05-13 17:51 更新

前面介紹了如何通過Go搭建一個Web服務(wù),我們可以看到簡單應(yīng)用一個net/http包就方便的搭建起來了。那么Go在底層到底是怎么做的呢?萬變不離其宗,Go的Web服務(wù)工作也離不開我們前面介紹的Web工作方式。

web工作方式的幾個概念

以下均是服務(wù)器端的幾個概念

 Request:用戶請求的信息,用來解析用戶的請求信息,包括post、get、cookie、url等信息

Response:服務(wù)器需要反饋給客戶端的信息

Conn:用戶的每次請求鏈接

Handler:處理請求和生成返回信息的處理邏輯

分析http包運行機制

如下圖所示,是Go實現(xiàn)Web服務(wù)的工作模式的流程圖

GO WEB流程圖

http包執(zhí)行流程:

  1. 創(chuàng)建Listen Socket, 監(jiān)聽指定的端口, 等待客戶端請求到來。
  2. Listen Socket接受客戶端的請求, 得到Client Socket, 接下來通過Client Socket與客戶端通信。
  3. 處理客戶端的請求, 首先從Client Socket讀取HTTP請求的協(xié)議頭, 如果是POST方法, 還可能要讀取客戶端提交的數(shù)據(jù), 然后交給相應(yīng)的handler處理請求, handler處理完畢準(zhǔn)備好客戶端需要的數(shù)據(jù), 通過Client Socket寫給客戶端。

這整個的過程里面我們只要了解清楚下面三個問題,也就知道Go是如何讓W(xué)eb運行起來了

  • 如何監(jiān)聽端口?
  • 如何接收客戶端請求?
  • 如何分配handler?

前面小節(jié)的代碼里面我們可以看到,Go是通過一個函數(shù)ListenAndServe來處理這些事情的,其實現(xiàn)源碼如下:

func ListenAndServe(addr string, handler Handler) error {
	server := &Server{Addr: addr, Handler: handler}
	return server.ListenAndServe()
}

ListenAndServe會初始化一個sever對象,然后調(diào)用了Server對象的方法ListenAndServe。其源碼如下:

func (srv *Server) ListenAndServe() error {
	if srv.shuttingDown() {
		return ErrServerClosed
	}
	addr := srv.Addr
	if addr == "" {
		addr = ":http"
	}
	ln, err := net.Listen("tcp", addr)
	if err != nil {
		return err
	}
	return srv.Serve(ln)
}

?ListenAndServe?調(diào)用了?net.Listen("tcp", addr)?,也就是底層用TCP協(xié)議搭建了一個服務(wù),最后調(diào)用?src.Serve?監(jiān)控我們設(shè)置的端口。監(jiān)控之后如何接收客戶端的請求呢?

?Serve?的具體實現(xiàn)如下(為突出重點,僅展示關(guān)鍵代碼),通過下面的分析源碼我們可以看到客戶端請求的具體處理過程:

func (srv *Server) Serve(l net.Listener) error {
	...

	ctx := context.WithValue(baseCtx, ServerContextKey, srv)
	for {
		rw, err := l.Accept()
		...

		connCtx := ctx
		if cc := srv.ConnContext; cc != nil {
			connCtx = cc(connCtx, rw)
			if connCtx == nil {
				panic("ConnContext returned nil")
			}
		}
		tempDelay = 0
		c := srv.newConn(rw)
		c.setState(c.rwc, StateNew, runHooks) // before Serve can return
		go c.serve(connCtx)
	}
}

這個函數(shù)里面起了一個?for{}?,首先通過Listener接收請求:?l.Accept()?,其次創(chuàng)建一個Conn:?c := srv.newConn(rw)?,最后單獨開了一個goroutine,把這個請求的數(shù)據(jù)當(dāng)做參數(shù)扔給這個conn去服務(wù):?go c.serve(connCtx)?。這個就是高并發(fā)體現(xiàn)了,用戶的每一次請求都是在一個新的goroutine去服務(wù),相互不影響。

那么如何具體分配到相應(yīng)的函數(shù)來處理請求呢?我們繼續(xù)分析conn的?serve?方法,其源碼如下(為突出重點,僅展示關(guān)鍵代碼):

func (c *conn) serve(ctx context.Context) {
    ...

	ctx, cancelCtx := context.WithCancel(ctx)
	c.cancelCtx = cancelCtx
	defer cancelCtx()

	c.r = &connReader{conn: c}
	c.bufr = newBufioReader(c.r)
	c.bufw = newBufioWriterSize(checkConnErrorWriter{c}, 4<<10)

	for {
		w, err := c.readRequest(ctx)
        ...

		// HTTP cannot have multiple simultaneous active requests.[*]
		// Until the server replies to this request, it can't read another,
		// so we might as well run the handler in this goroutine.
		// [*] Not strictly true: HTTP pipelining. We could let them all process
		// in parallel even if their responses need to be serialized.
		// But we're not going to implement HTTP pipelining because it
		// was never deployed in the wild and the answer is HTTP/2.
		serverHandler{c.server}.ServeHTTP(w, w.req)
		w.cancelCtx()
        ...

	}
}

conn首先會解析request:w, err := c.readRequest(ctx), 然后獲取相應(yīng)的handler去處理請求:serverHandler{c.server}.ServeHTTP(w, w.req),ServeHTTP的具體實現(xiàn)如下:

func (sh serverHandler) ServeHTTP(rw ResponseWriter, req *Request) {
	handler := sh.srv.Handler
	if handler == nil {
		handler = DefaultServeMux
	}
	if req.RequestURI == "*" && req.Method == "OPTIONS" {
		handler = globalOptionsHandler{}
	}
	handler.ServeHTTP(rw, req)
}

?sh.srv.Handler?就是我們剛才在調(diào)用函數(shù)?ListenAndServe?時候的第二個參數(shù),我們前面例子傳遞的是nil,也就是為空,那么默認(rèn)獲取?handler = DefaultServeMux?,那么這個變量用來做什么的呢?對,這個變量就是一個路由器,它用來匹配url跳轉(zhuǎn)到其相應(yīng)的handle函數(shù),那么這個我們有設(shè)置過嗎?有,我們調(diào)用的代碼里面第一句不是調(diào)用了?http.HandleFunc("/", sayhelloName)?嘛。這個作用就是注冊了請求/的路由規(guī)則,當(dāng)請求uri為"/",路由就會轉(zhuǎn)到函數(shù)sayhelloName,DefaultServeMux會調(diào)用ServeHTTP方法,這個方法內(nèi)部其實就是調(diào)用sayhelloName本身,最后通過寫入response的信息反饋到客戶端。

詳細(xì)的整個流程如下圖所示:


至此我們的三個問題已經(jīng)全部得到了解答,你現(xiàn)在對于Go如何讓W(xué)eb跑起來的是否已經(jīng)基本了解了呢?


以上內(nèi)容是否對您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號