Go语言实战 (14) Gin+gRPC 微服务实现备忘录 (上) | 用户模块

Posted 小生凡一

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Go语言实战 (14) Gin+gRPC 微服务实现备忘录 (上) | 用户模块相关的知识,希望对你有一定的参考价值。

写在前面

介于很多同学让我出一下关于gRPC的内容,我就用gRPC把备忘录重新做一遍。

源码地址:

https://github.com/CocaineCong/gRPC-todoList

1. 安装部分

1.1 安装gRPC

go get google.golang.org/grpc
go get google.golang.org/protobuf

1.2 安装protoc

可用于通讯协议、数据存储等领域的语言无关、平台无关、可扩展的序列化结构数据格式。

下载proto,我是下载这个的。

然后解压出来把bin目录放在直接放在系统变量当中




验证

2. 用户模块

2.1 总体项目架构

user/
├── cmd                   // 启动入口
├── config                // 配置文件
├── discovery             // etcd服务注册、keep-alive、获取服务信息等等
├── internal              // 业务逻辑(不对外暴露)
│   ├── handler           // 视图层
│   ├── cache             // 缓存模块
│   ├── repository        // 持久层
│   └── service           // 服务层
│       └──pb             // 放置生成的pb文件
├── logs                  // 放置打印日志模块
├── pkg                   // 各种包
│   ├── e                 // 统一错误状态码
│   ├── res               // 统一response接口返回
│   └── util              // 各种工具、JWT、Logger等等..
├── routes                // http路由模块
└── wrappers              // 各个服务之间的熔断降级

2.2 proto文件定义

  • user/internal/service/pb
syntax="proto3";
package pb;
option go_package = "/internal/service;service";

message UserModel 
    // @inject_tag: json:"user_id"
    uint32 UserID=1;
    // @inject_tag: json:"user_name"
    string UserName=2;
    // @inject_tag: json:"nick_name"
    string NickName=3;

执行

protoc -I internal/service/pb internal/service/pb/*.proto --go_out=plugins=grpc:.

生成 pb.go 文件

  • user/service/pb

定义user模块的请求和返回的结构类型,已经rpc方法。

syntax="proto3";
package pb;
import "userModels.proto";
option go_package = "/internal/service;service";

message UserRequest
  // @inject_tag: json:"nick_name" form:"nick_name" uri:"nick_name"
  string NickName=1;
  // @inject_tag: json:"user_name" form:"user_name" uri:"user_name"
  string UserName=2;
  // @inject_tag: json:"password" form:"password" uri:"password"
  string Password=3;
  // @inject_tag: json:"password_confirm" form:"password_confirm" uri:"password_confirm"
  string PasswordConfirm=4;


message UserDetailResponse
  UserModel UserDetail=1;
  uint32 Code=2;


service UserService
  rpc UserLogin(UserRequest) returns(UserDetailResponse);
  rpc UserRegister(UserRequest) returns(UserDetailResponse);
  rpc UserLogout(UserRequest) returns(UserDetailResponse);

2.3 repository

这一层主要是对持久化的操作,基础的业务操作。

type User struct 
	UserID         uint      `gorm:"primarykey"`
	UserName       string    `gorm:"unique"`
	NickName       string
	PasswordDigest string

  • 视图返回
func BuildUser(item User) *service.UserModel 
	userModel := service.UserModel
		UserID:   uint32(item.UserID),
		NickName: item.NickName,
		UserName: item.UserName,
	
	return &userModel

2.4 接入ETCD服务发现

  • user/cmd/main.go

新建一个etcd节点

etcdAddress := []stringviper.GetString("etcd.address")
// 服务注册
etcdRegister := discovery.NewRegister(etcdAddress, logrus.New())
grpcAddress := viper.GetString("server.grpcAddress")

接入user节点

userNode := discovery.Server
		Name: viper.GetString("server.domain"),
		Addr: grpcAddress,
	

节点绑定服务

service.RegisterUserServiceServer(server, handler.NewUserService())

服务注册

if _, err := etcdRegister.Register(userNode, 10); err != nil 
	panic(fmt.Sprintf("start server failed, err: %v", err))

3. 接入网关

3.1 router

  • api-gateway/routes/router.go

引入gin作为http网关

func NewRouter(service ...interface) *gin.Engine 
	ginRouter := gin.Default()
	ginRouter.Use(middleware.Cors(), middleware.InitMiddleware(service), middleware.ErrorMiddleware())
	store := cookie.NewStore([]byte("something-very-secret"))
	ginRouter.Use(sessions.Sessions("mysession", store))
	v1 := ginRouter.Group("/api/v1")
	
		v1.GET("ping", func(context *gin.Context) 
			context.JSON(200, "success")
		)
		// 用户服务
		v1.POST("/user/register", handler.UserRegister)
		v1.POST("/user/login", handler.UserLogin)
	
	return ginRouter

3.2 网关连接etcd

与user进行连接,获取user服务

userConn, _ := grpc.Dial("127.0.0.1:10001", opts...)
userService := service.NewUserServiceClient(userConn)

3.3 调用服务

  • api-gateway/internal/handler/user.go
func UserRegister(ginCtx *gin.Context) 
	var userReq service.UserRequest
	PanicIfUserError(ginCtx.Bind(&userReq))
	// 从gin.Key中取出服务实例
	userService := ginCtx.Keys["user"].(service.UserServiceClient)
	userResp, err := userService.UserRegister(context.Background(), &userReq)
	PanicIfUserError(err)
	r := res.Response
		Data:   userResp,
		Status: uint(userResp.Code),
		Msg:    e.GetMsg(uint(userResp.Code)),
	
	ginCtx.JSON(http.StatusOK, r)

开发者涨薪指南 48位大咖的思考法则、工作方式、逻辑体系

以上是关于Go语言实战 (14) Gin+gRPC 微服务实现备忘录 (上) | 用户模块的主要内容,如果未能解决你的问题,请参考以下文章

微服务实战之 Go gRPC 调试工具

3.13 Go微服务实战(微服务理论) --- 微服务中的进程间通信

13.Go语言高并发与微服务实战 --- 综合实战:秒杀系统的设计与实现

13.Go语言高并发与微服务实战 --- 综合实战:秒杀系统的设计与实现

Go + gRPC-Gateway(V2) 构建微服务实战系列,小程序登录鉴权服务:RSA(RS512) 签名 JWT

1.1 Go微服务实战(Go语言基础) --- Go语言程序基础