go標(biāo)準(zhǔn)庫net/http服務(wù)端的實現(xiàn)示例
1、http簡單使用
go的http標(biāo)準(zhǔn)庫非常強大,調(diào)用了兩個函數(shù)就能夠?qū)崿F(xiàn)一個簡單的http服務(wù):
func HandleFunc(pattern string, handler func(ResponseWriter, *Request)) func ListenAndServe(addr string, handler Handler) error
handleFunc注冊一個路由和相應(yīng)的處理函數(shù),第一個參數(shù)表示注冊的路由,第二個參數(shù)表示注冊路由對應(yīng)的處理函數(shù);ListenAndServe用來啟動http服務(wù)并監(jiān)聽,第一個參數(shù)是服務(wù)器地址,第二個參數(shù)表示使用的處理器。
下面是用這兩個函數(shù)實現(xiàn)的簡單的http服務(wù):注冊了一個“/”路由的處理函數(shù),并在8080端口啟動http服務(wù),ListenAndServe第二個參數(shù)為空表示使用標(biāo)準(zhǔn)庫默認(rèn)的處理器,也可使用自定義處理器,傳參即可。處理器的概念在下面標(biāo)準(zhǔn)庫分析中進(jìn)行介紹。
import (
"net/http"
)
func main() {
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
// TODO
})
http.ListenAndServe(":8080", nil)
}2、http標(biāo)準(zhǔn)庫分析
根據(jù)上面的兩個函數(shù)來對http標(biāo)準(zhǔn)庫展開分析
2.1、服務(wù)端數(shù)據(jù)結(jié)構(gòu)
首先介紹下這兩個函數(shù)涉及到的數(shù)據(jù)類型
(1)服務(wù)器對象,其中最核心的是Handler成員,表示整個http服務(wù)的路由器,存儲路由路徑對應(yīng)到處理函數(shù)的映射,可自定義,例如第1小姐中的案例,沒有自定義路由器對象,就會使用標(biāo)準(zhǔn)庫提供的默認(rèn)對象DefaultServeMux
type Server struct {
Addr string // 地址 host:port
Handler Handler // 處理器對象或路由器
// ...
}(2)Handler是一個接口,提供了ServeHTTP方法,用來將路由映射到相應(yīng)的處理函數(shù)上
type Handler interface {
ServeHTTP(ResponseWriter, *Request)
}(3)路由器對象ServeMux,用來存儲路由到處理函數(shù)的映射關(guān)系,該對象就是Handler接口的具體實現(xiàn)。
type serveMux struct {
mu sync.RWMutex
m map[string]muxEntry
es []muxEntry // slice of entries sorted from longest to shortest.
hosts bool // whether any patterns contain hostnames
}(4)muxEntry就是一個映射關(guān)系單元
type muxEntry struct {
h Handler
pattern string
}2.2、HandleFunc流程
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
// TODO
})根據(jù)上面的函數(shù)來分析標(biāo)準(zhǔn)庫的執(zhí)行流程,首先看HandleFunc相關(guān)的實現(xiàn):使用默認(rèn)的DefaultServeMux路由器對象,調(diào)用ServeMux的HandleFunc,最后路由的注冊是在mux.handle中實現(xiàn),其中mux.Handle(pattern, HandlerFunc(handler))中對處理器做了類型轉(zhuǎn)換,HandlerFunc 類型實現(xiàn)了ServeHTTP方法,所以被該類型轉(zhuǎn)換后的函數(shù)都是Handler對象的實例
var DefaultServeMux = &defaultServeMux
var defaultServeMux ServeMux
type HandlerFunc func(ResponseWriter, *Request)
func (f HandlerFunc) ServeHTTP(w ResponseWriter, r *Request) {
f(w, r)
}
func HandleFunc(pattern string, handler func(ResponseWriter, *Request)) {
DefaultServeMux.HandleFunc(pattern, handler)
}
func (mux *ServeMux) HandleFunc(pattern string, handler func(ResponseWriter, *Request)) {
if handler == nil {
panic("http: nil handler")
}
mux.Handle(pattern, HandlerFunc(handler))
}
func (mux *ServeMux) Handle(pattern string, handler Handler) {
mux.handle(pattern, handler)
}進(jìn)入到mux.handle中,會創(chuàng)建一個路由單元muxEntry對象,存儲相應(yīng)的路由和處理函數(shù),其中對于根路徑的存儲需要做出特殊處理,在muxEntry中通過es存儲,并按照順序存儲在muxEntry切片中,到此,已經(jīng)完成了路由注冊
func (mux *ServeMux) handle(pattern string, handler Handler) {
mux.mu.Lock()
defer mux.mu.Unlock()
if pattern == "" {
panic("http: invalid pattern")
}
if handler == nil {
panic("http: nil handler")
}
if _, exist := mux.m[pattern]; exist {
panic("http: multiple registrations for " + pattern)
}
if mux.m == nil {
mux.m = make(map[string]muxEntry)
}
e := muxEntry{h: handler, pattern: pattern}
mux.m[pattern] = e
if pattern[len(pattern)-1] == '/' {
mux.es = appendSorted(mux.es, e)
}
if pattern[0] != '/' {
mux.hosts = true
}
}
func appendSorted(es []muxEntry, e muxEntry) []muxEntry {
n := len(es)
i := sort.Search(n, func(i int) bool {
return len(es[i].pattern) < len(e.pattern)
})
if i == n {
return append(es, e)
}
es = append(es, muxEntry{})
copy(es[i+1:], es[i:])
es[i] = e
return es
}2.3、ListenAndServe流程
ListenAndServe先初始化一個Server對象,并綁定地址和路由器,調(diào)用Server的ListenAndServe方法,其中net.Listen("tcp", addr)用于創(chuàng)建一個監(jiān)聽套接字并開始監(jiān)聽指定網(wǎng)絡(luò)地址上的連接,返回一個實現(xiàn)了Listener接口的對象。關(guān)鍵是srv.Serve()
func ListenAndServe(addr string, handler Handler) error {
server := &Server{Addr: addr, Handler: handler}
return 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)
}在srv.Serve(ln)中使用onceCloseListener 對Listener進(jìn)行封裝,防止被多次關(guān)閉,context.WithValue用來將srv服務(wù)器對象信息存儲在context中,并使用for循環(huán)輪詢等待連接,l.Accept()會阻塞等待,直到連接到達(dá),并執(zhí)行conn.serve函數(shù)。
type onceCloseListener struct {
net.Listener
once sync.Once
closeErr error
}
type contextKey struct {
name string
}
ServerContextKey = &contextKey{"http-server"}
func (srv *Server) Serve(l net.Listener) error {
l = &onceCloseListener{Listener: l}
defer l.Close()
// ...
ctx := context.WithValue(baseCtx, ServerContextKey, srv)
for {
rw, err := l.Accept()
// ...
connCtx := ctx
// ...
c := srv.newConn(rw)
// ...
go c.serve(connCtx)
}
}其中newConn會將Accept的返回的net.Conn封裝成一個conn對象,對每個請求都會創(chuàng)建一個線程來處理,在conn.serve中會針對conn對象創(chuàng)建讀寫器并將內(nèi)容置入緩沖區(qū),在for中調(diào)用readRequest函數(shù)傳入上下文,在readRequest中讀取請求體req,并返回一個ResponseWriter的接口對象,用于向請求方返回響應(yīng),并在調(diào)用serverHandler的ServeHTTP方法
type conn struct {
server *Server
rwc net.Conn
// ...
}
func (c *conn) serve(ctx context.Context) {
c.remoteAddr = c.rwc.RemoteAddr().String()
ctx = context.WithValue(ctx, LocalAddrContextKey, c.rwc.LocalAddr())
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, _ := c.readRequest(ctx)
serverHandler{c.server}.ServeHTTP(w, w.req)
w.finishRequest()
...
}
}serverHandler的ServeHTTP方法用來根據(jù)路由分配handler,如果Server的Handler為空就是用默認(rèn)的DefaultServerMux,對應(yīng)上了文章一開始調(diào)用ListenAndServe的第二個參數(shù),如果為空就使用默認(rèn)路由器對象,最后調(diào)用路由器的ServeHTTP函數(shù)。
type serverHandler struct {
srv *Server
}
func (sh serverHandler) ServeHTTP(rw ResponseWriter, req *Request) {
handler := sh.srv.Handler
if handler == nil {
handler = DefaultServeMux
}
if !sh.srv.DisableGeneralOptionsHandler && req.RequestURI == "*" && req.Method == "OPTIONS" {
handler = globalOptionsHandler{}
}
handler.ServeHTTP(rw, req)
}接下來流程如下,依次調(diào)用返回命中的handler,如果沒有命中,則采用模糊匹配命中,最后調(diào)用handler的ServeHTTP函數(shù),因為注冊路由時候的函數(shù)在注冊時候被強轉(zhuǎn)成HandleFunc函數(shù)類型,該類型是實現(xiàn)ServeHTTP方法的,所以執(zhí)行handler的ServeHTTP方法就是執(zhí)行注冊路由是對應(yīng)的處理函數(shù)。
func (mux *ServeMux) ServeHTTP(w ResponseWriter, r *Request) {
h, _ := mux.Handler(r)
h.ServeHTTP(w, r)
}
func (mux *ServeMux) Handler(r *Request) (h Handler, pattern string) {
// ...
return mux.handler(host, r.URL.Path)
}
func (mux *ServeMux) handler(host, path string) (h Handler, pattern string) {
mux.mu.RLock()
defer mux.mu.RUnlock()
return mux.match(path)
}
func (mux *ServeMux) match(path string) (h Handler, pattern string) {
v, ok := mux.m[path]
if ok {
return v.h, v.pattern
}
for _, e := range mux.es {
if strings.HasPrefix(path, e.pattern) {
return e.h, e.pattern
}
}
return nil, ""
}到此這篇關(guān)于go標(biāo)準(zhǔn)庫net/http服務(wù)端的實現(xiàn)示例的文章就介紹到這了,更多相關(guān)go net/http服務(wù)端內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
golang獲取prometheus數(shù)據(jù)(prometheus/client_golang包)
本文主要介紹了使用Go語言的prometheus/client_golang包來獲取Prometheus監(jiān)控數(shù)據(jù),具有一定的參考價值,感興趣的可以了解一下2025-03-03
Go語言實現(xiàn)LRU算法的核心思想和實現(xiàn)過程
這篇文章主要介紹了Go語言實現(xiàn)LRU算法的核心思想和實現(xiàn)過程,LRU算法是一種常用的緩存淘汰策略,它的核心思想是如果一個數(shù)據(jù)在最近一段時間內(nèi)沒有被訪問到,那么在將來它被訪問的可能性也很小,因此可以將其淘汰,感興趣想要詳細(xì)了解可以參考下文2023-05-05
Go實現(xiàn)完全靜態(tài)編譯和交叉編譯的示例代碼
Go 語言天生支持跨平臺編譯,并且其標(biāo)準(zhǔn)庫幾乎不依賴系統(tǒng)動態(tài)庫,所以在大多數(shù)場景下,它編譯出來的二進(jìn)制文件幾乎可以直接丟到任何機(jī)器運行,但實際開發(fā)中,我們經(jīng)常遇到兩個問題,如何完全靜態(tài)編譯和交叉編譯,本文詳細(xì)的給大家介紹了解決方法,需要的朋友可以參考下2025-07-07

