Dans cet article, je décrirai l'utilisation du kit Go, un ensemble d'outils et de bibliothèques pour créer des micro-services sur Go. Cet article est une introduction au kit Go. La première partie de mon blog, le code source des exemples est disponible ici .
Go est de plus en plus choisi pour le développement de systèmes distribués modernes. Lorsque vous développez un système distribué basé sur le cloud, vous devrez peut-être prendre en charge diverses fonctionnalités spécifiques dans vos services, telles que: divers protocoles de transport ( etc. trad. HTTP, gRPC, etc. ) et formats de codage des messages pour eux, fiabilité RPC, journalisation , traçage, métriques et profilage, interruption des requêtes, limitation du nombre de requêtes, intégration dans l'infrastructure, voire description de l'architecture. Go est un langage populaire en raison de sa simplicité et de ses approches «sans magie», donc les packages Go, par exemple, une bibliothèque standard, sont déjà plus adaptés pour développer des systèmes distribués que d'utiliser un cadre à part entière avec beaucoup de «magie sous le capot». Personnellement, je [ env. trans. Shiju Varghese ] Je ne supporte pas l'utilisation de frameworks à part entière, je préfère utiliser des bibliothèques qui donnent plus de liberté au développeur. Le kit Go a comblé une lacune dans l'écosystème Go, permettant d'utiliser un ensemble de bibliothèques et de packages lors de la création de microservices, qui à leur tour permettent l'utilisation de bons principes pour la conception de services individuels dans des systèmes distribués.

