关于golang:微服务从代码到k8s部署应有尽有系列四用户中心

51次阅读

共计 4487 个字符,预计需要花费 12 分钟才能阅读完成。

咱们用一个系列来解说从需要到上线、从代码到 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-zerostar 反对咱们!

微信交换群

关注『微服务实际 』公众号并点击 交换群 获取社区群二维码。

正文完
 0