0. Почему сложно хорошо делать микросервисы?
Чтобы преуспеть в микросервисах, нам нужно понять и освоить множество знаний в нескольких измерениях:
-
базовый функциональный уровень
- Параллельный контроль и ограничение тока для предотвращения перегрузки служб из-за резкого увеличения трафика
- Регистрация службы и обнаружение службы для обеспечения динамического обнаружения увеличения или уменьшения количества узлов.
- Балансировка нагрузки, которая должна распределять трафик в соответствии с пропускной способностью узлов.
- Контроль тайм-аута, чтобы избежать выполнения бесполезной работы по тайм-ауту запросов
- Конструкция предохранителей, быстрая отказоустойчивость и обеспечение отказоустойчивости неисправных узлов
-
Расширенный функциональный уровень
- Запросить аутентификацию, чтобы гарантировать, что каждый пользователь может получить доступ только к своим данным.
- Отслеживание ссылок для понимания всей системы и быстрого выявления проблем с конкретными запросами
- Журналы для сбора данных и определения местоположения проблемы
- Наблюдаемость, нет оптимизации без метрик
Для каждого из этих пунктов нам нужно использовать длинное место для описания принципа и реализации.Нашим бэкенд-разработчикам очень сложно освоить и внедрить эти точки знаний в бизнес-систему., но мы можем положиться на фреймворк, проверенный большим трафиком.фреймворк микросервисов с нуляродился для этого.
Кроме того, мы всегда придерживаемсяИнструменты перевешивают соглашения и документациюконцепция. Мы хотим максимально снизить умственную нагрузку разработчиков, вложить всю энергию в код, создающий ценность для бизнеса, и сократить написание повторяющегося кода, поэтому мы разработалиgoctl
инструмент.
Ниже я использую сервис книжного магазина, чтобы продемонстрировать черезgo-zeroБыстро создайте процесс микросервисов.Пройдясь по нему, вы обнаружите, что писать микросервисы так просто!
1. Введение в пример услуги книжного магазина
Для простоты урока мы используем в качестве примера сервис книжного магазина и реализуем только функции добавления библиографии и проверки цен.
Этот сервис книжного магазина написан для демонстрации процесса построения полного микросервиса с помощью go-zero в целом, а детали реализации максимально упрощены.
2. Схема архитектуры микросервиса книжного магазина
3. Список генерации кода на каждом уровне goctl
Все функциональные модули с зеленым фоном автоматически генерируются и активируются по запросу.Красные модули нужно писать сами по себе, то есть добавлять нижние зависимости и писать бизнес-специфическую логику.Схемы каждого слоя следующие:
- API Gateway
- RPC
- model
Давайте полностью рассмотрим процесс быстрого создания микросервисов.Go
!🏃♂️
4. Подготовка
-
Установить etcd, mysql, redis
-
Установите инструмент goctl
GO111MODULE=on GOPROXY=https://goproxy.cn/,direct go get -u github.com/tal-tech/go-zero/tools/goctl
-
Создать рабочий каталог
bookstore
-
существует
bookstore
Выполнить в каталогеgo mod init bookstore
инициализацияgo.mod
5. Напишите код шлюза API
-
существует
bookstore/api
Генерируется goctl в каталогеapi/bookstore.api
:goctl api -o bookstore.api
редактировать
bookstore.api
, для краткости уберите начало файлаinfo
, код показан ниже:type ( addReq struct { book string `form:"book"` price int64 `form:"price"` } addResp struct { ok bool `json:"ok"` } ) type ( checkReq struct { book string `form:"book"` } checkResp struct { found bool `json:"found"` price int64 `json:"price"` } ) service bookstore-api { @server( handler: AddHandler ) get /add(addReq) returns(addResp) @server( handler: CheckHandler ) get /check(checkReq) returns(checkResp) }
Использование type такое же, как и у go, а service используется для определения запросов API, таких как get/post/head/delete.Объяснение следующее:
-
service bookstore-api {
Эта строка определяет имя службы -
@server
Часть используется для определения свойств, используемых на стороне сервера. -
handler
Определяет имя обработчика сервера -
get /add(addReq) returns(addResp)
Определяет маршрут, параметры запроса, возвращаемые параметры и т. д. метода get.
-
-
Сгенерировать код шлюза API с помощью goctl
goctl api go -api bookstore.api -dir .
В результате структура файла выглядит следующим образом:
api ├── bookstore.api // api定义 ├── bookstore.go // main入口定义 ├── etc │ └── bookstore-api.yaml // 配置文件 └── internal ├── config │ └── config.go // 定义配置 ├── handler │ ├── addhandler.go // 实现addHandler │ ├── checkhandler.go // 实现checkHandler │ └── routes.go // 定义路由处理 ├── logic │ ├── addlogic.go // 实现AddLogic │ └── checklogic.go // 实现CheckLogic ├── svc │ └── servicecontext.go // 定义ServiceContext └── types └── types.go // 定义请求、返回结构体
-
Запустите службу шлюза API, которая по умолчанию прослушивает порт 8888.
go run bookstore.go -f etc/bookstore-api.yaml
-
Протестируйте службу шлюза API
curl -i "http://localhost:8888/check?book=go-zero"
Возврат следующим образом:
HTTP/1.1 200 OK Content-Type: application/json Date: Thu, 03 Sep 2020 06:46:18 GMT Content-Length: 25 {"found":false,"price":0}
Вы можете видеть, что наш API-шлюз ничего не сделал и вернул нулевое значение.Далее мы реализуем бизнес-логику в службе rpc.
-
можно изменить
internal/svc/servicecontext.go
для передачи сервисных зависимостей (при необходимости) -
Логика реализации может быть изменена
internal/logic
соответствующий файл под -
в состоянии пройти
goctl
Создание кода вызова API на различных языках клиента -
На этом этапе вы можете генерировать клиентский код через goctl для параллельной разработки клиентских студентов, поддерживать несколько языков, подробности см. в документации.
6. Напишите добавить службу rpc
-
существует
rpc/add
написать в каталогadd.proto
документШаблон файла proto можно сгенерировать командой
goctl rpc template -o add.proto
Содержимое модифицированного файла следующее:
syntax = "proto3"; package add; message addReq { string book = 1; int64 price = 2; } message addResp { bool ok = 1; } service adder { rpc add(addReq) returns(addResp); }
-
использовать
goctl
Сгенерируйте код rpc, вrpc/add
Выполните команду в каталогеgoctl rpc proto -src add.proto
Структура файла следующая:
rpc/add ├── add.go // rpc服务main函数 ├── add.proto // rpc接口定义 ├── adder │ ├── adder.go // 提供了外部调用方法,无需修改 │ ├── adder_mock.go // mock方法,测试用 │ └── types.go // request/response结构体定义 ├── etc │ └── add.yaml // 配置文件 ├── internal │ ├── config │ │ └── config.go // 配置定义 │ ├── logic │ │ └── addlogic.go // add业务逻辑在这里实现 │ ├── server │ │ └── adderserver.go // 调用入口, 不需要修改 │ └── svc │ └── servicecontext.go // 定义ServiceContext,传递依赖 └── pb └── add.pb.go
Его можно запустить напрямую следующим образом:
$ go run add.go -f etc/add.yaml
Starting rpc server at 127.0.0.1:8080...
etc/add.yaml
Вы можете изменить конфигурацию, такую как порт прослушивания в файле
7. Написать проверку службы rpc
-
существует
rpc/check
написать в каталогcheck.proto
документШаблон файла proto можно сгенерировать командой
goctl rpc template -o check.proto
Содержимое модифицированного файла следующее:
syntax = "proto3"; package check; message checkReq { string book = 1; } message checkResp { bool found = 1; int64 price = 2; } service checker { rpc check(checkReq) returns(checkResp); }
-
использовать
goctl
Сгенерируйте код rpc, вrpc/check
Выполните команду в каталогеgoctl rpc proto -src check.proto
Структура файла следующая:
rpc/check ├── check.go // rpc服务main函数 ├── check.proto // rpc接口定义 ├── checker │ ├── checker.go // 提供了外部调用方法,无需修改 │ ├── checker_mock.go // mock方法,测试用 │ └── types.go // request/response结构体定义 ├── etc │ └── check.yaml // 配置文件 ├── internal │ ├── config │ │ └── config.go // 配置定义 │ ├── logic │ │ └── checklogic.go // check业务逻辑在这里实现 │ ├── server │ │ └── checkerserver.go // 调用入口, 不需要修改 │ └── svc │ └── servicecontext.go // 定义ServiceContext,传递依赖 └── pb └── check.pb.go
etc/check.yaml
Вы можете изменить конфигурацию, такую как порт прослушивания в файленеобходимо изменить
etc/check.yaml
Порт8081
,так как8080
Былadd
Служба используется и может быть запущена напрямую следующим образом:$ go run check.go -f etc/check.yaml Starting rpc server at 127.0.0.1:8081...
8. Измените код шлюза API для вызова службы добавления/проверки rpc.
-
Изменить файл конфигурации
bookstore-api.yaml
, добавьте следующееAdd: Etcd: Hosts: - localhost:2379 Key: add.rpc Check: Etcd: Hosts: - localhost:2379 Key: check.rpc
Автоматически обнаруживать доступные сервисы добавления/проверки через etcd
-
Исправлять
internal/config/config.go
Как показано ниже, добавьте/проверьте зависимости службы.type Config struct { rest.RestConf Add rpcx.RpcClientConf // 手动代码 Check rpcx.RpcClientConf // 手动代码 }
-
Исправлять
internal/svc/servicecontext.go
,следующее:type ServiceContext struct { Config config.Config Adder adder.Adder // 手动代码 Checker checker.Checker // 手动代码 } func NewServiceContext(c config.Config) *ServiceContext { return &ServiceContext{ Config: c, Adder: adder.NewAdder(rpcx.MustNewClient(c.Add)), // 手动代码 Checker: checker.NewChecker(rpcx.MustNewClient(c.Check)), // 手动代码 } }
Передайте зависимости между различной бизнес-логикой через ServiceContext
-
Исправлять
internal/logic/addlogic.go
внутреннийAdd
Методы, как показано ниже:func (l *AddLogic) Add(req types.AddReq) (*types.AddResp, error) { // 手动代码开始 resp, err := l.svcCtx.Adder.Add(l.ctx, &adder.AddReq{ Book: req.Book, Price: req.Price, }) if err != nil { return nil, err } return &types.AddResp{ Ok: resp.Ok, }, nil // 手动代码结束 }
позвонив
adder
изAdd
Метод реализует добавление книг в систему книжного магазина. -
Исправлять
internal/logic/checklogic.go
внутреннийCheck
Методы, как показано ниже:func (l *CheckLogic) Check(req types.CheckReq) (*types.CheckResp, error) { // 手动代码开始 resp, err := l.svcCtx.Checker.Check(l.ctx, &checker.CheckReq{ Book: req.Book, }) if err != nil { return nil, err } return &types.CheckResp{ Found: resp.Found, Price: resp.Price, }, nil // 手动代码结束 }
позвонив
checker
изCheck
Метод реализует запрос цены книг из системы книжного магазина.
9. Определить структуру таблицы базы данных и сгенерировать код CRUD+cache.
-
Создано в рамках книжного магазина
rpc/model
содержание:mkdir -p rpc/model
-
Напишите файл sql для создания таблицы book в каталоге rpc/model.
book.sql
,следующее:CREATE TABLE `book` ( `book` varchar(255) NOT NULL COMMENT 'book name', `price` int NOT NULL COMMENT 'book price', PRIMARY KEY(`book`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
-
Создать БД и таблицу
create database gozero;
source book.sql;
-
существует
rpc/model
Выполните следующую команду в каталоге, чтобы сгенерировать код CRUD+cache,-c
указать использованиеredis cache
goctl model mysql ddl -c -src book.sql -dir .
также можно использовать
datasource
команда вместо этогоddl
чтобы указать, что ссылка на базу данных генерируется непосредственно из схемыСгенерированная файловая структура выглядит следующим образом:
rpc/model ├── bookstore.sql ├── bookstoremodel.go // CRUD+cache代码 └── vars.go // 定义常量和变量
10. Измените код добавления/проверки rpc для вызова кода crud+cache.
-
Исправлять
rpc/add/etc/add.yaml
а такжеrpc/check/etc/check.yaml
, добавьте следующее:DataSource: root:@tcp(localhost:3306)/gozero Table: book Cache: - Host: localhost:6379
Может использовать несколько Redis в качестве кеша, поддерживать единую точку Redis или кластер Redis.
-
Исправлять
rpc/add/internal/config.go
а такжеrpc/check/internal/config.go
,следующее:type Config struct { rpcx.RpcServerConf DataSource string // 手动代码 Table string // 手动代码 Cache cache.CacheConf // 手动代码 }
Добавлена конфигурация кеша mysql и redis.
-
Исправлять
rpc/add/internal/svc/servicecontext.go
а такжеrpc/check/internal/svc/servicecontext.go
,следующее:type ServiceContext struct { c config.Config Model *model.BookModel // 手动代码 } func NewServiceContext(c config.Config) *ServiceContext { return &ServiceContext{ c: c, Model: model.NewBookModel(sqlx.NewMysql(c.DataSource), c.Cache, c.Table), // 手动代码 } }
-
Исправлять
rpc/add/internal/logic/addlogic.go
,следующее:func (l *AddLogic) Add(in *add.AddReq) (*add.AddResp, error) { // 手动代码开始 _, err := l.svcCtx.Model.Insert(model.Book{ Book: in.Book, Price: in.Price, }) if err != nil { return nil, err } return &add.AddResp{ Ok: true, }, nil // 手动代码结束 }
-
Исправлять
rpc/check/internal/logic/checklogic.go
,следующее:func (l *CheckLogic) Check(in *check.CheckReq) (*check.CheckResp, error) { // 手动代码开始 resp, err := l.svcCtx.Model.FindOne(in.Book) if err != nil { return nil, err } return &check.CheckResp{ Found: true, Price: resp.Price, }, nil // 手动代码结束 }
На этом модификация кода завершена, и я пометил измененный вручную код для всего.
11. Демонстрация полного звонка
-
добавить вызов API
curl -i "http://localhost:8888/add?book=go-zero&price=10"
Возврат следующим образом:
HTTP/1.1 200 OK Content-Type: application/json Date: Thu, 03 Sep 2020 09:42:13 GMT Content-Length: 11 {"ok":true}
-
проверить вызов API
curl -i "http://localhost:8888/check?book=go-zero"
Возврат следующим образом:
HTTP/1.1 200 OK Content-Type: application/json Date: Thu, 03 Sep 2020 09:47:34 GMT Content-Length: 25 {"found":true,"price":10}
12. Benchmark
Поскольку запись зависит от скорости записи mysql, она эквивалентна нажатию mysql, поэтому тест давления проверяет только интерфейс проверки, что эквивалентно чтению из mysql и использованию кеша.Для удобства нажмите прямо на эту книгу, потому что есть Кэш, одинаковый для нескольких книг, не влияет на результаты стресс-теста.
Перед нагрузочным тестированием увеличим количество дескрипторов открытых файлов:
ulimit -n 20000
И уровень лога меняется наerror
, чтобы слишком много информации не влияло на результаты теста под давлением, добавьте следующее в каждый файл конфигурации yaml:
Log:
Level: error
Видно, что я могу достичь 30 000+ запросов в секунду на своем MacBook Pro.
13. Полный код
14. Резюме
Мы всегда подчеркивалиИнструменты перевешивают соглашения и документацию.
go-zero — это не просто фреймворк, но и техническая система на основе фреймворка + инструменты, которая упрощает и стандартизирует всю конструкцию микросервиса.
Сохраняя простоту, мы также стараемся инкапсулировать сложность управления микросервисами в структуру, что значительно снижает умственную нагрузку разработчиков и обеспечивает быстрое развитие бизнеса.
Код, сгенерированный go-zero+goctl, содержит различные компоненты управления микросервисами, в том числе: управление параллелизмом, адаптивный прерыватель цепи, адаптивное снижение нагрузки, автоматическое управление кешем и т. д., и его можно легко развернуть для обработки огромного трафика.
Если у вас есть хорошие идеи по повышению инженерной эффективности, пожалуйста, не стесняйтесь общаться! 👏
15. Адрес проекта
16. Группа общения WeChat
Добавьте мой WeChat: kevwan, пожалуйста, укажите go-zero, я присоединюсь к группе сообщества go-zero 🤝
ТАЛ Технология