Kit d'introduction à Go
Le kit Go est un ensemble de packages Go qui facilitent la création de microservices fiables et pris en charge. Le kit Go fournit des bibliothèques pour implémenter divers composants d'une architecture d'application transparente et fiable, en utilisant des couches telles que: la journalisation, les métriques, le traçage, la limitation et l'interruption des requêtes qui sont nécessaires pour exécuter des microservices sur le prod. Le kit Go est bon car il dispose d'outils bien mis en œuvre pour interagir avec diverses infrastructures, formats d'encodage de messages et différentes couches de transport.
En plus de l'ensemble des bibliothèques pour les services du monde en développement, il fournit et encourage l'utilisation de bons principes pour concevoir l'architecture de vos services. Le kit Go vous aide à adhérer aux principes SOLID, à l'approche orientée sujet (DDD) et à l' architecture hexagonale proposés par Alistair Cockburn ou à toute autre approche issue des principes architecturaux connus sous le nom de « architecture oignon » par Jeffrey Palermo et « architecture propre » par Robert C. Martin . Bien que le kit Go ait été conçu comme un ensemble de packages pour développer des microservices, il convient également pour développer des monolithes élégants.
Kit Architecture Go
Les trois niveaux principaux de l'architecture des applications développées à l'aide du kit Go sont:
- niveau de transport
- niveau de point final
- niveau de service
Niveau de transport
Lorsque vous écrivez des microservices pour des systèmes distribués, les services qu'ils contiennent doivent souvent communiquer entre eux à l'aide de divers protocoles de transport, tels que: HTTP ou gRPC, ou utiliser des systèmes pub / sub, tels que NATS. La couche de transport dans le kit Go est liée à un protocole de transport spécifique (ci-après transport). Le kit Go prend en charge divers transports pour votre service, tels que: HTTP, gRPC, NATS, AMQP et Thirft ( environ. Vous pouvez également développer votre propre transport pour votre protocole ). Par conséquent, les services écrits à l'aide du kit Go se concentrent souvent sur la mise en œuvre d'une logique métier spécifique qui ne sait rien du transport utilisé, vous êtes libre d'utiliser différents transports pour le même service. Par exemple, un service écrit dans le kit Go peut simultanément y accéder via HTTP et gRPC.
Points de terminaison
Un point de terminaison ou un point de terminaison est la pierre angulaire fondamentale des services et des clients. Dans le kit Go, le modèle de communication principal est RPC. Le point final est présenté comme une méthode RPC distincte. Chaque méthode de service du kit Go est convertie en point de terminaison, vous permettant de communiquer entre le serveur et le client dans le style RCP. Chaque point de terminaison expose une méthode de service utilisant la couche Transport, qui à son tour utilise divers protocoles de transport, tels que HTTP ou gRPC. Un point de terminaison séparé peut être exposé en dehors du service simultanément à l'aide de plusieurs transports ( environ HTTP et gRPC selon différents ports ).
Les services
La logique métier est implémentée dans la couche service. Les services écrits avec le kit Go sont conçus comme des interfaces. La logique métier dans la couche service contient le noyau principal de la logique métier, qui n'a besoin de rien savoir sur les points de terminaison utilisés ou un protocole de transport spécifique, comme HTTP ou gRPC, ou sur l'encodage ou le décodage des demandes et des réponses de divers types de messages. Cela vous permettra d'adhérer à une architecture propre dans les services écrits à l'aide du kit Go. Chaque méthode de service est convertie en point de terminaison à l'aide d'un adaptateur et exposée à l'extérieur à l'aide d'un transport spécifique. Grâce à l'utilisation d'une architecture propre, une seule méthode peut être définie à l'aide de plusieurs transports en même temps.
Des exemples
Et maintenant, regardons les couches décrites ci-dessus en utilisant un exemple d'application simple.
Logique métier au service
La logique métier du service est conçue à l'aide d'interfaces. Nous considérerons l'exemple d'une commande en e-commerce:
L'interface du service de commande fonctionne avec l'entité de domaine de commande:
Ici, nous implémentons l'interface du service Order:
package implementation import ( "context" "database/sql" "time" "github.com/go-kit/kit/log" "github.com/go-kit/kit/log/level" "github.com/gofrs/uuid" ordersvc "github.com/shijuvar/gokit-examples/services/order" )
Demandes et réponses pour les points de terminaison RPC
Les méthodes de service sont exposées en tant que points de terminaison RPC. Nous devons donc déterminer les types de messages ( environ Per. DTO - objet de transfert de données ) qui seront utilisés pour envoyer et recevoir des messages via des points de terminaison RPC. Définissons maintenant les structures des types de demande et de réponse pour les points de terminaison RPC dans le service Order:
Aller aux points de terminaison du kit pour les méthodes de service comme les points de terminaison RPC
Le cœur de notre logique métier est séparé du reste du code et placé dans la couche de service, qui est exposée à l'aide de points de terminaison RPC, qui utilisent l'abstraction du kit Go appelée Endpoint
.
Voici à quoi ressemble le point final du kit Go:
type Endpoint func(ctx context.Context, request interface{}) (response interface{}, err error)
Comme nous l'avons dit ci-dessus, le point final représente une méthode RPC distincte. Chaque méthode de service est convertie en endpoint.Endpoint
aide d'adaptateurs. Faisons les kits d'extrémité du kit Go pour les méthodes de service de commande:
import ( "context" "github.com/go-kit/kit/endpoint" "github.com/shijuvar/gokit-examples/services/order" )
L'adaptateur de point de terminaison prend l'interface comme argument à l'entrée et la convertit en endpoint.Enpoint
kit d'abstraction Go. endpoint.Enpoint
faisant de chaque méthode de service individuelle un point de terminaison. Cette fonction d'adaptateur effectue des conversions de comparaison et de type pour les demandes, appelle une méthode de service et renvoie un message de réponse.
func makeCreateEndpoint(s order.Service) endpoint.Endpoint { return func(ctx context.Context, request interface{}) (interface{}, error) { req := request.(CreateRequest) id, err := s.Create(ctx, req.Order) return CreateResponse{ID: id, Err: err}, nil } }
Exposer un service à l'aide de HTTP
Nous avons créé notre service et décrit les points de terminaison RPC pour exposer nos méthodes de service. Nous devons maintenant publier notre service à l'extérieur afin que d'autres services puissent appeler des points de terminaison RCP. Pour exposer notre service, nous devons déterminer le protocole de transport pour notre service, selon lequel il acceptera les demandes. Le kit Go prend en charge divers transports, tels que HTTP, gRPC, NATS, AMQP et Thrift prêts à l'emploi.
Par exemple, nous utilisons le transport HTTP pour notre service. Le package go kit github.com/go-kit/kit/transport/http offre la possibilité de servir les requêtes HTTP. Et la fonction NewServer
du NewServer
transport/http
créera un nouveau serveur http qui implémentera http.Handler
et http.Handler
les points de terminaison fournis.
Vous trouverez ci-dessous le code qui convertit les points de terminaison du kit Go en un transport HTTP qui sert les requêtes HTTP:
package http import ( "context" "encoding/json" "errors" "github.com/shijuvar/gokit-examples/services/order" "net/http" "github.com/go-kit/kit/log" kithttp "github.com/go-kit/kit/transport/http" "github.com/gorilla/mux" "github.com/shijuvar/gokit-examples/services/order/transport" ) var ( ErrBadRouting = errors.New("bad routing") )
Nous créons http.Handler
à l'aide de la fonction NewServer
du NewServer
transport/http
, qui nous fournit des points de terminaison et demande des fonctions de décodage (renvoie la valeur de type DecodeRequestFunc func
) et un codage de réponse (par exemple, type EncodeReponseFunc func
).
Voici des exemples de DecodeRequestFunc
et EncodeResponseFunc
:
Démarrage du serveur HTTP
Enfin, nous pouvons exécuter notre serveur HTTP pour traiter les demandes. La fonction NewService
décrite ci-dessus implémente l'interface http.Handler
qui nous permet de l'exécuter en tant que serveur HTTP:
func main() { var ( httpAddr = flag.String("http.addr", ":8080", "HTTP listen address") ) flag.Parse() var logger log.Logger { logger = log.NewLogfmtLogger(os.Stderr) logger = log.NewSyncLogger(logger) logger = level.NewFilter(logger, level.AllowDebug()) logger = log.With(logger, "svc", "order", "ts", log.DefaultTimestampUTC, "caller", log.DefaultCaller, ) } level.Info(logger).Log("msg", "service started") defer level.Info(logger).Log("msg", "service ended") var db *sql.DB { var err error
Maintenant, notre service est lancé et utilise le protocole HTTP au niveau du transport. Le même service peut être démarré à l'aide d'un autre transport. Par exemple, un service peut être exposé à l'aide de gRPC ou d'Apache Thrift.
Pour l'article d'introduction, nous avons déjà suffisamment utilisé les primitives du kit Go, mais il fournit également plus de fonctionnalités pour créer des systèmes de modèles transparents et fiables, la découverte de services, l'équilibrage de charge, etc. Nous en discuterons et d'autres dans le kit Go dans les articles suivants.
Code source
Le code source complet pour les exemples peut être consulté sur GitHub ici.
Kit Middlewares in Go
Le kit Go prédispose à l'utilisation de bons principes de conception de système, tels que la superposition. L'isolement des composants de service et des points d'extrémité est possible à l'aide de middlewares ( modèle de médiateur de voie environ ). Les middlewares du kit Go fournissent un mécanisme puissant par lequel vous pouvez encapsuler des services et des points de terminaison et ajouter des fonctionnalités (composants isolés), telles que la journalisation, l'interruption des demandes, la limitation du nombre de demandes, l'équilibrage de charge ou le traçage distribué.
Ci-dessous, une image du site Web du kit Go , qui est décrite comme une «architecture d'oignon» typique utilisant des middlewares dans le kit Go:

Méfiez-vous du syndrome des microservices Spring Boot
Comme le kit Go, Spring Boot est une boîte à outils de microservices dans le monde Java. Mais, contrairement au kit Go, Spring Boot est un framework très mature. En outre, de nombreux développeurs Java utilisent Spring Boot pour créer des services mondiaux à l'aide de la pile Java avec des commentaires positifs sur l'utilisation, certains d'entre eux pensent que les microservices ne concernent que l'utilisation de Spring Boot. Je vois de nombreuses équipes de développement qui interprètent mal l'utilisation des microservices, qui ne peuvent être développées qu'en utilisant Spring Boot et OSS Netflix et ne perçoivent pas les microservices comme un modèle lors du développement de systèmes distribués.
Gardez donc à l'esprit qu'avec un ensemble d'outils, comme un kit Go ou une sorte de cadre, vous orientez votre développement vers les microseurises, comme modèle de conception. Bien que les microservices résolvent de nombreux problèmes de mise à l'échelle des commandes et des systèmes, cela crée également de nombreux problèmes car les données des systèmes basés sur les microservices sont dispersées dans diverses bases de données, ce qui crée parfois de nombreux problèmes lors de la création de requêtes transactionnelles ou de données. Tout dépend du problème du domaine et du contexte de votre système. Ce qui est cool, c'est que le kit Go, conçu comme un outil pour créer des microservices, était également adapté pour créer des monolithes élégants qui sont créés avec une bonne architecture pour vos systèmes.
Et certaines fonctionnalités du kit Go, telles que l'interruption et la restriction des demandes, sont également disponibles sur les plates-formes de maillage de service, comme Istio. Donc, si vous utilisez quelque chose comme Istio pour lancer vos microseurises, vous n'aurez peut-être pas besoin de certaines choses du kit Go, mais tout le monde n'aura pas assez de largeur de canal pour utiliser le maillage de service pour créer une communication interservices, car cela ajoute plus un niveau et une complexité supplémentaire.
PS
L'auteur de la traduction peut ne pas partager l'avis de l'auteur du texte original , cet article a été traduit à des fins éducatives uniquement pour la communauté de langue russe Go.
UPD
Ceci est également le premier article de la section traduction et je vous serais reconnaissant de tout commentaire sur la traduction.