Посмотрите, как мой проект Go-zero делает его еще проще
Когда использовать RESTful API
Для большинства стартапов мы должны больше сосредоточиться на предоставлении продуктов на ранней стадии бизнеса. Преимущества монолитных сервисов заключаются в простой архитектуре, легком развертывании и более высокой производительности разработки, что может помочь нам быстро выполнить требования к продукту. Хотя мы используем монолитные сервисы для быстрой доставки продуктов, нам также необходимо оставлять возможность для расширения бизнеса, поэтому мы обычно четко разделяем различные бизнес-модули на монолитные сервисы.
Монолитная сервисная архитектура торгового центра
Мы берем торговый центр в качестве примера для построения монолитного сервиса. Служба торгового центра, как правило, относительно сложна и состоит из нескольких модулей, наиболее важные модули включают модули учетной записи, продукта и заказа и т. д. Каждый модуль будет иметь свою собственную независимую бизнес-логику, и каждый модуль также будет зависеть от некоторых других. Например, модуль заказа и модуль продукта будут зависеть от модуля учетной записи. В монолитном приложении этот вид зависимости обычно реализуется вызовами методов между модулями. Монолитные службы обычно совместно используют ресурсы хранения, такие как MySQL
и Redis
.
Общая архитектура монолитных сервисов относительно проста, что также является преимуществом монолитных сервисов. Запросы клиентов анализируются через DNS
и перенаправляются в серверные службы торгового центра через Nginx
. Сервисы торгового центра развернуты на облачных хостах. Чтобы добиться большей пропускной способности и высокой доступности, служба обычно развертывается с несколькими копиями. Эта простая архитектура может обеспечить высокую пропускную способность, если она хорошо оптимизирована.
Например, запрос на интерфейс сведений о заказе /order/detail
направляется в модуль заказа, который использует модуль учетной записи и модуль продукта для составления полных сведений о заказе обратно пользователю, а несколько модулей в одной службе обычно совместно используют базу данных и кеш.
Монолитный сервис
В следующем разделе описывается, как быстро реализовать монолитный сервис торгового центра на основе go-zero
. Разработчики, которые использовали go-zero
, знают, что мы предоставляем файл формата API
для описания Restful API
, а затем мы можем сгенерировать соответствующий код с помощью goctl
с помощью одной команды, нам просто нужно заполнить соответствующую бизнес-логику в файлах logic
. Служба торгового центра содержит несколько модулей, и чтобы сделать модули независимыми друг от друга, разные модули определяются отдельными API
, но все API
определяются для одного и того же service (mall-api)
.
Создайте user.api
, order.api
, product.api
и mall.api
в каталоге api
, где mall.api
– это объединенный файл api
. Другие файлы api
импортируются через директивы import
.
api
|-- mall.api
|-- order.api
|-- product.api
|-- user.api
Определение API торгового центра
mall.api
определяется следующим образом, где syntax = "v1"
означает, что это синтаксис v1
для zero-api
.
syntax = "v1"
import "user.api" import "order.api" import "product.api"
Определение API модуля учетной записи
- Просмотр сведений о пользователе
- Получить все заказы для пользователя
syntax = "v1"
type ( UserRequest { ID int64 `path:"id"` }
UserReply { ID int64 `json:"id"` Name string `json:"name"` Balance float64 `json:"balance"` }
UserOrdersRequest { ID int64 `path:"id"` }
UserOrdersReply { ID string `json:"id"` State uint32 `json:"state"` CreateAt string `json:"create_at"` } )
service mall-api { @handler UserHandler get /user/:id (UserRequest) returns (UserReply)
@handler UserOrdersHandler get /user/:id/orders (UserOrdersRequest) returns (UserOrdersReply) }
Определение API модуля заказа
- Получить детали заказа
- Генерировать заказы
syntax = "v1"
type ( OrderRequest { ID string `path:"id"` }
OrderReply { ID string `json:"id"` State uint32 `json:"state"` CreateAt string `json:"create_at"` }
OrderCreateRequest { ProductID int64 `json:"product_id"` }
OrderCreateReply { Code int `json:"code"` } )
service mall-api { @handler OrderHandler get /order/:id (OrderRequest) returns (OrderReply)
@handler OrderCreateHandler post /order/create (OrderCreateRequest) returns (OrderCreateReply) }
Определение API модуля продукта
- Посмотреть сведения о продукте
syntax = "v1"
type ProductRequest { ID int64 `path:"id"` }
type ProductReply { ID int64 `json:"id"` Name string `json:"name"` Price float64 `json:"price"` Count int64 `json:"count"` }
service mall-api { @handler ProductHandler get /product/:id (ProductRequest) returns (ProductReply) }
Создание монолитного сервиса
С уже определенным API
создание службы с API
становится очень простым, мы используем goctl
для создания монолитного кода службы.
$ goctl api go -api api/mall.api -dir .
Сгенерированный код структурирован следующим образом.
.
├── api
│ ├── mall.api
│ ├── order.api
│ ├── product.api
│ └── user.api
├── etc
│ └── mall-api.yaml
├─ internal
│ ├── config
│ │ └── config.go
│ ├── handler
│ │ ├── ordercreatehandler.go
│ │ ├── orderhandler.go
│ │ ├── producthandler.go
│ │ ├── routes.go
│ │ ├── userhandler.go
│ │ └─ userordershandler.go
│ ├─ logic
│ │ ├─ ordercreatelogic.go
│ │ ├── orderlogic.go
│ │ ├── productlogic.go
│ │ ├── userlogic.go
│ │ └── userorderslogic.go
│ ├── svc
│ │ └── servicecontext.go
│ └── types
│ └── types.go
└── mall.go
Давайте объясним сгенерированные файлы.
api
: содержит файл описанияAPI
etc
: используется для определения конфигурации проекта, все элементы конфигурации могут быть записаны вmall-api.yaml
internal/config
: определение конфигурации службыinternal/handler
: реализацияhandler
, соответствующая маршрутам, определенным в файлеAPI
internal/logic
: используется для размещения бизнес-логики, соответствующей каждому маршруту, причина различия междуhandler
иlogic
заключается в том, чтобы сделать часть бизнес-обработки как можно менее зависимой, отделитьHTTP requests
от кода логической обработки и облегчить последующее разделение вRPC service
internal/svc
: используется для определения зависимостей обработки бизнес-логики, мы можем создать зависимые ресурсы в функцииmain
и передать ихhandler
иlogic
черезServiceContext
internal/types
: определяетAPI
структуры данных запроса и ответа.mall.go
: файл, в котором находится функцияmain
, с тем же именем, что иservice
в определенииAPI
, за исключением суффикса-api
.
Сгенерированную службу можно запустить без каких-либо изменений: `
$ go run mall.go
Starting server at 0.0.0.0:8888...
Реализация бизнес-логики
Далее реализуем бизнес-логику. Логика будет проста для демонстрационных целей, а не для реальной бизнес-логики.
Для начала реализуем логику получения всех заказов для пользователей. Поскольку в пользовательском модуле нет информации о заказах, нам нужно полагаться на модуль заказов для запроса заказов пользователей, поэтому мы добавляем зависимость от OrderLogic
в UserOrdersLogic
.
type UserOrdersLogic struct { logx.Logger ctx context.Context svcCtx *svc.ServiceContext orderLogic *OrderLogic }
func NewUserOrdersLogic(ctx context.Context, svcCtx *svc.ServiceContext) *UserOrdersLogic { return &UserOrdersLogic{ Logger: logx.WithContext(ctx), ctx: ctx, svcCtx: svcCtx, orderLogic: NewOrderLogic(ctx, svcCtx), } }
Реализуйте метод в OrderLogic
для запроса всех заказов на основе user id
func (l *OrderLogic) ordersByUser(uid int64) ([]*types.OrderReply, error) { if uid == 123 { // It should actually be queried from database or cache return []*types.OrderReply{ { ID: "236802838635", State: 1, CreateAt: "2022-5-12 22:59:59", }, { ID: "236802838636", State: 1, CreateAt: "2022-5-10 20:59:59", }, }, nil }
return nil, nil }
Вызовите метод ordersByUser
в методе UserOrders
UserOrdersLogic
.
func (l *UserOrdersLogic) UserOrders(req *types.UserOrdersRequest) (*types.UserOrdersReply, error) { orders, err := l.orderLogic.ordersByUser(req.ID) if err ! = nil { return nil, err }
return &types.UserOrdersReply{ Orders: orders, }, nil }
В этот момент мы перезапускаем службу mall-api
и запрашиваем все заказы пользователя в браузере.
https://localhost:8888/user/123/orders
Результат возврата выглядит следующим образом, как мы и ожидали
{
"orders": [
{
"id": "236802838635",
"state": 1,
"create_at": "2022-5-12 22:59:59"
},
{
"id": "236802838636",
"state": 1,
"create_at": "2022-5-10 20:59:59"
}
]
}
Далее реализуем логику создания заказа. Чтобы создать заказ, нам сначала нужно посмотреть, достаточно ли товара на складе, поэтому нам нужно полагаться на модуль товара в модуле заказа.
type OrderCreateLogic struct { logx.Logger ctx context.Context svcCtx *svc.ServiceContext productLogic *ProductLogic productLogic *ProductLogic }
func NewOrderCreateLogic(ctx context.Context, svcCtx *svc.ServiceContext) *OrderCreateLogic { return &OrderCreateLogic{ Logger: logx.WithContext(ctx), ctx: ctx, svcCtx: svcCtx, productLogic: NewProductLogic(ctx, svcCtx), } }
Логика создания заказа следующая.
const ( success = 0 failure = -1 )
func (l *OrderCreateLogic) OrderCreate(req *types.OrderCreateRequest) (*types.OrderCreateReply, error) { product, err := l.productLogic.productByID(req.ProductID) if err ! = nil { return nil, err }
if product.Count > 0 { return &types.OrderCreateReply{Code: success}, nil }
return &types.OrderCreateReply{Code: failure}, nil }
Логика модуля зависимого продукта следующая.
func (l *ProductLogic) Product(req *types.ProductRequest) (*types.ProductReply, error) { return l.productByID(req.ID) }
func (l *ProductLogic) productByID(id int64) (*types.ProductReply, error) { return &types.ProductReply{ ID: id, Name: "apple watch 3", Price: 3333.33, Count: 99, }, nil }
Вышеизложенное показывает, что использование go-zero
для разработки монолитного сервиса очень просто, что помогает нам быстро развиваться. А еще мы разделили модули, что тоже дает возможность позже перейти на микросервисы.
Краткое содержание
Приведенный выше пример показывает, что очень просто использовать go-zero
для разработки монолитных сервисов. Вам нужно только определить файл api
, после чего инструмент goctl
сможет автоматически сгенерировать код проекта. Нам нужно только заполнить код бизнес-логики в пакете логики. В этой статье мы просто продемонстрировали, как быстро разрабатывать монолитные сервисы на основе go-zero
, не задействуя базы данных. Фактически, goctl
также может генерировать код CRUD
и cache
одной командой.
Для различных бизнес-сценариев также можно настроить шаблоны. А кастомизированные шаблоны можно использовать внутри команды через удаленные git
репозитории, что может быть очень эффективным для совместной работы.
Want to Connect?
Welcome to use go-zero
and star to support us!