http source code analysis service

http source code analysis service

Go to read the source code, can deepen understanding and awareness of the language go, to share today http relevant source code section
without using third-party libraries, we can easily achieve an http service with go,

package main

import (
    "fmt"
    "net/http"
)

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

func main() {
    http.HandleFunc("/", IndexHandler)
    if err := http.ListenAndServe(":9100", nil); err != nil {
        panic(err)
    }
}

Direct access to port 9100 in a browser can return hello world !
go to all the details have a good package, we only need to write your own Handler achieved enough. Source In simple terms do the following things:

  • Handler methods to add our custom default route DefaultServeMuxof the Map, such as: http.HandleFunc("/", IndexHandler)(BTW: Go not thread-safe language map, you can see the official handling the http source inside);
  • Start a tcp service listening 9100 port, waiting for http calls;
  • When the monitor has http call, start a coroutine to process the request, this is an important reason to go faster http service, converting content into http.Request request, the currently connected package http.RespnseWriter;
  • Default route DefaultServeMuxto find a corresponding path request Handler, and the request transmitted to responseWriter Handler business logic processing, response in response to write information to the client;

ServeMux & Handler

http default route packets DefaultServeMuxare ServeMuxstructural Hugh instance
http.HandleFunc("/", IndexHandler)calls, preservation methods will path information and information customized to DefaultServeMuxthe m map[string]muxEntryvariable where
we look at the ServeMuxdefinition:

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
}

type muxEntry struct {
    h       Handler
    pattern string
}

ServeMuxSave the pathand Handlerthe corresponding relations, as well as routing relations.

Handler

muxEntryThe h Handlerpair is Handler is our self-defined method, for example, our own example method func IndexHandler(w http.ResponseWriter, r *http.Request)Observant students may ask Handler is an interface, but we just define a method, this is how convert it?
Interface Halder set a signature rule processing method that is our custom

type Handler interface {
    ServeHTTP(ResponseWriter, *Request)
}

go all languages ​​custom types can implement your own method, http package is a custom func come to realize the Handler interface,

type HandlerFunc func(ResponseWriter, *Request)

// ServeHTTP calls f(w, r).
func (f HandlerFunc) ServeHTTP(w ResponseWriter, r *Request) {
    f(w, r)
}

Then ServerMuxthe method HandleFuncwhen the process will handler func(ResponseWriter, *Request)be converted into HandlerFunc, as follows:

// HandleFunc registers the handler function for the given pattern.
func (mux *ServeMux) HandleFunc(pattern string, handler func(ResponseWriter, *Request)) {
    if handler == nil {
        panic("http: nil handler")
    }
    mux.Handle(pattern, HandlerFunc(handler))
}

ServerMuxThere is also a read-write lock structure mu sync.RWMutexmu is used to secure access to multi-threaded processing at the map.

Find & Handler calls

A method to obtain a custom handler, is to map matching path obtained in accordance with Handler

func (mux *ServeMux) handler(host, path string) (h Handler, pattern string) {
    mux.mu.RLock()
    defer mux.mu.RUnlock()

    // Host-specific pattern takes precedence over generic ones
    if mux.hosts {
        h, pattern = mux.match(host + path)
    }
    if h == nil {
        h, pattern = mux.match(path)
    }
    if h == nil {
        h, pattern = NotFoundHandler(), ""
    }
    return
}
func (mux *ServeMux) match(path string) (h Handler, pattern string) {
    // Check for exact match first.
    v, ok := mux.m[path]
    if ok {
        return v.h, v.pattern
    }

    // Check for longest valid match.  mux.es contains all patterns
    // that end in / sorted from longest to shortest.
    for _, e := range mux.es {
        if strings.HasPrefix(path, e.pattern) {
            return e.h, e.pattern
        }
    }
    return nil, ""
}

ServeMuxTo achieve the Handlerlocal interface is the default call routing to achieve specific rules of his ServeHTTPmethod approach is to get a custom handler method, and call our custom methods:

func (mux *ServeMux) ServeHTTP(w ResponseWriter, r *Request) {
    if r.RequestURI == "*" {
        if r.ProtoAtLeast(1, 1) {
            w.Header().Set("Connection", "close")
        }
        w.WriteHeader(StatusBadRequest)
        return
    }
    h, _ := mux.Handler(r)
    h.ServeHTTP(w, r)
}

Interface Halder signature rule set, since our approach is defined
such as the following code, the function IndexHandler our custom method is returned to the client requests a hello world !string. How is a call center to request specific logical way of our custom is http package provides, but is not mysterious,

http.HandleFunc("/", IndexHandler)

// IndexHandler 我们自己定义的Handler方法
func IndexHandler(w http.ResponseWriter, r *http.Request) {
    fmt.Fprintln(w, "hello world ! ")
}
type Handler interface {
    ServeHTTP(ResponseWriter, *Request)
}
// 
type HandlerFunc func(ResponseWriter, *Request)
// ServeHTTP calls f(w, r).
func (f HandlerFunc) ServeHTTP(w ResponseWriter, r *Request) {
    f(w, r)
}

http ListenAndServe

Then ServeMuxis how to combine Handlerthe interfaces to implement routing and calling, you should say something, http service is how to get the client incoming information, and rresponse the package requet.
When you start the program http.ListenAndServe, there are two arguments, the first number refers to the write port, and the second is the processing logic, if we have not given processing logic, will use the default handlerDefaultServeMux

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)
}

ListenAndServeMethod to open tcp port monitor, and then Listenerpassed to srv.Servethe method

func (srv *Server) ListenAndServe() error {
    // 省略部分代码 ...
    ln, err := net.Listen("tcp", addr)
    if err != nil {
        return err
    }
    return srv.Serve(tcpKeepAliveListener{ln.(*net.TCPListener)})
}

Say about specific Servicemethods, this method, for listening tcp request, then the requesting client connection package,

func (srv *Server) Serve(l net.Listener) error {
    // 省略部分代码 ...
    ctx := context.WithValue(baseCtx, ServerContextKey, srv)
    for {
        rw, e := l.Accept()
        // 省略部分代码 ...
        tempDelay = 0
        c := srv.newConn(rw)
        c.setState(c.rwc, StateNew) // before Serve can return
        go c.serve(ctx)
    }
}

The client's request into a package Conn, 然后启动一个协程go c.serve(ctx)来处理这个连接请求,这就是http包快的一个重要原因, each connection is a coroutine. A client and server are connected, and then use this conn multiple http requests to transmit, so that, each time can be reduced by connecting some speed increase. Like some rpc where is the use of two-way stream flow this point to achieve, such as the post before me go micro-services framework go-micro deep learning (e) the calling procedure Detailed stream , he is establishing a tcp connection, and then based on the conn, a plurality of transmission request, data return times respose.

// Serve a new connection.
func (c *conn) serve(ctx context.Context) {
    c.remoteAddr = c.rwc.RemoteAddr().String()
    ctx = context.WithValue(ctx, LocalAddrContextKey, c.rwc.LocalAddr())
    // 省略部分代码 ...
    // 循环读取请求 ...
    for {
        // 读取请求数据,封装response
        w, err := c.readRequest(ctx)
        if c.r.remain != c.server.initialReadLimitSize() {
            // If we read any bytes off the wire, we're active.
            c.setState(c.rwc, StateActive)
        }
        // 省略部分代码 ...
        // 路由调用自定义的方法,把封装好的responseWrite和 request传到方法内
        serverHandler{c.server}.ServeHTTP(w, w.req)
        w.cancelCtx()
        if c.hijacked() {
            return
        }
        w.finishRequest()
        // 省略部分代码 ...
    }
}
@. Posted 2019-05-13 11:56 li-Peng read ( ... ) Comments ( ... ) edit collections

Guess you like

Origin blog.csdn.net/mi_duo/article/details/90193845