Maison >développement back-end >Golang >Golang implémente la passerelle API

Golang implémente la passerelle API

王林
王林original
2023-05-10 09:34:36868parcourir

Avec la popularité de l'architecture des microservices et l'augmentation du nombre de services, afin d'améliorer la sécurité, la fiabilité et l'évolutivité des services, la technologie de passerelle API a émergé au fur et à mesure que les temps l'exigent. Aujourd'hui, nous allons parler de la façon d'utiliser Golang pour écrire une passerelle API.

1. Pourquoi choisir d'utiliser Golang

  1. Performances de concurrence : Golang prend naturellement en charge les coroutines, ce qui peut améliorer l'efficacité dans les scénarios de concurrence élevée et avoir un débit plus élevé.
  2. Fonctionnalités complètes : Golang propose des packages réseau complets, tels que HTTP, WebSocket, etc., et il prend également en charge plusieurs protocoles, tels que gRPC, MQTT, etc.
  3. Qualité du code : Golang est un langage statique. Son système de types peut garantir la qualité du code. En même temps, il peut également utiliser pleinement la complétion, les invites et d'autres fonctions de l'éditeur pour améliorer l'efficacité du développement.
  4. Multiplateforme : Golang peut être exécuté sur une variété de plateformes différentes, et son déploiement est également très simple.

2. Idées de conception de passerelle API

Avant de concevoir une passerelle API, nous devons savoir ce qu'une passerelle API doit faire. De manière générale, une passerelle API doit effectuer les opérations suivantes :

  1. Routage : acheminer la requête vers le service correspondant en fonction de l'URI demandé.
  2. Filtrage : Vérifiez et authentifiez les demandes.
  3. Équilibrage de charge : distribuez les requêtes aux différentes instances de service sur le backend.
  4. Caching : mettez en cache les résultats de réponse pour améliorer la vitesse de réponse.
  5. Monitoring : Statistiques et suivi des résultats des réponses aux requêtes.

Selon les idées ci-dessus, nous concevons une passerelle API simple, qui comprend principalement les composants suivants :

  1. Routeur : Responsable du routage des requêtes vers les services correspondants.
  2. Filtre : Responsable de la vérification, de l'authentification et d'autres opérations des demandes.
  3. Équilibrage de charge : responsable de la distribution des demandes aux différentes instances de service sur le backend.
  4. Caching : responsable de la mise en cache des résultats de réponse et de l'amélioration de la vitesse de réponse.
  5. Surveillance : Responsable des statistiques et du suivi des résultats des réponses aux demandes.

3. Implémenter API Gateway

Ensuite, nous implémenterons les composants ci-dessus un par un.

1. Routeur

Pour implémenter un routeur, nous devons d'abord définir un élément de routage :

type Route struct {
    Path      string
    Method    string
    Handler   http.Handler
}

Route contient un chemin pour stocker le chemin de routage, une méthode pour stocker le type de méthode de la requête et un gestionnaire. pour stocker et traiter la méthode de demande.

Ensuite, nous définissons une structure de routeur pour stocker la liste de routes et acheminer la demande vers le processeur correspondant :

type Router struct {
    routes []*Route
}

func (r *Router) HandleFunc(path string, method string, handlerFunc http.HandlerFunc) {
    r.routes = append(r.routes, &Route{Path: path, Method: method, Handler: handlerFunc})
}

func (r *Router) ServeHTTP(w http.ResponseWriter, req *http.Request) {
    for _, route := range r.routes {
        if route.Path == req.URL.Path && route.Method == req.Method {
            route.Handler.ServeHTTP(w, req)
            return
        }
    }

    http.Error(w, "404 not found", http.StatusNotFound)
}

