Go语言实战 (10) go-micro微服务 实现简单备忘录 (上) | 用户模块

Posted 小生凡一

tags:

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

写在前面

基于go-micro v2版本的简单备忘录的实现,这一章,我们先实现用户的登录注册功能

go-micro框架结构

结构图

简单来说,浏览器通过访问这个网关服务器,服务器到服务发现查看是否有这个服务,有就将这个服务进行请求,再进行响应返回。

1. 下载

1.1 go-micro/v2

下载github.com/micro/go-micro/v2 注意一定要v2版本,并且确保电脑的最高版本是v2,而不是v3,如果是v3就把v3删除,因为后续生成pb文件的时间是用默认环境中最新版本生成的。

1.2 protoc

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

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

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



1.3 protobuf

protoc-gen-go是protobuf编译插件系列中的Go版本

下载github.com/golang/protobuf 注意

我们在终端

go get github.com/golang/protobuf

如果你像我一样go get不下来的话,可以直接 git clone 源码


然后进入protoc-gen-go文件夹中,在此文件夹下打开cmd

执行

go build


就会发现多了一个可执行文件


然后把这个可执行文件放到刚刚1.2 讲的那个proto的文件夹下

打开cmd 输入 protoc


这样即可

2. 用户模块

2.1 数据库配置

  • user/conf/conf.ini

文件配置,配置mysql,service服务等等…

[service]
AppMode = debug
HttpPort = :3000

[mysql]
Db = mysql
DbHost = 127.0.0.1
DbPort = 3306
DbUser = root
DbPassWord = root
DbName = todo_list
  • user/model/

加载配置

func Init() 
	file, err := ini.Load("./conf/config.ini")
	if err != nil 
		fmt.Println("配置文件读取错误,请检查文件路径:", err)
	
	LoadMysqlData(file)
	path := strings.Join([]stringDbUser, ":", DbPassWord, "@tcp(", DbHost, ":", DbPort, ")/", DbName, "?charset=utf8&parseTime=true", "")
	model.Database(path)

  • user/model/user.go

定义数据库模型

type User struct 
	gorm.Model
	UserName       string `gorm:"unique"`
	PasswordDigest string

  • user/model/migration.go

数据库模型迁移

func migration() 
	//自动迁移模式
	DB.Set("gorm:table_options", "charset=utf8mb4").
		AutoMigrate(&User)

2.2 proto文件编写

  • user/services/protos/userModels.proto

定义user的proto模型,注意这里一定要大写!!不然后面网关是绑定不到的!

syntax="proto3";
package services;
option go_package ="./;protos";

message UserModel
    // @inject_tag: json:"id"
    uint32 ID=1;
    // @inject_tag: json:"user_name"
    string UserName=2;
    // @inject_tag: json:"avatar"
    string Avatar=3;
    // @inject_tag: json:"email"
    string Email=4;
    // @inject_tag: json:"nickname"
    string NickName=5;
    // @inject_tag: json:"status"
    string Status=6;
    // @inject_tag: json:"limit"
    uint32 Limit=7;
    // @inject_tag: json:"created_at"
    int64 CreatedAt=8;
    // @inject_tag: json:"updated_at"
    int64 UpdatedAt=9;
    // @inject_tag: json:"deleted_at"
    int64 DeletedAt=10;

在proto目录下执行

protoc --proto_path=. --micro_out=. --go_out=. userModel.proto
  • user/services/protos/userService.proto

这里定义了用户请求的参数UserRequestUserResponse的信息

syntax="proto3";
package services;
import "userModels.proto";
option go_package ="./;protos";

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


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



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

在proto目录下执行

protoc --proto_path=. --micro_out=. --go_out=. userService.proto

生成这些四个文件即可,然后可以移动到services文件中。

2.3 实现用户模块业务逻辑

定义用户服务的结构体

//UserService 用户服务
type UserService struct 

2.3.1 用户注册方法

这里传入的参数是上下文信息,还有services层的 UserRequestUserDetailResponse

// UserRegister 用户注册
func (*UserService) UserRegister(ctx context.Context, req *services.UserRequest, res *services.UserDetailResponse) error 
	if req.Password != req.PasswordConfirm 
		err := errors.New("两次密码输入不一致")
		return err
	
	count := 0
	if err := model.DB.Model(&model.User).Where("user_name=?", req.UserName).Count(&count).Error; err != nil 
		return err
	
	if count > 0 
		err := errors.New("用户名已存在")
		return err
	
	user := model.User
		UserName: req.UserName,
	
	// 加密密码
	if err := user.SetPassword(req.Password); err != nil 
		return err
	
	// 创建用户
	if err := model.DB.Create(&user).Error; err != nil 
		return err
	
	res.UserDetail = BuildUser(user)
	return nil

2.3.2 登录服务方法

一样的逻辑方法,这里实现了UserLogin方法,同样地传入上下文,请求参数,响应参数

//UserLogin 实现用户服务接口 用户登录
func (*UserService) UserLogin(ctx context.Context, req *services.UserRequest, res *services.UserDetailResponse) error 
	var user model.User
	res.Code = 200
	if err := model.DB.Where("user_name = ?", req.UserName).First(&user).Error; err != nil 
		//如果查询不到,返回相应错误
		if gorm.IsRecordNotFoundError(err) 
			res.Code = 10003
			return nil
		
		res.Code = 30001
		return nil
	
	if user.CheckPassword(req.Password) == false 
		res.Code = 10004
		return nil
	
	res.UserDetail =  BuildUser(user)
	return nil

