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
这里定义了用户请求的参数UserRequest
和UserResponse
的信息
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层的 UserRequest
和 UserDetailResponse
// 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微服务 实现简单备忘录 (下) | 备忘录模块