Микросервисы с Go-Micro на примере

—

от автора

Привет, Хабр!

Микросервисная архитектура представляет из себя подход, в котором каждый сервис отвечает за конкретную функциональность и может быть развернут, обновлен и масштабирован независимо от других. Go-Micro — это фреймворк, который упрощает создание таких микросервисов на Golang.

Основные фичи Go-Micro:

  • Автоматическое обнаружение сервисов: сервисы автоматом регистрируются и обнаруживаются.

  • Встроенная балансировка нагрузки: запросы равномерно распределяются между экземплярами сервисов.

  • Поддержка как синхронной, так и асинхронной коммуникации: возможность использования RPC и PubSub для обмена сообщениями между сервисами.

  • Гибкая конфигурация: динамическая загрузка и обновление конфигураций из различных источников.

Установим Go-Micro с помощью go get:

go get go-micro.dev/v4

Основные компоненты

Service является основой Go-Micro. Он предоставляет интерфейс для создания, инициализации и запуска микросервисов

service := micro.NewService(     micro.Name("example"),     micro.Version("latest"), )  service.Init() service.Run()

Registry отвечает за регистрацию и обнаружение сервисов. Он позволяет сервисам находить друг друга в распределенной системе. В Go-Micro реализована поддержка различных механизмов регистрации Consul, Etcd, Kubernetes и т.д. По дефолту используется встроенный механизм mDNS.

import "github.com/micro/go-micro/v2/registry/consul"  registry := consul.NewRegistry() service := micro.NewService(     micro.Registry(registry), )

Broker реализует паттерн Pub/Sub и отвечает за асинхронную передачу сообщений между сервисами. Он поддерживает различные брокеры сообщений: NATS, Kafka, RabbitMQ и другие.

import "github.com/micro/go-micro/v2/broker/nats"  broker := nats.NewBroker() service := micro.NewService(     micro.Broker(broker), )

Client предоставляет интерфейс для выполнения запросов к другим сервисам. Он поддерживает синхронные и асинхронные вызовы, автоматическую балансировку нагрузки и повторные попытки в случае неудачи. Клиент автоматом обнаруживает сервисы через

import "github.com/micro/go-micro/v2/client"  req := client.NewRequest("example", "Example.Method", &Request{}) rsp := &Response{} err := client.Call(context.Background(), req, rsp)

Server обрабатывает входящие запросы и обеспечивает взаимодействие с клиентами. Он поддерживает протоколы HTTP и gRPC, и позволяет регистрировать обработчики запросов.

import "github.com/micro/go-micro/v2/server"  server := micro.NewService(     micro.Server(server.NewServer()), )

Transport отвечает за передачу данных между сервисами. Он поддерживает различные транспортные механизмы, включая HTTP, gRPC и т.п.

import "github.com/micro/go-micro/v2/transport/grpc"  transport := grpc.NewTransport() service := micro.NewService(     micro.Transport(transport), )

Codec предоставляет интерфейс для кодирования и декодирования сообщений. Он поддерживает различные форматы данных.

import "github.com/micro/go-micro/v2/codec/json"  codec := json.NewCodec() service := micro.NewService(     micro.Codec(codec), )

Создание микросервиса на примере

Создаем директорию проекта и инициализируем модуль Go:

mkdir order-service cd order-service go mod init order-service

Начнем с создания основного файла main.go, который будет запускать сам сервис:

package main  import (     "log"     "github.com/micro/go-micro/v2"     "github.com/micro/go-micro/v2/server"     "context" )  type OrderService struct{}  func (s *OrderService) CreateOrder(ctx context.Context, req *CreateOrderRequest, rsp *CreateOrderResponse) error {     // логика создания заказа     rsp.OrderId = "12345"     return nil }  func main() {     service := micro.NewService(         micro.Name("order-service"),     )      service.Init()      server := service.Server()     server.Handle(         server.NewHandler(&OrderService{}),     )      if err := service.Run(); err != nil {         log.Fatal(err)     } }

Для хранения заказов будем использовать базу данных PostgreSQL. Начнем с установки драйвера psql:

go get github.com/lib/pq

Добавим взаимодействие с БД в сервис:

package main  import (     "database/sql"     "log"     "github.com/micro/go-micro/v2"     "github.com/micro/go-micro/v2/server"     _ "github.com/lib/pq"     "context" )  type OrderService struct {     db *sql.DB }  func (s *OrderService) CreateOrder(ctx context.Context, req *CreateOrderRequest, rsp *CreateOrderResponse) error {     query := "INSERT INTO orders (product_id, quantity) VALUES ($1, $2) RETURNING id"     err := s.db.QueryRow(query, req.ProductId, req.Quantity).Scan(&rsp.OrderId)     if err != nil {         return err     }     return nil }  func main() {     db, err := sql.Open("postgres", "user=username password=password dbname=orderdb sslmode=disable")     if err != nil {         log.Fatal(err)     }      service := micro.NewService(         micro.Name("order-service"),     )      service.Init()      orderService := &OrderService{db: db}     server := service.Server()     server.Handle(         server.NewHandler(orderService),     )      if err := service.Run(); err != nil {         log.Fatal(err)     } }

Для балансировки нагрузки будем юзать Consul как сервисный регистр и NGINX для распределения запросов. Устанавливаем Consul и настраиваем его для регистрации наших сервисов.

brew install consul consul agent -dev

Настраиваем сервис для использования Consul:

import (     "github.com/micro/go-micro/v2/registry"     "github.com/micro/go-micro/v2/registry/consul" )  func main() {     consulReg := consul.NewRegistry(registry.Addrs("127.0.0.1:8500"))      service := micro.NewService(         micro.Name("order-service"),         micro.Registry(consulReg),     )      service.Init()      orderService := &OrderService{db: db}     server := service.Server()     server.Handle(         server.NewHandler(orderService),     )      if err := service.Run(); err != nil {         log.Fatal(err)     } }

Теперь настроим NGINX для балансировки нагрузки. Создаем файл конфигурации NGINX /etc/nginx/nginx.conf:

http {     upstream order_service {         least_conn;         server 127.0.0.1:8080;         server 127.0.0.1:8081;     }      server {         listen 80;          location / {             proxy_pass http://order_service;             proxy_set_header Host $host;             proxy_set_header X-Real-IP $remote_addr;             proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;             proxy_set_header X-Forwarded-Proto $scheme;         }     } }

Перезапускаем NGINX:

sudo systemctl restart nginx

Теперь сервис заказов готов к использованию. Можно запускать несколько экземпляров сервиса на разных портах и NGINX будет балансировать нагрузку между ними. Запустим два экземпляра сервиса:

go run main.go -server_address=:8080 go run main.go -server_address=:8081

Теперь любой запрос к сервису будет балансироваться между этими двумя экземплярами.

Подробнее с другими возможностями Go-Micro можно ознакомиться здесь.


В завершение напомню, что сегодня вечером в Otus пройдёт открытый урок, посвященный теме модульных монолитов и DDD. На нём участники рассмотрят основы domain-driven
design и применение к предметно-ориентированному проектированию. Обсудят, как DDD помогает в построении архитектуры. Если актуально, регистрируйтесь по ссылке.


ссылка на оригинал статьи https://habr.com/ru/articles/823782/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *