Principe de concurrence élevée de Golang

La puissance de traitement du logiciel n'est pas seulement liée à la mémoire, mais également à son blocage, à son traitement asynchrone, à son processeur, etc. Il est donc possible d'avoir un langage qui utilise des unités de traitement plus petites et utilise moins de mémoire que les threads, de sorte que sa puissance de traitement simultanée peut être plus élevée. Google a donc fait cela, il y a la langue golang, et golang prend en charge une concurrence élevée au niveau de la langue.

Go-core haute goroutine de traitement de concurrence

Goroutine est au cœur de la conception parallèle de Go. En dernière analyse, la goroutine est en fait une coroutine. Elle est plus petite que les threads et prend moins de ressources. Des dizaines de goroutines peuvent être reflétées dans la couche inférieure. Il s'agit de cinq ou six threads. Le langage Go vous aide à réaliser le partage de mémoire entre ces goroutines. Seule une petite quantité de mémoire de pile (environ 4 ~ 5 Ko) est requise pour exécuter goroutine, et bien sûr, elle évoluera en fonction des données correspondantes. Pour cette raison, des dizaines de milliers de tâches simultanées peuvent être exécutées simultanément. La goroutine est plus facile à utiliser, plus efficace et plus légère que le fil.
Les coroutines sont plus légères et prennent moins de mémoire, ce qui est la condition préalable à une concurrence élevée.

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

Compilez ensuite

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

 Alors visitez

curl 127.0.0.1:9000
Hello world!

 Ensuite, nous comprenons étape par étape comment ce service Web s'exécute et comment atteindre une concurrence élevée.

Nous avons suivi la méthode http.HandleFunc ("/", réponse) et recherché le code.

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
}

 Ce qui précède est la définition et la description de DefaultServeMux. Nous voyons la structure ServeMux, qui a un verrou en lecture-écriture pour gérer une utilisation simultanée. La structure muxEntry contient des méthodes de traitement de gestionnaire et des chaînes de routage.

Voyons ensuite ce que fait la fonction http.HandleFunc, qui est DefaultServeMux.HandleFunc. Nous examinons d'abord le deuxième paramètre de mux.Handle HandlerFunc (gestionnaire)

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

La méthode de réponse personnalisée que nous transmettons est forcée d'être convertie en type HandlerFunc, donc la méthode de réponse que nous transmettons implémente par défaut la méthode ServeHTTP.

Nous examinons ensuite le premier paramètre 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
    }
}

 Stockez la chaîne de routage et la fonction de gestionnaire dans la table de carte ServeMux.m, la structure muxEntry dans la carte, comme décrit ci-dessus, une route correspond à une méthode de traitement de gestionnaire.

Ensuite, nous regardons ce que fait 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) consiste à construire un service avec le protocole TCP en utilisant le port addr. tcpKeepAliveListener consiste à surveiller le port de l'adr.

Vient ensuite le code clé, le traitement 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)
    }
}

Pour que l.Accept () accepte la demande de connexion TCP, c: = srv.newConn (rw) crée un Conn et Conn détient les informations de la demande (srv, rw). Démarrez goroutine, passez les paramètres demandés à c.serve et laissez goroutine s'exécuter.

Il s'agit du point le plus critique de la haute simultanéité GO. Chaque demande est exécutée par un goroutine distinct.

 

Alors, où correspondaient les itinéraires précédemment définis? Cela se fait en analysant la MÉTHODE URI, etc. dans c.readRequest (ctx) de c.server, et en exécutant serverHandler {c.server} .ServeHTTP (w, w.req). Regardez le code

 

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

Le gestionnaire est vide, juste le deuxième paramètre de ListenAndServe dans le projet que nous venons de démarrer. Nous sommes nuls, donc nous allons à DefaultServeMux, nous savons que nous avons défini DefaultServeMux pour démarrer le routage, donc dans DefaultServeMux je peux trouver le gestionnaire correspondant à la route demandée, puis exécuter ServeHTTP. Comme mentionné précédemment, pourquoi notre méthode de réponse a la fonction ServeHTTP. C'est probablement le processus.

 

 

Conclusion

Nous avons essentiellement appris le principe de fonctionnement complet de HTTP qui a oublié GO, et pourquoi il peut atteindre une concurrence élevée dans le développement WEB. Ce ne sont que la pointe de l'iceberg de GO, ainsi que le pool de connexions Redis MySQL. Pour vous familiariser avec cette langue ou en écrire plus, vous pouvez la maîtriser. Utilisation flexible et compétente.

 

 


 

A publié 127 articles originaux · Aime 24 · Visites 130 000+

Je suppose que tu aimes

Origine blog.csdn.net/Linzhongyilisha/article/details/105473880
conseillé
Classement