Dans le code, nous fournissons deux méthodes pour enregistrer la méthode de traitement de la demande et la route, HandleFunc devrait être similaire à http.HandleFunc, il lie l'adresse de routage, la méthode de demande et la méthode de traitement de la demande à un objet. ServeHTTP recherchera une route correspondante après avoir reçu la demande, elle transmettra la demande à la méthode de traitement correspondante. Si elle n'est pas trouvée, renverra 404.

Ensuite, écrivez une implémentation simple d'une méthode de traitement :

func main() {
    router := &Router{}

    router.HandleFunc("/hello", "GET", func(w http.ResponseWriter, r *http.Request) {
        fmt.Fprint(w, "hello")
    })

    http.ListenAndServe(":8080", router)
}

Cette méthode de traitement renverra hello pour toute requête GET avec l'adresse /hello.

2. Filtre

WAF (Web Application Firewall) est un filtre couramment utilisé pour protéger les applications Web contre diverses attaques. Nous pouvons filtrer les demandes en fonction de la méthode de demande, des en-têtes de demande, des paramètres de demande et d'autres informations. Ici, nous utilisons les en-têtes de requête comme filtres. Si l'en-tête de la requête contient une balise spécifique, elle est transmise, sinon elle est considérée comme une requête invalide.

Pour implémenter des filtres à l'aide de Golang, nous devons écrire un middleware qui vérifiera si une balise spécifique est incluse dans chaque requête. S'il est inclus, continuez à transmettre la demande, sinon une erreur sera renvoyée. Nous pouvons utiliser gorilla/mux pour implémenter un middleware.

type FilterMiddleware struct {
    next http.Handler
}

func (f *FilterMiddleware) ServeHTTP(w http.ResponseWriter, r *http.Request) {
    if r.Header.Get("X-Auth-Token") != "magic" {
        http.Error(w, "Unauthorized request", http.StatusUnauthorized)
        return
    }

    f.next.ServeHTTP(w, r)
}

func (r *Router) RegisterMiddleware(middleware func(http.Handler) http.Handler) {
    handler := http.Handler(r)

    for _, m := range middlewareFunctions {
        handler = m(handler)
    }

    r.Handler = handler
}

Dans le code, FilterMiddleware vérifiera si l'en-tête de la requête contient la balise "X-Auth-Token". Si c'est le cas, la requête sera transmise, sinon une erreur non autorisée sera renvoyée. Nous définissons également une fonction RegisterMiddleware pour enregistrer le middleware.

3. Équilibrage de charge

L'équilibrage de charge est l'un des composants les plus importants de la passerelle API. Il peut distribuer les requêtes à différentes instances de service sur le backend. Nous pouvons utiliser des algorithmes de sondage, aléatoires et autres pour y parvenir.

Ici, nous utilisons un algorithme d'interrogation simple pour l'équilibrage de charge. Nous pouvons sélectionner l'adresse du serveur suivant dans un pool et transmettre la demande à ce serveur.

type LoadBalancer struct {
    Pool []string
    Next int
}

func (l *LoadBalancer) Pick() string {
    if l.Next >= len(l.Pool) {
        l.Next = 0
    }

    host := l.Pool[l.Next]

    l.Next++

    return host
}

func (r *Router) HandleFunc(path string, method string, handlerFunc http.HandlerFunc) {
    l := &LoadBalancer{
        Pool: []string{"http://127.0.0.1:8081", "http://127.0.0.1:8082"},
        Next: 0,
    }

    handler := http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
        url, err := url.Parse(l.Pick())
        if err != nil {
            http.Error(w, err.Error(), http.StatusInternalServerError)
            return
        }

        proxy := httputil.NewSingleHostReverseProxy(url)
        proxy.ServeHTTP(w, req)
    })

    r.routes = append(r.routes, &Route{Path: path, Method: method, Handler: handler})
}

Dans le code, nous avons créé une structure simple d'équilibrage de charge qui contient le pool de serveurs et l'emplacement du prochain serveur à sélectionner. La méthode pick() sélectionnera une adresse de serveur en fonction de la longueur du pool de serveurs. Dans HandleFunc, nous utilisons l'algorithme Round Robin pour transmettre les requêtes à différents serveurs.

