Перейти в Интернет работает

Go
Запустить веб-сервис в Go очень просто. следующее:
//开启web服务
func test(){
    http.HandleFunc("/", sayHello)
    err := http.ListenAndServe(":9090",nil)
    if err!=nil {
        log.Fatal("ListenAndServer:",err)
    }
}

func sayHello(w http.ResponseWriter, r *http.Request){
    r.ParseForm()
    fmt.Println("path",r.URL.Path)
    fmt.Println("scheme",r.URL.Scheme)

    fmt.Fprintf(w, "Hello Guest!")
}

в настоящее время используетListenAndServeВ этом методе система назначит нам роутер,DefaultServeMuxмаршрутизатор по умолчанию, используемый системой, еслиListenAndServeВторой параметр этого метода передается в nil, и система будет использовать его по умолчанию.DefaultServeMux. Конечно, здесь также можно передать собственный маршрутизатор.

Первый взглядhttp.HandleFunc("/", sayHello),отHandleFuncМетод нажимается следующим образом:

func HandleFunc(pattern string, handler func(ResponseWriter, *Request)) {
    DefaultServeMux.HandleFunc(pattern, handler)
}

звонил сюдаDefaultServeMuxизHandleFuncметод, этот метод имеет два параметра,patternэто соответствующее правило маршрутизации,handlerУказывает метод обработки, соответствующий этому правилу маршрутизации, и этот метод обработки имеет два параметра.

В примере кода, который мы написали,patternсоответствовать/,handlerсоответствоватьsayHello, когда мы набираем в браузереhttp://localhost:9090, это вызоветsayHelloметод.

мы следуемDefaultServeMuxизHandleFuncМетод продолжает указывать, следующим образом:

func (mux *ServeMux) HandleFunc(pattern string, handler func(ResponseWriter, *Request)) {
    mux.Handle(pattern, HandlerFunc(handler))
}

В этом методе маршрутизатор снова вызываетHandleметод, обратите внимание на этоHandleВторой параметр метода, переданный ранееhandlerЭтот метод ответа заставляетHandlerFuncтип.

этоHandlerFuncЧто это за тип? следующее:

type HandlerFunc func(ResponseWriter, *Request)

Кажется, и мы определяемSayHelloТипы методов аналогичны. но! ! !
этоHandlerFuncРеализовано по умолчаниюServeHTTPинтерфейс! такHandlerFuncобъект имеетServeHTTPметод! следующее:

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

Эта деталь очень важна, потому что этот шаг связан с тем, будет ли вызываться соответствующий метод ответа при совпадении правила маршрутизации! Когда этот метод вызывается, будет описано в следующем разделе.

Далее возвращаемся и продолжаем смотретьmuxизHandleметод, который является этим кодомmux.Handle(pattern, HandlerFunc(handler)). Что делает этот код? Исходный код выглядит следующим образом:

func (mux *ServeMux) Handle(pattern string, handler Handler) {
    mux.mu.Lock()
    defer mux.mu.Unlock()

    if pattern == "" {
        panic("http: invalid pattern " + pattern)
    }
    if handler == nil {
        panic("http: nil handler")
    }
    if mux.m[pattern].explicit {
        panic("http: multiple registrations for " + pattern)
    }

    if mux.m == nil {
        mux.m = make(map[string]muxEntry)
    }
    mux.m[pattern] = muxEntry{explicit: true, h: handler, pattern: pattern}

    if pattern[0] != '/' {
        mux.hosts = true
    }

    // Helpful behavior:
    // If pattern is /tree/, insert an implicit permanent redirect for /tree.
    // It can be overridden by an explicit registration.
    n := len(pattern)
    if n > 0 && pattern[n-1] == '/' && !mux.m[pattern[0:n-1]].explicit {
        // If pattern contains a host name, strip it and use remaining
        // path for redirect.
        path := pattern
        if pattern[0] != '/' {
            // In pattern, at least the last character is a '/', so
            // strings.Index can't be -1.
            path = pattern[strings.Index(pattern, "/"):]
        }
        url := &url.URL{Path: path}
        mux.m[pattern[0:n-1]] = muxEntry{h: RedirectHandler(url.String(), StatusMovedPermanently), pattern: pattern}
    }
}

