Вы все еще разрываете микросервисы? Давайте попробуем автоматическую генерацию микросервисов от go-zero.

Архитектура Go распределенный Kubernetes

0. Почему сложно хорошо делать микросервисы?

Чтобы преуспеть в микросервисах, нам нужно понять и освоить множество знаний в нескольких измерениях:

  • базовый функциональный уровень

    1. Параллельный контроль и ограничение тока для предотвращения перегрузки служб из-за резкого увеличения трафика
    2. Регистрация службы и обнаружение службы для обеспечения динамического обнаружения увеличения или уменьшения количества узлов.
    3. Балансировка нагрузки, которая должна распределять трафик в соответствии с пропускной способностью узлов.
    4. Контроль тайм-аута, чтобы избежать выполнения бесполезной работы по тайм-ауту запросов
    5. Конструкция предохранителей, быстрая отказоустойчивость и обеспечение отказоустойчивости неисправных узлов
  • Расширенный функциональный уровень

    1. Запросить аутентификацию, чтобы гарантировать, что каждый пользователь может получить доступ только к своим данным.
    2. Отслеживание ссылок для понимания всей системы и быстрого выявления проблем с конкретными запросами
    3. Журналы для сбора данных и определения местоположения проблемы
    4. Наблюдаемость, нет оптимизации без метрик

Для каждого из этих пунктов нам нужно использовать длинное место для описания принципа и реализации.Нашим бэкенд-разработчикам очень сложно освоить и внедрить эти точки знаний в бизнес-систему., но мы можем положиться на фреймворк, проверенный большим трафиком.фреймворк микросервисов с нуляродился для этого.

Кроме того, мы всегда придерживаемсяИнструменты перевешивают соглашения и документациюконцепция. Мы хотим максимально снизить умственную нагрузку разработчиков, вложить всю энергию в код, создающий ценность для бизнеса, и сократить написание повторяющегося кода, поэтому мы разработалиgoctlинструмент.

Ниже я использую сервис книжного магазина, чтобы продемонстрировать черезgo-zeroБыстро создайте процесс микросервисов.Пройдясь по нему, вы обнаружите, что писать микросервисы так просто!

1. Введение в пример услуги книжного магазина

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

Этот сервис книжного магазина написан для демонстрации процесса построения полного микросервиса с помощью go-zero в целом, а детали реализации максимально упрощены.

2. Схема архитектуры микросервиса книжного магазина

architecture

3. Список генерации кода на каждом уровне goctl

Все функциональные модули с зеленым фоном автоматически генерируются и активируются по запросу.Красные модули нужно писать сами по себе, то есть добавлять нижние зависимости и писать бизнес-специфическую логику.Схемы каждого слоя следующие:

  • API Gateway

api

  • RPC

rpc

  • model

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

benchmark

Видно, что я могу достичь 30 000+ запросов в секунду на своем MacBook Pro.

13. Полный код

GitHub.com/them-specialty/go…

14. Резюме

Мы всегда подчеркивалиИнструменты перевешивают соглашения и документацию.

go-zero — это не просто фреймворк, но и техническая система на основе фреймворка + инструменты, которая упрощает и стандартизирует всю конструкцию микросервиса.

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

Код, сгенерированный go-zero+goctl, содержит различные компоненты управления микросервисами, в том числе: управление параллелизмом, адаптивный прерыватель цепи, адаптивное снижение нагрузки, автоматическое управление кешем и т. д., и его можно легко развернуть для обработки огромного трафика.

Если у вас есть хорошие идеи по повышению инженерной эффективности, пожалуйста, не стесняйтесь общаться! 👏

15. Адрес проекта

GitHub.com/them-specialty/go…

16. Группа общения WeChat

Добавьте мой WeChat: kevwan, пожалуйста, укажите go-zero, я присоединюсь к группе сообщества go-zero 🤝

ТАЛ Технология