Micro в действии, часть 2: полное руководство по Bootstrap
Это вторая статья в серии статей «Micro в действии», посвященной Micro. Мы шаг за шагом создадим микросервис и объясним особенности Micro на этом пути. Мы начнем с основных понятий и тем, а затем перейдем к расширенным функциям.
Макет проекта
В прошлой статье мы создали простой проект и запустили его. В этой статье мы продолжаем это путешествие, сначала представляя структуру проекта и назначение каждого из файлов.
Примечание: поскольку тема этой серии статей - Micro, нерелевантные темы будут проигнорированы, например, лучшие практики для макета проекта, как подключиться к базе данных и внедрение зависимостей и т. д. Поэтому мы будем объяснять только содержание проекта, как оно есть, без внесения каких-либо несущественных изменений.
Компоновка проекта следующая:
.
├── main.go
├── generate.go
├── plugin.go
├── proto/hello
│ └── hello.proto
│ └── hello.pb.go
│ └── hello.pb.micro.go
├── handler
│ └── hello.go
├── subscriber
│ └── hello.go
├── Dockerfile
├── README.md
├── .gitignore
└── go.mod
Вот список файлов в проекте:
- main.go - главный файл проекта, который будет подробно описан позже.
- generate.go - содержит только одну строку
//go:generate make proto
которая интегрируется сgo generate
. Убедитесь, что она будет вызываться автоматически во время выполненияgo generate
- plugins.go - этот файл в настоящее время пуст. Согласно соглашению Micro, здесь рекомендуется управлять всем импортом плагинов, которые будут использоваться позже.
- proto/hello/hello.proto - файл определения службы gRPC, который определяет службу RPC с именем
Hello
. В этом сервисе заявлены 3 типичных метода RPC: одинарный RPC, серверный потоковый RPC и двунаправленный потоковый RPC. - proto/hello/hello.pb.go - исходный файл golang, созданный
protoc
из вышеупомянутого файла.proto
- proto/hello/hello.pb.micro.go - исходный файл golang, созданный с помощью
protoc-gen-micro
. Это еще больше упрощает работу разработчика. Он определяет интерфейсыHelloSerivce
иHelloHandler
. Последний - это интерфейс, который нам нужен для реализации и завершения нашей бизнес-логики. - handler/hello.go - файл, в котором реализована бизнес-логика. Он определяет объект
Hello
, реализующий интерфейсHelloHandler
. - subscriber/hello.go - файл, который реализует асинхронную подписку и обработку сообщений. Он показывает два разных способа обработки сообщений: один - с помощью метода объекта, а другой - с помощью функции.
- Dockerfile - определяет, как создавать Docker образы.
- Makefile - определяет несколько общих задач, компиляцию, тестирование, сборку образа Docker и т.д.
- README.md - содержит основную информацию о проекте
- .gitignore - игнорировать hello-service по умолчанию
- go.mod - файл модуля Go
Примечание: папка proto
имеет особое значение. Хотя нет никаких технических ограничений, по соглашению Micro, папка proto
в корневом каталоге каждого проекта используется для хранения «интерфейсных» файлов. Это включает как интерфейсы, которые проект должен предоставить внешнему миру, так и другие интерфейсы, от которых зависит проект. Например, предположим, что нам нужно полагаться на другую службу foo для реализации бизнес-логики. Затем мы создадим папку с именем proto/foo
и поместим в нее три файла foo.proto
, foo.pb.go
, foo.pb.micro.go
.
Объяснение для Bootstrap
Давайте посмотрим на процесс начальной загрузки main.go:
package main
import (
"github.com/micro/go-micro/v2"
log "github.com/micro/go-micro/v2/logger"
"hello/handler"
"hello/subscriber"
hello "hello/proto/hello"
)
func main() {
// New Service
service := micro.NewService(
micro.Name("com.foo.service.hello"),
micro.Version("latest"),
)
// Initialise service
service.Init()
// Register Handler
hello.RegisterHelloHandler(service.Server(), new(handler.Hello))
// Register Struct as Subscriber
micro.RegisterSubscriber("com.foo.service.hello", service.Server(), new(subscriber.Hello))
// Run service
if err := service.Run(); err != nil {
log.Fatal(err)
}
}
Код примерно разделен на 4 части: импорт зависимостей, создание и инициализация службы, регистрация обработчиков бизнес-обработки и запуск службы.
Импорт зависимостей
Отдельно стоит пояснить только одну строчку кода в этой части:
hello "hello/proto/hello"
Мы устанавливаем явное имя пакета hello
на hello/proto/hello
. Это также соглашение Micro: установить явные имена пакетов для всех пакетов импорта интерфейса. Это позволяет избежать использования исходного имени пакета импортированного кода. На практике, если вы не сделаете специальных настроек, имя пакета автоматически сгенерированного кода будет относительно длинным. Возьмем к примеру hello.pb.go
, имя его пакета com_foo_srv_hello
. Очевидно, что установка явного имени пакета - лучший выбор.
Создание и инициализация сервисов
// New Service
service := micro.NewService(
micro.Name("com.foo.srv.hello"),
micro.Version("latest"),
)
Мы используем метод micro.NewService(opts …Option) Service
для создания сервиса. Этот метод принимает в качестве параметра micro.Option
, затем создает и возвращает экземпляр micro.Service
По-видимому micro.Option
, это ключ к управлению сервисом. В приведенном выше примере кода используются параметры для указания имени службы и номера версии соответственно.
В настоящее время доступно 29 опций для управления всеми аспектами службы. Некоторые параметры можно указывать несколько раз для формирования эффекта наложения (будет описано позже).
Однако документации для таких важных опций нет. Если вы хотите учиться, копание исходного кода - единственный способ. И большая часть исходного кода не имеет комментариев, что еще больше усложняет изучение.
Хотя эта статья не является исчерпывающим справочным руководством для Micro, я решил перечислить все 29 вариантов Micro v2.4.0 и объяснить их один за другим, как показано ниже. Поскольку эти параметры очень важны для понимания и использования Micro, и нет другого материала, на который можно было бы ссылаться.
- micro.Name(n string) Option - Укажите имя службы. Обычно соглашение об именах
$namespace. $type. $name
.$namespace
представляет пространство имен проекта и$type
тип службы (например, gRPC и веб). Тип службы gRPC обычно сокращается доsrv
. После запуска экземпляра службы это имя будет автоматически зарегистрировано вRegistry
, что станет основой для обнаружения службы. По умолчанию этоgo.micro.server
. Примечание: Следовательно, этот параметр необходимо указать, иначе все узлы будут использовать одно и то же имя по умолчанию, что приведет к путанице. - micro.Version(v string) Option - Укажите версию сервиса. Значение по умолчанию - это форматированная строка с момента запуска. при правильном выборе номера версии в сочетании с правильностью
Selector
мы можем реализовать элегантное обновление с вращением, выпуск в оттенках серого, A/B-тестирование и многие другие операции. - micro.Address(addr string) Option - Задает адрес службы gRPC. По умолчанию используется адрес localhost в сочетании со случайным портом. Поскольку клиенты обнаруживают службы через
Registry
, случайный порт не влияет на это обнаружение. Однако на практике часто указывается фиксированный номер порта, что будет полезно для работы и контроля безопасности. - micro.RegisterTTL(t time.Duration) Option - Укажите TTL регистрационной информации службы в реестре. Значение по умолчанию - 1 минута.
- micro.RegisterInterval(t time.Duration) Option - Определяет интервал, через который служба сообщает о своем состоянии в реестр. Значение по умолчанию - 30 секунд. Эти два параметра, относящиеся к реестру, помогают избежать «неверной регистрационной информации», когда служба обнаруживает непредвиденный простой.
- micro.WrapHandler(w …server.HandlerWrapper) Option - Обернуть обработчик службы. Он концептуально похож на Gin Middleware и централизованно контролирует поведение обработчика. Обертку можно применять на нескольких уровнях, а порядок выполнения - снаружи внутрь (пример будет представлен в следующей статье).
- micro.WrapSubscriber(w …server.SubscriberWrapper) Option - Аналогичен WrapHandler, за исключением того, что он используется для обертывания подписчиков в асинхронном обмене сообщениями.
- micro.WrapCall(w …client.CallWrapper) Option - Оборачивает каждый вызов метода от клиента.
- micro.WrapClient(w …client.Wrapper) Option - Обернуть клиента службы, может применяться на нескольких уровнях, и порядок выполнения - изнутри наружу.
- micro.BeforeStart(fn func() error) Option - Установите несколько функций обратного вызова перед запуском службы.
- micro.BeforeStop(fn func() error) Option - Установите несколько функций обратного вызова до остановки службы.
- micro.AfterStart(fn func() error) Option - Установите несколько функций обратного вызова после запуска службы.
- micro.AfterStop(fn func() error) Option - Установить несколько функций обратного вызова после остановки службы
- micro.Action(a func(*cli.Context)) Option - Обрабатывать аргументы командной строки. Поддерживает подкоманды и флаги. см микро / CLI для получения подробной информации
- micro.Flags(flags …cli.Flag) Option - Обрабатывать флаги командной строки. см микро / CLI для получения подробной информации
- micro.Cmd(c cmd.Cmd) Option - Определяет объект обработки командной строки. Созданный по умолчанию этот объект поддерживает несколько переменных среды по умолчанию и параметры командной строки. По сути, это встроенный набор cli.Flag. Примечание. Что касается поддержки командной строки, у Micro есть как преимущества, так и недостатки одновременно. Преимущество в том, что он предоставляет множество параметров по умолчанию, что может сэкономить время разработчиков. Недостатком является то, что этот дизайн сильно инвазивен для пользовательских программ: среда требует, чтобы разработчики обрабатывали свои аргументы командной строки единообразно в соответствии с micro/cli. В противном случае программа сообщит об ошибке и не сможет работать. Например, если мы выполним
./hello-service --foo=bar
, мы получим ошибоку:Incorrect Usage. Flag provided but not defined: --foo=bar
. К счастью,micro.Cmd
может решить проблему, вызванную навязчивостью. Если существующий проект хочет представить Micro и у него уже есть собственный механизм обработки командной строки, то вам необходимо использовать его для переопределения поведения по умолчанию (при потере возможностей обработки командной строки по умолчанию). Что касается обработки из командной строки, дальнейшее объяснение будет предоставлено позже в этой статье. - micro.Metadata(md map[string]string) Option - Укажите метаданные службы. Метаданные часто используются для тегирования и группировки сервисов, реализации пользовательской стратегии балансировки нагрузки и т.д.
- micro.Transport(t transport.Transport) Option - Укажите транспортный протокол, по умолчанию - HTTP
- micro.Selector(s selector.Selector) Option - Укажите селектор узла для реализации различных стратегий загрузки. По умолчанию - случайный выбор
- micro.Registry(r registry.Registry) Option - Укажите, какой
Registry
используется для обнаружения службы. По умолчанию используется mDNS-basedRegistry
- micro.Server(s server.Server) Option - Укажите индивидуальный
Server
, если сервер по умолчанию не соответствует вашим требованиям. По умолчаниюrpcServer
- micro.HandleSignal(b bool) Option - Включает автоматическую установку обработчика сигналов, который перехватывает TERM, INT и QUIT. По умолчанию
true
- micro.Context(ctx context.Context) Option - Укажите начальный контекст службы. По умолчанию это значение
context.BackGround()
, которое можно использовать для управления сроком службы и т.д. - micro.Client(c client.Client) Option - Укажите клиента службы. По умолчанию
rpcClient
- micro.Broker(b broker.Broker) Option - Укажите брокер сообщений, используемый pub/sub. По умолчанию - HTTP-брокер
- micro.Profile(p profile.Profile) Option - Профиль, который будет использоваться для профиля отладки
- micro.Tracer(t trace.Tracer) Option - Tracer устанавливает трассировщик для службы
- micro.Auth(a auth.Auth) Option - Auth устанавливает авторизацию для службы. (согласно официальному Slask этот API недостаточно стабилен, не готов к продакшену на v2.4.0)
- micro.Config(c config.Config) Option - Config устанавливает конфигурацию для службы
Таким образом, указав соответствующий параметр Option
, можно настроить поведение службы. Например, чтобы изменить TTL регистрационной информации службы:
...
// New Service
service := micro.NewService(
micro.Name("foo.bar"),
micro.Version("v1.0"),
// change default TTL value
micro.RegisterTTL(5 * time.Minute),
...
)
...
Примечание. Большинство из вышеперечисленных параметров можно указать несколькими способами. Жесткое кодирование в исходном файле - лишь один из способов. Фактически, Micro рекомендует пользователям указывать параметры через переменные среды, поскольку это обеспечивает большую гибкость. Взяв micro.RegisterTTL
в качестве примера, мы можем указать его во время выполнения через переменную среды $ MICRO_REGISTER_TTL=value
или флаг командной строки --register_ttl=value
(значение в секундах). Для этих встроенных опций в настоящее время нет документации. Выполнение ./hello-service -h
покажет их краткое описание. Если вы хотите узнать все подробности, раскопайте источник newCmd сами. В последующих статьях этой серии эта тема будет более подробно раскрыта.
После создания вы можете инициализировать службу:
// Initialize service
service.Init()
Метод service.Init
может принимать одни и те же параметры, как и micro.NewService
. Таким образом, вышеуказанные 29 вариантов также могут быть использованы в service.Init
. Они имеют одинаковый эффект, но различаются только по времени.
Поскольку на этом этапе служба была создана, мы можем получить информацию из экземпляра службы. Например, вы можете прочитать случайный порт:
// Initialize service
service.Init(
// print log after start
micro.AfterStart(func() error {
log.Infof("service listening on %s!",
service.Options().Server.Options().Address,
)
return nil
}),
)
Регистрация бизнес-обработчиков
// Register Handler
hello.RegisterHelloHandler(service.Server(), new(handler.Hello))
// Register Struct as Subscriber
micro.RegisterSubscriber("com.foo.service.hello", service.Server(), new(subscriber.Hello))
Только после регистрации обработчика наш бизнес-код может действительно предоставлять услуги внешнему миру. Вот две типичные операции регистрации:
- Зарегистрируйте обработчик gRPC. Создайте объект
handler.Hello
и зарегистрируйте его наServer
. Посколькуhandler.Hello
реализует интерфейсHelloHandler
, его можно передатьhello.RegisterHelloHandler
, иначе произойдет ошибка. НесколькоHandlers
могут быть зарегистрированы для выполненияServer
различных бизнес-функций. - Зарегистрируйте объект обработки сообщений. Первый параметр - это тема сообщения, второй параметр - это
Server
, а третий параметр - объект обработки сообщения.
Подробнее об асинхронном обмене сообщениями мы поговорим позже в специальной статье.
Запустить сервис
if err := service.Run(); err != nil {
log.Fatal(err)
}
На этом этапе служба наконец запускается
Проверить статус выполнения
Как упоминалось в предыдущей статье, инструмент командной строки micro
можно использовать для проверки и управления службами во время выполнения. Давай попробуем.
После запуска службы запустите micro web
:
$ micro web
2020-04-02 17:16:48 level=info service=web HTTP API Listening on [::]:8082
2020-04-02 17:16:48 level=info service=web Starting [service] go.micro.web
2020-04-02 17:16:48 level=info service=web Server [grpc] Listening on [::]:60537
2020-04-02 17:16:48 level=info service=web Registry [mdns] Registering node: go.micro.web-dc1270ba-8b1f-4f05-92ec-57fb450efaaa
Как видите, сервер прослушивает локальный хост, порт 8082.
Примечание. Порт 8082 является значением по умолчанию и может быть изменен с помощью переменных среды или флага командной строки.
Затем посетите http://127.0.0.1:8082/service/com.foo.service.hello, чтобы просмотреть статус службы. Ниже скриншот:
На картинке выше мы можем просмотреть различную информацию об услуге:
- Наименование услуги
- Список сервисных узлов. Если несколько узлов работают одновременно, вы увидите их все.
- Информация о каждом узле: версия, уникальный идентификатор, адрес, метаданные и т.д.
- Конечные точки. Определение службы, имя службы gRPC, имя метода, информация о параметрах, тип данных и т.д.
Так различный статус выполнения службы могут быть легко проверены micro web
.