4. Mise en cache

La mise en cache peut améliorer les performances du système et également réduire le nombre de requêtes adressées au backend. Dans la passerelle API, nous pouvons intégrer le cache dans le processus de réponse à la demande et renvoyer directement la demande dans le cache qui peut fournir rapidement une réponse.

type Cache struct {
    data map[string] []byte
    mutex sync.Mutex
}

func (c *Cache) Get(key string) []byte {
    c.mutex.Lock()
    defer c.mutex.Unlock()

    if value, ok := c.data[key]; ok {
        return value
    }

    return nil
}

func (c *Cache) Set(key string, value []byte) {
    c.mutex.Lock()
    defer c.mutex.Unlock()

    c.data[key] = value
}

func (r *Router) HandleFunc(path string, method string, handlerFunc http.HandlerFunc) {
    cache := &Cache{
        data: make(map[string][]byte),
    }

    handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        key := r.URL.String()
        if data := cache.Get(key); data != nil {
            w.Write(data)
            return
        }

        buffer := &bytes.Buffer{}
        proxy := httputil.NewSingleHostReverseProxy(r.URL)
        proxy.ServeHTTP(buffer, r)
        cache.Set(key, buffer.Bytes())
        w.Write(buffer.Bytes())
    })

    r.routes = append(r.routes, &Route{Path: path, Method: method, Handler: handler})
}

Dans le code, nous avons créé une structure de cache et mis en cache la requête dans HandleFunc. Si la même requête existe dans le cache, nous pouvons renvoyer le résultat directement depuis le cache, réduisant ainsi le nombre de requêtes vers le backend.

5. Surveillance

La surveillance peut nous aider à mieux comprendre l'état de fonctionnement du système et à saisir la vitesse de réponse du système actuel, etc.

使用Prometheus来实现API网关的监控。我们只需记录下来每一个请求的响应时间、状态码及信息,然后将所有的数据发送到Prometheus。

var (
    apiRequestsDuration = prometheus.NewHistogramVec(prometheus.HistogramOpts{
        Name:    "api_request_duration_seconds",
        Help:    "The API request duration distribution",
        Buckets: prometheus.DefBuckets,
    }, []string{"status"})
)

type MetricMiddleware struct {
    next http.Handler
}

func (m *MetricMiddleware) ServeHTTP(w http.ResponseWriter, r *http.Request) {
    start := time.Now()

    ww := httptest.NewRecorder()

    m.next.ServeHTTP(ww, r)

    duration := time.Since(start)

    apiRequestsDuration.WithLabelValues(strconv.Itoa(ww.Code)).Observe(duration.Seconds())

    for key, values := range ww.Header() {
        for _, value := range values {
            w.Header().Add(key, value)
        }
    }

    w.WriteHeader(ww.Code)

    body := ww.Body.Bytes()

    w.Write(body)
}

func main() {
    router := &Router{}

    router.HandleFunc("/hello", "GET", func(w http.ResponseWriter, r *http.Request) {
        fmt.Fprint(w, "hello")
    })

    logger := NewLoggerMiddleware(router)

    http.Handle("/metrics", prometheus.Handler())
    http.Handle("/", logger)

    service := ":8080"

    log.Printf("Server starting on %v
", service)
    log.Fatal(http.ListenAndServe(service, nil))
}

在代码中,我们定义了一个MetricMiddleware并在请求结束后统计相关时间数据,最后通过Prometheus将数据输出到Metrics监控系统中。我们还通过http.Handle将Prometheus绑定在“/metrics”路径上,方便查询。

结束语

至此,我们用Golang实现了一个简单的API网关。在实际使用中,我们还可以添加更多的功能,如熔断、限流、容错等,来提高其安全性、稳定性和可用性。

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Déclaration:
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn