如何做好微服务
• 基本功能层面
a. 并发控制&限流
,避免服务被突发流量击垮
b. 服务注册与服务发现
,确保能够动态侦测增减的节点
c. 负载均衡
,需要根据节点承受能力分发流量
d. 超时控制
,避免对已超时请求做无用功
e. 熔断设计
,快速失败,保障故障节点的恢复能力
• 高阶功能层面
a. 请求认证
,确保每个用户只能访问自己的数据
b. 链路追踪
,用于理解整个系统和快速定位特定请求的问题
c. 日志
,用于数据收集和问题定位
d. 可观测性
,没有度量就没有优化
实例应用架构
绿色背景的功能模块是自动生成的,按需激活
红色模块是需要自己写的,也就是增加下依赖,编写业务特有逻辑
• 微服务架构图
• goctl
各层代码生成
API gateway
rpc
model
goctl
工具介绍
goctl
是go-zero
配套的代码生成器
定义api请求
根据定义的api自动生成golang(后端), java(iOS & Android), typescript(web & 小程序),dart(flutter)
生成MySQL CURD
· 1) 生成 api
goctl api -o xxx.api
# 描述了api基本信息,比如Auth,api是哪个用途
info(
title: doc title
desc: >
doc description first part,
doc description second part<
version: 1.0
)
# type类型声明和golang语法兼容
type int userType
type user struct {
name string `json:"user"` // 用户姓名
}
type student struct {
name string `json:"name"` // 学生姓名
}
type teacher struct {
}
type (
address struct {
city string `json:"city"` // 城市
}
innerType struct {
image string `json:"image"`
}
createRequest struct {
innerType
name string `form:"name"` // niha
age int `form:"age,optional"` // nihaod
address []address `json:"address,optional"`
}
getRequest struct {
name string `path:"name"`
age int `form:"age,optional"`
}
getResponse struct {
code int `json:"code"`
desc string `json:"desc,omitempty"`
address address `json:"address"`
service int `json:"service"`
}
)
# 一个文件里面只能有一个service
# service代表一组服务,
# 一个服务可以由多组名称相同的service组成,
# 可以针对每一组service配置group属性来指定service生成所在子目录
service user-api {
# doc用来描述此路由的用途
@doc(
summary: user title
desc: >
user description first part,
user description second part,
user description second line
)
@server(
handler: GetUserHandler
# GetProfileHandler表示处理这个路由的handler
group: user
# group属性来指定service生成所在子目录
)
get /api/user/:name(getRequest) returns(getResponse)
#请求方式 路由path 返回的结构体
@server(
handler: CreateUserHandler
group: user
)
post /api/users/create(createRequest)
}
service user-api {
@doc(summary: user title)
@server(
handler: GetProfileHandler
)
get /api/profile/:name(getRequest) returns(getResponse)
@server(
handler: CreateProfileHandler
)
post /api/profile/create(createRequest)
}
service user-api {
@doc(summary: desc in one line)
@server(
handler: PingHandler
)
head /api/ping()
}
·2) 根据定义好的api文件生成golang代码
goctl api [go/java/ts] [-api user/user.api] [-dir ./src]
.
├── internal
│ ├── config
│ │ └── config.go
│ ├── handler
│ │ ├── pinghandler.go
│ │ ├── profile
│ │ │ ├── createprofilehandler.go
│ │ │ └── getprofilehandler.go
│ │ ├── routes.go
│ │ └── user
│ │ ├── createuserhandler.go
│ │ └── getuserhandler.go
│ ├── logic
│ │ ├── pinglogic.go
│ │ ├── profile
│ │ │ ├── createprofilelogic.go
│ │ │ └── getprofilelogic.go
│ │ └── user
│ │ ├── createuserlogic.go
│ │ └── getuserlogic.go
│ ├── svc
│ │ └── servicecontext.go
│ └── types
│ └── types.go
└── user.go
生成的代码可以直接跑,有几个地方需要改
在servicecontext.go
里面增加需要传递给logic的一些资源,比如mysql, redis,rpc等
在定义的get/post/put/delete等请求
的handler
和logic
里增加处理业务逻辑的代码
实例应用
1)mkdir hello
cd hello
2) go mod init hello
3) goctl api -o hello.api
4) 编辑hello
syntax = "v1"
info(
title: "用戶注冊"// TODO: add title
desc: "用戶注冊"// TODO: add description
author: "jiamin"
email: "jiamin@xx.cn"
)
type (
UserOptReq {
Mobile string `json:"mobile"`
Passwd string `json:"passwd"`
Code string `json:"code"`
}
UserOpResp {
Id int `json:"id"`
Token string `json:"token"`
}
//图片验证码支持
VerifyReq {
Ticket string `json:"ticket"`
}
//图片验证码支持
VerifyResp {
Data string `json:"data"`
}
)
service hello-api {
@doc(
summary: 公开的api函数
desc: >
register: 用户注册,
authorization: 用户登录,
verify: 图片验证码接口
)
@server(
handler: registerHandler
group: register
)
post /open/register(UserOptReq) returns(UserOpResp)
}
service hello-api {
@doc(
summary: 公开的api函数
desc: >
register: 用户注册,
authorization: 用户登录,
verify: 图片验证码接口
)
@server(
handler: authorizationHandler
group: authorization
)
post /open/authorization(UserOptReq) returns(UserOpResp)
@server(
handler: verifyHandler
group: verify
)
post /open/verify(VerifyReq) returns(VerifyResp)
}
5)生成代码
goctl api go -api hello.api -dir .
6) 运行测试
go run hello.go
curl http://127.0.0.1:8888/open/register -X POST -H "Content-Type: application/json" -d {\"mobile\":\"15367151352\",\"passwd\":\"testpwd\",\"code\":\"asdf\"}
# {"id":0,"token":""}