Кода много, на самом деле он в основном делает одно,DefaultServeMuxизmap[string]muxEntryДобавьте соответствующие правила маршрутизации иhandler.

map[string]muxEntryЧто это за фигня?

mapэто объект словаря, который содержитkey-value.
[string]представляет этот словарьkeyдаstringтип, этоkeyЭто значение будет содержать наши правила маршрутизации.
muxEntryЭто экземпляр объекта, в котором хранится метод обработки, соответствующий правилу маршрутизации.

Найдите соответствующий код следующим образом:

//路由器
type ServeMux struct {
    mu    sync.RWMutex
    m     map[string]muxEntry //路由规则,一个string对应一个mux实例对象,map的key就是注册的路由表达式(string类型的)
    hosts bool // whether any patterns contain hostnames
}

//muxEntry
type muxEntry struct {
    explicit bool
    h        Handler //这个路由表达式对应哪个handler
    pattern  string
}

//路由响应方法
type Handler interface {
    ServeHTTP(ResponseWriter, *Request)  //handler的路由实现器
}

ServeMuxЭто маршрутизатор по умолчанию в этой системе.

Наконец, резюмируя этот раздел:
1. звонокhttp.HandleFunc("/", sayHello)
2. позвонитьDefaultServeMuxизHandleFunc(), который определяет нашуsayHello()упаковано вHandlerFuncтип
3. Продолжайте звонитьDefaultServeMuxизHandle(),В направленииDefaultServeMuxизmap[string]muxEntryДобавьте правила маршрутизации и соответствующиеhandler

Хорошо, эта часть кода так много делает, и первая часть закончена.

Вторая часть предназначена в основном для изучения этого кодаerr := http.ListenAndServe(":9090",nil), это,ListenAndServeСюда. Нажмите на этот метод следующим образом:

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

В этом методе инициализируйтеserverобъект, затем вызовите этоserverобъектListenAndServeметод, в этом методе, следующим образом:

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

В этом методе вызовитеnet.Listen("tcp", addr), то есть нижний уровень строит службу с протоколом TCP, а затем отслеживает установленный нами порт.

В конце кода вызовитеsrvизServeМетоды, как показано ниже:

func (srv *Server) Serve(l net.Listener) error {
    defer l.Close()
    if fn := testHookServerServe; fn != nil {
        fn(srv, l)
    }
    var tempDelay time.Duration // how long to sleep on accept failure

    if err := srv.setupHTTP2_Serve(); err != nil {
        return err
    }

    srv.trackListener(l, true)
    defer srv.trackListener(l, false)

    baseCtx := context.Background() // base is always background, per Issue 16220
    ctx := context.WithValue(baseCtx, ServerContextKey, srv)
    ctx = context.WithValue(ctx, LocalAddrContextKey, l.Addr())
    for {
        rw, e := l.Accept()
        if e != nil {
            select {
            case <-srv.getDoneChan():
                return ErrServerClosed
            default:
            }
            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
                }
                srv.logf("http: Accept error: %v; retrying in %v", e, tempDelay)
                time.Sleep(tempDelay)
                continue
            }
            return e
        }
        tempDelay = 0
        c := srv.newConn(rw)
        c.setState(c.rwc, StateNew) // before Serve can return
        go c.serve(ctx)
    }
}

Последние три фрагмента кода более важны, и они также являются воплощением языка Go, поддерживающего высокий параллелизм, а именно:

c := srv.newConn(rw)
c.setState(c.rwc, StateNew) // before Serve can return
go c.serve(ctx)

