Go благодаря возможностям компиляции и встроенным механизмам конкурентной многозадачности очень хорошо подходит для создания сетевых приложений и активно используется в создании инструментов для DevOps и распределенных приложений. В этой статье мы рассмотрим некоторые возможности фреймворка GoMicro для реализации микросервисных приложений на Go.

В микросервисной архитектуре одним из наиболее важных аспектов является механизм передачи данных внутри распределенного приложения и GoMicro поддерживает как использование REST, так и возможность применения очередей сообщений для обмена данных как в подходе RPC, так и для реализации архитектуры реактивных приложений, основанных на событиях. GoMicro основан на использовании расширяемой архитектуры и представляет большое количество важных подсистем для реализации микросервисов:

  • Аутентификация и авторизация обеспечивают для каждого сервиса идентификатор и сертификаты и реализует управление доступом на основе правил.

  • Хранилище данных — простой интерфейс хранилища данных для чтения, записи и удаления записей. Данные могут быть сохранены как в память, так и во внешнее хранилище.

  • Динамическая конфигурация — загрузка и обновление в реальном времени динамической конфигурации из любого места. Интерфейс конфигурации предоставляет способ загрузки конфигурации уровня приложения из любого источника, такого как переменные окружения, файл и т. д. Источники могут быть объединены и можно определить способ отката на другой источник или указать значения по умолчанию.

  • Механизм обнаружения сервисов — автоматическая регистрация сервисов и разрешение имен (по умолчанию на основе mDNS).

  • Балансировка нагрузки — балансировка нагрузки на стороне клиента, основанная на обнаружении сервисов. Нагрузка распределяется равномерно между несколькими реализациями сервисами с автоматическим переключением при наличии ошибок.

  • Кодирование сообщений — возможность кодирования сообщений в JSON / Protobuf.

  • Клиент/сервер RPC — запрос/ответ на основе RPC с поддержкой двунаправленной потоковой передачи.

  • Асинхронный обмен сообщениями — PubSub является основой для распределенных приложений, управляемых событиями (на основе HTTP-запросов).

  • Потоковая передача событий реализует поддержку потоков NATS Jetstream и Redis.

  • Синхронизация. Поддерживается распределенная блокировка и протокол динамического выбора лидера среди доступного кворума.

  • Подключаемые интерфейсы — Go Micro использует интерфейсы Go для каждой абстракции распределенной системы и позволяет создавать собственные реализации для каждого компонента.

Для подключения GoMicro нужно установить и импортировать модуль из "go-micro.dev/v4" и дальше использовать его методы для управления создаваемым сервисом. Также в модулях в github.com/go-micro/plugins/v4/... доступны плагины, которых делятся на следующие категории:

  • broker - взаимодействие с брокерами сообщений для реализации потоковой передачи сообщений (NATS, RabbitMQ, Kafka);

  • client - клиенты для запросов к другим микросервисам (через RPC, gRPC или HTTP);

  • server - серверный компонент для обеспечения доступа к микросервису (RPC, gRPC, HTTP);

  • codec - кодирование сообщений в BSON, Mercury, Protobuf;

  • config - управление распределенной конфигурацией;

  • registry - регистрация и обнаружение сервисов (в том числе, может взаимодействовать с Kubernetes);

  • selector - балансировка нагрузки;

  • transport - двухсторонняя передача данных через NATS или RabbitMQ;

  • wrapper - дополнительные middleware (например, логирование, трассировка запросов, ограничение скорости запросов и т.д.).

Создание сервиса начинается с вызова NewService и дальнейшей инициализации созданной структуры. Также сразу может быть выполнена регистрация сервиса и настройка способов обнаружения других сервисов, подключены серверные и клиентские компоненты.

package main

import (
	"go-micro.dev/v4"
  	"github.com/go-micro/plugins/v4/registry/kubernetes"
  	grpcc "github.com/go-micro/plugins/v4/client/grpc"
	grpcs "github.com/go-micro/plugins/v4/server/grpc"
)

func main() {
    registry := kubernetes.NewRegistry()
	service := micro.NewService(
		micro.Name("my.service"),
        micro.Registry(registry),
        micro.Server(grpcs.NewServer()),
		micro.Client(grpcc.NewClient()),
	)
    service.Server().handle()
	service.Init()
    service.Run()
}

