Principio de alta concurrencia de Golang

La potencia de procesamiento del software no solo está relacionada con la memoria, sino también si está bloqueada, si se procesa de forma asíncrona, la CPU, etc. Entonces, ¿es posible tener un lenguaje que use unidades de procesamiento más pequeñas y use menos memoria que los subprocesos, por lo que su poder de procesamiento concurrente puede ser mayor. Así que Google hizo esto, está el idioma golang, y golang admite una alta concurrencia desde el nivel del idioma.

Procesamiento de alta concurrencia de Go Core-Goroutine

Goroutine es el núcleo del diseño paralelo de Go. En el análisis final, la goroutina es en realidad una rutina. Es más pequeña que los hilos y requiere menos recursos. Decenas de goroutinas pueden reflejarse en la capa inferior. Son cinco o seis hilos. El lenguaje Go te ayuda a lograr compartir la memoria entre estas goroutinas. Solo se necesita una pequeña cantidad de memoria de pila (aproximadamente 4 ~ 5 KB) para ejecutar goroutine, y por supuesto escalará de acuerdo con los datos correspondientes. Debido a esto, decenas de miles de tareas simultáneas pueden ejecutarse simultáneamente. Goroutine es más fácil de usar, más eficiente y más ligero que el hilo.
Las corutinas son más livianas y ocupan menos memoria, que es el requisito previo para una alta concurrencia.

Un servicio web simple

package main

import (
    "fmt"
    "log"
    "net/http"
)

func response(w http.ResponseWriter, r *http.Request) {
    fmt.Fprintf(w, "Hello world!") //这个写入到w的是输出到客户端的
}

func main() {
    http.HandleFunc("/", response)
    err := http.ListenAndServe(":9000", nil)
    if err != nil {
        log.Fatal("ListenAndServe: ", err)
    }
}

Luego compila

go build -o test_web.gobin
./test_web.gobin

 Entonces visita

curl 127.0.0.1:9000
Hello world!

 A continuación, comprendemos paso a paso cómo se ejecuta este servicio web y cómo lograr una alta concurrencia.

Seguimos el método http.HandleFunc ("/", respuesta) y buscamos el código.

func HandleFunc(pattern string, handler func(ResponseWriter, *Request)) {
    DefaultServeMux.HandleFunc(pattern, handler)
}
var DefaultServeMux = &defaultServeMux
var defaultServeMux ServeMux

type ServeMux struct {
    mu    sync.RWMutex//读写锁。并发处理需要的锁
    m     map[string]muxEntry//路由规则map。一个规则一个muxEntry
    hosts bool //规则中是否带有host信息
}
一个路由规则字符串,对应一个handler处理方法。
type muxEntry struct {
    h       Handler
    pattern string
}

 Lo anterior es la definición y descripción de DefaultServeMux. Vemos la estructura ServeMux, que tiene un bloqueo de lectura y escritura para manejar el uso concurrente. La estructura muxEntry contiene métodos de procesamiento de controlador y cadenas de enrutamiento.

A continuación, veamos qué hace la función http.HandleFunc, que es DefaultServeMux.HandleFunc. Primero miramos el segundo parámetro de mux.Handle HandlerFunc (controlador)

func (mux *ServeMux) HandleFunc(pattern string, handler func(ResponseWriter, *Request)) {
    mux.Handle(pattern, HandlerFunc(handler))
}
type Handler interface {
    ServeHTTP(ResponseWriter, *Request)  // 路由实现器
}
type HandlerFunc func(ResponseWriter, *Request)
func (f HandlerFunc) ServeHTTP(w ResponseWriter, r *Request) {
    f(w, r)
}

El método de respuesta personalizado que pasamos se ve obligado a convertirse al tipo HandlerFunc, por lo que el método de respuesta que pasamos por defecto implementa el método ServeHTTP.

A continuación, veremos el primer parámetro de mux.Handle.

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)
    }
    mux.m[pattern] = muxEntry{h: handler, pattern: pattern}

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

 Almacene la cadena de enrutamiento y la función del controlador en la tabla del mapa ServeMux.m, la estructura muxEntry en el mapa, como se describió anteriormente, una ruta corresponde a un método de procesamiento del controlador.

A continuación, observamos qué hace http.ListenAndServe (": 9000", nil)

func ListenAndServe(addr string, handler Handler) error {
    server := &Server{Addr: addr, Handler: handler}
    return 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) es construir un servicio con protocolo TCP usando el puerto addr. tcpKeepAliveListener es para monitorear el puerto de addr.

El siguiente es el código clave, el procesamiento HTTP

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

Para l.Accept () acepta la solicitud de conexión TCP, c: = srv.newConn (rw) crea un Conn, y Conn contiene la información de la solicitud (srv, rw). Inicie goroutine, pase los parámetros solicitados a c.serve y deje que se ejecute goroutine.

Este es el punto más crítico de GO alta concurrencia. Cada solicitud es ejecutada por una gorutina separada.

 

Entonces, ¿dónde coincidían las rutas previamente establecidas? Se realiza analizando el MÉTODO URI, etc. en c.readRequest (ctx) de c.server y ejecutando serverHandler {c.server} .ServeHTTP (w, w.req). Mira el código

 

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

El controlador está vacío, solo el segundo parámetro de ListenAndServe en el proyecto que acabamos de comenzar. Somos nulos, así que vamos a DefaultServeMux, sabemos que configuramos DefaultServeMux para iniciar el enrutamiento, por lo que en DefaultServeMux puedo encontrar el controlador correspondiente a la ruta solicitada y luego ejecutar ServeHTTP. Como se mencionó anteriormente, por qué nuestro método de respuesta tiene la función ServeHTTP. Este es probablemente el proceso.

 

 

Conclusión

Básicamente, hemos aprendido todo el principio de funcionamiento de HTTP que ha olvidado GO y por qué puede lograr una alta concurrencia en el desarrollo WEB. Estos son solo la punta del iceberg de GO, así como el conjunto de conexiones Redis MySQL. Para familiarizarse con este idioma o escribir más, puede dominarlo. Uso flexible y especializado.

 

 


 

127 artículos originales publicados · Me gusta 24 · Visitas 130,000+

Supongo que te gusta

Origin blog.csdn.net/Linzhongyilisha/article/details/105473880
Recomendado
Clasificación