咱们用一个系列来解说从需要到上线、从代码到k8s部署、从日志到监控等各个方面的微服务残缺实际。
整个我的项目应用了go-zero开发的微服务,根本蕴含了go-zero以及相干go-zero作者开发的一些中间件,所用到的技术栈根本是go-zero项目组的自研组件,根本是go-zero全家桶了。
实战我的项目地址:https://github.com/Mikaelemmm…
一、用户核心业务架构图
二、依赖关系
usercenter-api(用户核心api) 依赖 identity-rpc(受权认证rpc)、usercenter-rpc(用户核心rpc)
usercenter-rpc(用户核心rpc)依赖 identity-rpc(受权核心rpc)
咱们看我的项目usercenter/cmd/api/desc/usercenter.api ,所有的用户api对外的http办法都在这外面
这外面有4个业务注册、登陆、获取用户信息、微信小程序受权
三、注册举例
1、注册api服务
咱们在写api服务代码的时候是先要在usercenter.api中定义好service中的办法,而后在desc/user中写request、response,这样拆离开的益处是不那么臃肿
a、在usercenter.api中定义注册办法如下
// 用户模块v1版本的接口
@server(
prefix: usercenter/v1
group: user
)
service usercenter {
@doc "注册"
@handler register
post /user/register (RegisterReq) returns (RegisterResp)
.....
}
b、在app/usercenter/cmd/api/desc/user/user.api中定义RegisterReq\RegisterResp
type (
RegisterReq {
Mobile string `json:"mobile"`
Password string `json:"password"`
}
RegisterResp {
AccessToken string `json:"accessToken"`
AccessExpire int64 `json:"accessExpire"`
RefreshAfter int64 `json:"refreshAfter"`
}
)
c、goctl生成api代码
1)命令行进入app/usercenter/cmd/api/desc目录下。
2)去我的项目目录下deploy/script/gencode/gen.sh中,复制如下一条命令,在命令行中执行(命令行要切换到app/usercenter/cmd目录)
$ goctl api go -api *.api -dir ../ -style=goZero
d、关上app/usercenter/cmd/api/internal/logic/user/register.go文件
这里就很容易了,间接调用user的rpc服务即可
这里有个小技巧,很多同学感觉rpc服务返回的字段跟api定义差不多,每次都要手动去复制很麻烦,那么go有没有像java一样的BeanCopyUtils.copy 这种工具呢?答案必定是有的,能够看下面的代码copier.Copy ,这个库是gorm作者的另一款新作,是不是很兴奋。 那咱们持续看看调用后端的rpc是什么样子的。
2、注册rpc服务
- 定义protobuf文件
咱们在app/usercenter/cmd/rpc/pb中新建usercenter.proto,写入注册办法
// req 、resp
message RegisterReq {
string mobile = 1;
string nickname = 2;
string password = 3;
string authKey = 4;
string authType = 5;
}
message RegisterResp {
string accessToken = 1;
int64 accessExpire = 2;
int64 refreshAfter = 3;
}
// service
service usercenter {
rpc register(RegisterReq) returns(RegisterResp);
...
}
- 应用goctl生成代码,这里不须要本人手动敲
1)命令行进入app/usercenter/cmd/rpc/pb目录下。
2)去我的项目目录下deploy/script/gencode/gen.sh中,复制如下两条命令,在命令行中执行(命令行要切换到app/usercenter/cmd目录)
$ goctl rpc protoc *.proto --go_out=../ --go-grpc_out=../ --zrpc_out=../
$ sed -i "" 's/,omitempty//g' *.pb.go
-
关上app/usercenter/cmd/rpc/internal/logic/registerLogic.go写逻辑代码
注册设计到2张表,一个user表,一个user_auth表,user是存储用户根本信息的,user_auth是能够依据不同平台受权登陆的相干信息,所以这里设计到本地事务,因为go-zero的事务要在model中能力应用,然而我在model中做了个解决,把它在model中裸露进去,就能够在logic中应用
model中定义了Trans办法裸露事务给logic
在logic中间接应用
因为我的项目反对小程序、手机号,小程序注册不须要明码,所以在解决明码时候做了个解决,手机号注册就要传递明码,小程序注册就不须要传递明码,至于手机号注册明码不能为空要在手机号注册时候的api服务本人判断
在usercenter-rpc注册胜利之后,须要申请token给前端登陆,间接申请identity-rpc颁发该用户的token
identity-rpc中如下
message GenerateTokenReq {
int64 userId = 1;
}
message GenerateTokenResp {
string accessToken = 1;
int64 accessExpire = 2;
int64 refreshAfter = 3;
}
service identity{
//生成token,只针对用户服务凋谢拜访
rpc generateToken(GenerateTokenReq) returns(GenerateTokenResp);
.....
}
generatetokenlogic.go
// GenerateToken 生成token,只针对用户服务凋谢拜访.
func (l *GenerateTokenLogic) GenerateToken(in *pb.GenerateTokenReq) (*pb.GenerateTokenResp, error) {
now := time.Now().Unix()
accessExpire := l.svcCtx.Config.JwtAuth.AccessExpire
accessToken, err := l.getJwtToken(l.svcCtx.Config.JwtAuth.AccessSecret, now, accessExpire, in.UserId)
if err != nil {
return nil, errors.Wrapf(ErrGenerateTokenError, "getJwtToken err userId:%d , err:%v", in.UserId, err)
}
//存入redis
userTokenKey := fmt.Sprintf(globalkey.CacheUserTokenKey, in.UserId)
err = l.svcCtx.RedisClient.Setex(userTokenKey, accessToken, int(accessExpire))
if err != nil {
return nil, errors.Wrapf(ErrGenerateTokenError, "SetnxEx err userId:%d, err:%v", in.UserId, err)
}
return &pb.GenerateTokenResp{
AccessToken: accessToken,
AccessExpire: now + accessExpire,
RefreshAfter: now + accessExpire/2,
}, nil
}
注册胜利并去identity-rpc拿到token、token过期工夫、置换token的工夫给api服务
四、业务获取登陆用户id
当咱们在获取用户信息,或者下单等场景下总要获取登陆用户的id,前一篇咱们讲到,咱们在受权identity服务中校验完token,解析进去的userId会放到header中返回给nginx的authReuest
在文件app/identity/cmd/api/internal/handler/verify/tokenHandler.go
nginx通过authRequest而后拜访后端的服务时候,会把header内容传递给后端服务,因为咱们在nginx中配置了如下
那这样的话,咱们在后端服务就能够拿到这个userId了,比方咱们当初拜访usercenter/v1/user/detail获取以后登陆用户信息
ok,能够看到咱们通过 ctxdata.GetUidFromCtx(l.ctx)就能够拿到,为什么这么神奇呢?咱们点开看看这个办法
实际上就是从ctx中拿到的userId,是不是很奇怪,咱们明明在nignx就放在了header中,你在go的业务代码中为什么能通过ctx拿到?
1、【小技巧】middleware
当nginx在header中携带了x-user就是userId来拜访后端服务的时候,咱们后端服务在启动时main函数会加载一个全局中间件,比方usercenter-api中的main
app/usercenter/cmd/api/usercenter.go
这里定义了全局中间件,只有有申请到咱们usercenter-ap某个办法之前,都会先进入全局中间件中,中间件具体内容如下
所以是不是一下就明确了,在申请咱们usercenter/v1/user/detail时候,会先进入这个中间件,在这个中间件内,咱们通过nginx的header中的X-User拿到解析后的userId放到ctx中,那持续进入到usercenter/v1/user/detail时候,咱们是不是就能够通过ctx间接取出来在业务中用啦,所有水落石出。
同样其余用户核心服务登陆、获取登陆用户信息、小程序受权登陆都是一个情理,这里就不再啰嗦了,自行看代码即可
【注】小程序受权登陆,记得批改配置文件,这里的配置文件是假的,改成本人的
我的项目地址
https://github.com/zeromicro/go-zero
欢送应用 go-zero
并 star 反对咱们!
微信交换群
关注『微服务实际』公众号并点击 交换群 获取社区群二维码。
发表回复