Далее для реализации обработчиков и вызовов удаленных методов могут использоваться механизмы кодогенерации для protobuf (protoc‑gen‑go), для доступа к зарегистрированным компонентам используются методы в service. Например, для получения доступа к объекту сервера можно обратиться к service.Server(), аналогично для подключения к сгенерированному клиенту service.Client() и т. д. При наличии proto‑файла и сгенерированного описания методов и протокола взаимодействия с микросервисом, вызов может выглядеть следующим образом (в pb импортирован структура, сгенерированная protoc‑gen‑go)

	if err := pb.RegisterPaymentServiceHandler(srv.Server(), new(handler.PaymentService)); err != nil {
		logger.Fatal(err)
	}

При этом сам proto-файл описывает все аспекты взаимодействия с микросервисом и определяет дополнительные типы данных (перечисления, структуры и др.), например:

syntax = "proto3";

package shop;

option go_package = "./proto;shop";

service Health {
  rpc Check(HealthCheckRequest) returns (HealthCheckResponse) {}
  rpc Watch(HealthCheckRequest) returns (stream HealthCheckResponse) {}
}

message HealthCheckRequest { 
	string service = 1;
}

message HealthCheckResponse {
  enum ServingStatus {
    UNKNOWN = 0;
    SERVING = 1;
    NOT_SERVING = 2;
    SERVICE_UNKNOWN = 3;
  }
  ServingStatus status = 1;
}

При использовании реестров сервисов можно делать дополнительные обработки (например, создавать реализации селекторов для поиска подходящих сервисов). Также достаточно просто интегрируются очереди сообщений:

package main

import (
	"fmt"

	"context"
	"go-micro.dev/v4"
)

func pub(i int, p micro.Publisher) {
	msg := &Message{
		Say: fmt.Sprintf("This is an async message %d", i),
	}

	if err := p.Publish(context.TODO(), msg); err != nil {
		fmt.Println("pub err: ", err)
		return
	}

	fmt.Printf("Published %d: %v\n", i, msg)
}

func main() {
	service := micro.NewService()
	service.Init()

	p := micro.NewPublisher("example", service.Client())

	for i := 0; i < 10; i++ {
		pub(i, p)
	}
}

Аналогично можно создавать распределенные конфигурации, обнаруживать сервисы (через registry), создавать клиентов RPC, подписываться на появление сообщений в очередях RabbitMQ/Kafka. Также доступны механизмы для конфигурирования heartbeat (для проверки доступности и корректного функционирования сервисов):

	service := micro.NewService(
		micro.Name("payment"),
  		micro.RegisterInterval(time.Second*30),
		micro.RegisterTTL(time.Second*120),
	)

Дополнительно можно подключать собственные расширения с использованием micro.WrapHandler:

import (
    "log"
	"go-micro.dev/v4"
	"go-micro.dev/v4/server"
)

func logWrapper(fn server.HandlerFunc) server.HandlerFunc {
	return func(ctx context.Context, req server.Request, rsp interface{}) error {
		log.Printf("[request]: %v", req.Endpoint())
		err := fn(ctx, req, rsp)
		return err
	}
}

func main() {
	service := micro.NewService(
		micro.Name("greeter"),
		// wrap the handler
		micro.WrapHandler(logWrapper),
	)

	service.Init()

	proto.RegisterHandler(service.Server(), new(PaymentService))

	if err := service.Run(); err != nil {
		fmt.Println(err)
	}
}

Для мониторинга запущенных микросервисов может использоваться GoMicro Dashboard, который может быть установлен через go install github.com/go-micro/dashboard@latest.

Таким образом, MicroGo представляет легковесный и расширяемый фреймворк для разработки микросервисов, который обеспечивает разрабатываемые компоненты основными возможностями для безопасного доступа и обнаружения других микросервисов, хранением данных, взаимодействию через HTTP/gRPC или очереди сообщений и другими важными аспектами для реализации микросервисной архитектуры.

В завершение приглашаю на бесплатный урок, где мы рассмотрим плюсы и минусы монолитов и микросервисов, а также основные паттерны в микросервисной архитектуре.

Комментарии (4)


  1. Arkham
    00.00.0000 00:00

    Выглядит заманчиво, спасибо.

    Ещё бы Demo с 2/3 готовыми микросервисами, было бы совсем хорошо :)


    1. dmitriizolotov Автор
      00.00.0000 00:00
      +1

      У проекта есть официальное демо с десятками сервисов разных видов https://github.com/go-micro/demo, может быть полезно


  1. ring0
    00.00.0000 00:00

    Спасибо за пост. В первом примере скорее всего ошибка в строке:
    service.Server().handle()


  1. ReDev1L
    00.00.0000 00:00

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