2.4 接入etcd服务发现

  • 注册etcd
	etcdReg := etcd.NewRegistry(
		registry.Addrs("127.0.0.1:2379"),
	)
  • 得到微服务实例
	// 1. 得到微服务实例
	microService := micro.NewService(
		micro.Name("rpcUserService"), // 设置微服务名字,用来访问的
		micro.Address("127.0.0.1:8082"),
		micro.Registry(etcdReg),
	)
  • 初始化
	microService.Init()
  • 服务注册
    将用户服务注册到etcd中
	_ = services.RegisterUserServiceHandler(microService.Server(), new(core.UserService))
  • 启动微服务
	_ = microService.Run()

查看etcd中http://localhost:8080/etcdkeeper/是否有该模块的注册信息

至此,用户端的微服务已经完成了,我们接入网关,调用服务。

3. 接入网关

3.1 proto文件

  • api-gateway/services/proto

将我们的user模块的两个proto文件复制过来,然后同样的操作,生成pb文件。

注意要在api-gateway中重新生成proto文件

3.2 接入路由

  • api-gateway/weblib/router.go

在这个文件中接入gin路由,中间件部分就省略了。

func NewRouter(service ...interface) *gin.Engine 
	ginRouter := gin.Default()
	//使用中间件,接收服务调用实例
	ginRouter.Use(middlewares.Cors(), middlewares.InitMiddleware(service), middlewares.ErrorMiddleware())
	//使用session中间件
	store := cookie.NewStore([]byte("something-very-secret"))
	ginRouter.Use(sessions.Sessions("mysession", store))
		// 测试连接
		v1.GET("pong", func(c *gin.Context) 
			c.JSON(200, "success")
		)
		// 用户服务
		v1.POST("/user/register", handlers.UserRegister)
		v1.POST("/user/login", handlers.UserLogin)
	
	return ginRouter

3.3 编写接口

3.3.1 用户注册

  • 定义请求参数
var userReq services.UserRequest
  • 绑定参数
PanicIfUserError(ginCtx.Bind(&userReq))
  • 获取服务实例
userService := ginCtx.Keys["userService"].(services.UserService)
  • 调取服务对象
userRes, err := userService.UserRegister(context.Background(), &userReq)
  • 返回数据
ginCtx.JSON(http.StatusOK, gin.H"data": userRes)
  • 完整代码
func UserRegister(ginCtx *gin.Context) 
	var userReq services.UserRequest
	PanicIfUserError(ginCtx.Bind(&userReq))
	//从gin.keys取出服务实例
	userService := ginCtx.Keys["userService"].(services.UserService)
	userRes, err := userService.UserRegister(context.Background(), &userReq)
	PanicIfUserError(err)
	ginCtx.JSON(http.StatusOK, gin.H"data": userRes)

3.3.2 用户登录

func UserLogin(ginCtx *gin.Context) 
	var userReq services.UserRequest  
	// 定义请求参数  
	PanicIfUserError(ginCtx.Bind(&userReq)) 
	// 绑定服务
	userService := ginCtx.Keys["userService"].(services.UserService)
	//从gin.keys取出服务实例
	userRes, err := userService.UserLogin(context.Background(), &userReq)
	//调用服务端的函数
	PanicIfUserError(err)
	token, err := util.GenerateToken(uint(userRes.UserDetail.ID)) 
	// 生成token
	if err != nil 
		userRes.Code = e.ERROR_AUTH_TOKEN
	
	ginCtx.JSON(200, gin.H"code": userRes.Code, "msg": e.GetMsg(userRes.Code), "data": gin.H"admin": userRes.UserDetail, "token": token)   
	// 响应返回

3.4 接入etcd

  • 定义用户模块微服务实例
	etcdReq := etcd.NewRegistry(
		registry.Addrs("127.0.0.1:2379"),
	)
	// 用户
	userMicroService := micro.NewService(
		micro.Name("userService.client"),
		micro.WrapClient(wrappers.NewUserWrapper),
	)
	//用户服务调用实例
	userService := services.NewUserService("rpcUserService", userMicroService.Client())
  • 定义http微服务模块实例,使用gin暴露http接口并注册到etcd中
	server := web.NewService(
		web.Name("httpService"),
		web.Address(":4000"),
		//将服务调用实例使用gin处理
		web.Handler(weblib.NewRouter(userService)), // 把userService微服务实例放入其中
		web.Registry(etcdReq),
		web.RegisterTTL(time.Second*30),
		web.RegisterInterval(time.Second*15),
		web.Metadata(map[string]string"protocol": "http"),
	)

查看是否将http服务注册到etcd,注册成功

3.5 测试

在 user/main.go文件下执行

go run main.go --registry=etcd --registry_address=127.0.0.1:2379

在 api-gateway/main.go下执行

go run main.go --registry=etcd --registry_address=127.0.0.1:2379
  • 用户注册



  • 用户登录



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

Go语言实战 (11) go-micro微服务 实现简单备忘录 (下) | 备忘录模块

Go-Micro框架入门教程---框架结构

(10)go-micro微服务发送邮件

《Go-micro微服务框架入门教程》学习笔记 | Protobuf

go微服务框架go-micro深度学习-目录

go-micro微服务Mysql配置