Большой кусок кода выше означает, что после ввода метода сначала откройтеforцикл, вforПринять запрос в данный момент в цикле.После прихода запроса для каждого запроса будет создаваться запрос.Conn, затем откройте отдельныйgoroutine, кинуть данные этого запроса в качестве параметра в этотConnСлужить:go c.serve(). Каждый запрос от пользователя выполняется в новомgoroutineДля обслуживания каждый запрос не влияет друг на друга.

существуетconnизserveВ методе есть очень важный код, а именно:

serverHandler{c.server}.ServeHTTP(w, w.req)

выражатьserverHandler

также

ДостигнутоServeHTTPинтерфейс,ServeHTTPМетод реализуется следующим образом:

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

здесь, еслиhandlerпусто (этоhandlerЕго можно понимать как наш пользовательский маршрутизатор), будет использоваться системное значение по умолчанию.DefaultServeMux, последний вызов кодаDefaultServeMuxизServeHTTP()

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是Handler接口对象
    h.ServeHTTP(w, r)       //调用Handler接口对象的ServeHTTP方法实际上就调用了我们定义的sayHello方法
}

После того, как роутер получает запрос, если оно*тогда закройте ссылку, если нет*просто позвониmux.Handler(r)Возвращает обработку, соответствующую маршрутуHandler, затем выполнитеhandlerизServeHTTPМетод, который является кодомh.ServeHTTP(w, r),mux.Handler(r)что ты сделал? следующее:

func (mux *ServeMux) Handler(r *Request) (h Handler, pattern string) {
    if r.Method != "CONNECT" {
        if p := cleanPath(r.URL.Path); p != r.URL.Path {
            _, pattern = mux.handler(r.Host, p)
            url := *r.URL
            url.Path = p
            return RedirectHandler(url.String(), StatusMovedPermanently), pattern
        }
    }

    return mux.handler(r.Host, r.URL.Path)
}

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) {
    var n = 0
    for k, v := range mux.m {  //mux.m就是系统默认路由的map
        if !pathMatch(k, path) {
            continue
        }
        if h == nil || len(k) > n {
            n = len(k)
            h = v.h
            pattern = v.pattern
        }
    }
    return
}

это будет запрошено пользователемURLхранится в роутереmapЕсли совпадение успешно, он вернет сохраненныйhandler, назовите этоhandlerизServeHTTP()Вы можете выполнить соответствующий метод обработки, этот метод обработки фактически является тем, что мы только что начали.sayHello()Но этоsayHello()одеялоHandlerFuncдругой слой, потому чтоHandlerFuncДостигнутоServeHTTPинтерфейс, так называемыйHandlerFuncобъектServeHTTP()когда на самом делеServeHTTP ()внутренне звонит нашемуsayHello().

в заключении:
1. звонокhttp.ListenAndServe(":9090",nil)
2. Создать экземплярserver
3. позвонитьserverизListenAndServe()
4. звонокserverизServeметод, включитьforЦикл, принять запрос в цикле
5. Создайте один экземпляр для каждого запросаConnи откройтеgoroutineобслужить этот запросgo c.serve()
6. Прочитайте содержимое каждого запросаc.readRequest()
7. ПризватьserverHandlerизServeHTTP(),еслиhandlerпусто, поставьhandlerУстановить в качестве системного маршрутизатора по умолчаниюDefaultServeMux
8. ПризватьhandlerизServeHTTP()=> на самом деле называетсяDefaultServeMuxизServeHTTP()
9. ВServeHTTP()Соответствующая обработка маршрута будет вызвана вhandler
10. Соответствующая обработка в маршрутизацииhandlerбудет казненsayHello()

Следует отметить один момент:
DefaultServeMuxМетод обработки, соответствующий маршрутуhandlerвсе сбудетсяServeHTTPинтерфейс, они оба имеютServeHTTPметод, но цель метода иная, вDefaultServeMuxизServeHttp()Обработка, соответствующая маршруту, будет выполняться вhandlerизServeHttp().