16、注册中心-consul

目录

  • 一、服务注册与发现
  • 二、consul安装与配置
  • 三、consul服务注册与注销
    • 1 - 注册服务
    • 2 - 注销服务
    • 3 - 健康检查
    • 4 - 获取服务
  • 四、go中使用consul
  • 五、user_srv集成viper和zap
  • 六、grpc健康检查
    • grpc服务注册到consul
  • 七、gin集成consul
    • 1 - UserSrvClient优化
    • 2 - gin集成consul
  • 八、完整源码

一、服务注册与发现

  • 什么是服务注册和发现:假如这个产品已经在线上运行,有一天运营想搞一场促销活动,那么我们相对应的【用户服务】可能就要新开启三个微服务实例来支撑这场促销活动。而与此同时,作为苦逼程序员的你就只有手动去 API gateway 中添加新增的这三个微服务实例的 ip 与port ,一个真正在线的微服务系统可能有成百上千微服务,难道也要一个一个去手动添加吗?
  • 解决方案:当我们新添加一个微服务实例的时候,微服务就会将自己的 ip 与 port 发送到注册中心,在注册中心里面记录起来。当 API gateway 需要访问某些微服务的时候,就会去注册中心取到相应的 ip 与 port。从而实现自动化操作

16、注册中心-consul_第1张图片

  • 技术选型:Consul 与其他常见服务发现框架对比

16、注册中心-consul_第2张图片


二、consul安装与配置

  • consul参考地址:https://blog.csdn.net/qq23001186/article/details/126025565

三、consul服务注册与注销

1 - 注册服务

  • 添加服务:https://www.consul.io/api-docs/agent/service#register-service

16、注册中心-consul_第3张图片
16、注册中心-consul_第4张图片

2 - 注销服务

  • 注销服务:https://www.consul.io/api-docs/agent/service#deregister-service
    • postman地址:http://192.168.78.131:8500/v1/agent/service/deregister/web_api
    • 使用put请求
      16、注册中心-consul_第5张图片

3 - 健康检查

  • 健康检查:https://www.consul.io/api-docs/agent/check

4 - 获取服务

  • 获取服务:https://www.consul.io/api-docs/agent/service#list-services

四、go中使用consul

  • Register:注册服务
  • AllServices:获取所有服务
  • FilterService:过滤服务
package main

import (
	"fmt"
	"github.com/hashicorp/consul/api"
)

// Register 注册服务
func Register(address string, port int, name string, tags []string, id string) error {
	cfg := api.DefaultConfig()
	cfg.Address = "192.168.124.51:8500"

	client, err := api.NewClient(cfg)
	if err != nil {
		panic(err)
	}
	//生成对应的检查对象
	check := &api.AgentServiceCheck{
		HTTP:                           "http://192.168.124.9:8081/health",
		Timeout:                        "5s",
		Interval:                       "5s",
		DeregisterCriticalServiceAfter: "10s",
	}

	//生成注册对象
	registration := new(api.AgentServiceRegistration)
	registration.Name = name
	registration.ID = id
	registration.Port = port
	registration.Tags = tags
	registration.Address = address
	registration.Check = check

	err = client.Agent().ServiceRegister(registration)
	if err != nil {
		panic(err)
	}
	return nil
}

// AllServices 获取所有服务
func AllServices() {
	cfg := api.DefaultConfig()
	cfg.Address = "192.168.124.51:8500"

	client, err := api.NewClient(cfg)
	if err != nil {
		panic(err)
	}

	data, err := client.Agent().Services()
	if err != nil {
		panic(err)
	}
	for key, _ := range data {
		fmt.Println(key)
	}
}

// FilterService 服务过滤
func FilterService() {
	cfg := api.DefaultConfig()
	cfg.Address = "192.168.124.51:8500"

	client, err := api.NewClient(cfg)
	if err != nil {
		panic(err)
	}

	data, err := client.Agent().ServicesWithFilter(`Service == "user-web"`)
	if err != nil {
		panic(err)
	}
	for key, _ := range data {
		fmt.Println(key)
	}
}

func main() {
	_ = Register("http://192.168.124.9", 8081, "user-web", []string{"mxshop", "bobby"}, "user-web")
	AllServices()
	FilterService()
}


五、user_srv集成viper和zap

  • user_srv/config/config.go:添加配置
package config

type MysqlConfig struct {
	Host     string `mapstructure:"host" json:"host"`
	Port     int    `mapstructure:"port" json:"port"`
	Name     string `mapstructure:"db" json:"db"`
	User     string `mapstructure:"user" json:"user"`
	Password string `mapstructure:"password" json:"password"`
}

type ServerConfig struct {
	Name      string      `mapstructure:"name" json:"name"`
	MysqlInfo MysqlConfig `mapstructure:"mysql" json:"mysql"`
}

  • user_srv/global/global.go:添加全局对象ServerConfig
package global

import (
	"gorm.io/gorm"
	"nd/user_srv/config"
)

var (
	DB           *gorm.DB
	ServerConfig config.ServerConfig
)

  • user_srv/initialize/init_config.go:初始化config配置
package initialize

import (
	"fmt"
	"github.com/spf13/viper"
	"go.uber.org/zap"
	"nd/user_srv/global"
)

func GetEnvInfo(env string) bool {
	viper.AutomaticEnv()
	return viper.GetBool(env)
	//刚才设置的环境变量 想要生效 我们必须得重启goland
}

func InitConfig() {
	//从配置文件中读取出对应的配置
	debug := GetEnvInfo("DEV_CONFIG")
	configFilePrefix := "config"
	configFileName := fmt.Sprintf("%s_pro.yaml", configFilePrefix)
	if debug {
		configFileName = fmt.Sprintf("%s_debug.yaml", configFilePrefix)
	}

	v := viper.New()
	//文件的路径如何设置
	v.SetConfigFile(configFileName)
	if err := v.ReadInConfig(); err != nil {
		panic(err)
	}
	//这个对象如何在其他文件中使用 - 全局变量
	if err := v.Unmarshal(&global.ServerConfig); err != nil {
		panic(err)
	}
	zap.S().Infof("配置信息: %v", global.ServerConfig)
}

  • user_srv/initialize/init_db.go:初始化db
package initialize

import (
	"fmt"
	"log"
	"os"
	"time"

	"gorm.io/driver/mysql"
	"gorm.io/gorm"
	"gorm.io/gorm/logger"
	"gorm.io/gorm/schema"

	"nd/user_srv/global"
)

func InitDB() {
	c := global.ServerConfig.MysqlInfo
	dsn := fmt.Sprintf("%s:%s@tcp(%s:%d)/%s?charset=utf8mb4&parseTime=True&loc=Local",
		c.User, c.Password, c.Host, c.Port, c.Name)
	newLogger := logger.New(
		log.New(os.Stdout, "\r\n", log.LstdFlags), // io writer
		logger.Config{
			SlowThreshold: time.Second,   // 慢 SQL 阈值
			LogLevel:      logger.Silent, // Log level
			Colorful:      true,          // 禁用彩色打印
		},
	)

	// 全局模式
	var err error
	global.DB, err = gorm.Open(mysql.Open(dsn), &gorm.Config{
		NamingStrategy: schema.NamingStrategy{
			SingularTable: true,
		},
		Logger: newLogger,
	})
	if err != nil {
		panic(err)
	}
}

  • user_srv\initialize\init_logger.go:zap初始化
package initialize

import "go.uber.org/zap"

func InitLogger() {
	logger, _ := zap.NewDevelopment()
	zap.ReplaceGlobals(logger)
}

  • yaml
//user_srv/config_pro.yaml
mysql:
  host: '192.168.124.51'
  port: '3306'
  user: 'root'
  password: 'jiushi'
  db: 'mxshop_user_srv'
//user_srv/config_pro.yaml
mysql:
  host: '192.168.124.51'
  port: '3306'
  user: 'root'
  password: 'jiushi'
  db: 'mxshop_user_srv'
  • user_srv/main.go:主逻辑修改
package main

import (
	"flag"
	"fmt"
	"go.uber.org/zap"
	"nd/user_srv/global"
	"nd/user_srv/handler"
	"nd/user_srv/initialize"
	"nd/user_srv/proto"
	"net"

	"google.golang.org/grpc"
)

func main() {
	IP := flag.String("ip", "0.0.0.0", "ip地址")
	Port := flag.Int("port", 50051, "端口号")

	//初始化
	initialize.InitLogger()
	initialize.InitConfig()
	initialize.InitDB()
	zap.S().Info(global.ServerConfig)

	flag.Parse()
	zap.S().Info("ip: ", *IP)
	zap.S().Info("port: ", *Port)

	server := grpc.NewServer()
	proto.RegisterUserServer(server, &handler.UserServer{})
	lis, err := net.Listen("tcp", fmt.Sprintf("%s:%d", *IP, *Port))
	if err != nil {
		panic("failed to listen:" + err.Error())
	}
	err = server.Serve(lis)
	if err != nil {
		panic("failed to start grpc:" + err.Error())
	}
}

六、grpc健康检查

  • grpc健康检查官方地址:https://github.com/grpc/grpc/blob/master/doc/health-checking.md

grpc服务注册到consul

  • user_srv/config/config.go:添加ConsulConfig配置对象
package config

type MysqlConfig struct {
	Host     string `mapstructure:"host" json:"host"`
	Port     int    `mapstructure:"port" json:"port"`
	Name     string `mapstructure:"db" json:"db"`
	User     string `mapstructure:"user" json:"user"`
	Password string `mapstructure:"password" json:"password"`
}

type ConsulConfig struct {
	Host string `mapstructure:"host" json:"host"`
	Port int    `mapstructure:"port" json:"port"`
}

type ServerConfig struct {
	Name       string       `mapstructure:"name" json:"name"`
	MysqlInfo  MysqlConfig  `mapstructure:"mysql" json:"mysql"`
	ConsulInfo ConsulConfig `mapstructure:"consul" json:"consul"`
}

  • yaml
//user_srv/config_pro.yaml
mysql:
  host: '192.168.124.51'
  port: '3306'
  user: 'root'
  password: 'jiushi'
  db: 'mxshop_user_srv'

name: 'user_srv'

consul:
  host: '192.168.124.51'
  port: '8500'
//user_srv/config_pro.yaml
mysql:
  host: '192.168.124.51'
  port: '3306'
  user: 'root'
  password: 'jiushi'
  db: 'mxshop_user_srv'

name: 'user_srv'

consul:
  host: '192.168.124.51'
  port: '8500'
  • user_srv/main.go
    • ①.注册健康检查
    • ②.注册服务
package main

import (
	"flag"
	"fmt"
	"github.com/hashicorp/consul/api"
	"go.uber.org/zap"
	"google.golang.org/grpc/health"
	"google.golang.org/grpc/health/grpc_health_v1"
	"nd/user_srv/global"
	"nd/user_srv/handler"
	"nd/user_srv/initialize"
	"nd/user_srv/proto"
	"net"

	"google.golang.org/grpc"
)

func main() {
	IP := flag.String("ip", "0.0.0.0", "ip地址")
	Port := flag.Int("port", 50051, "端口号")

	//初始化
	initialize.InitLogger()
	initialize.InitConfig()
	initialize.InitDB()
	zap.S().Info(global.ServerConfig)

	flag.Parse()
	zap.S().Info("ip: ", *IP)
	zap.S().Info("port: ", *Port)

	server := grpc.NewServer()
	proto.RegisterUserServer(server, &handler.UserServer{})
	lis, err := net.Listen("tcp", fmt.Sprintf("%s:%d", *IP, *Port))
	if err != nil {
		panic("failed to listen:" + err.Error())
	}

	//注册服务健康检查
	grpc_health_v1.RegisterHealthServer(server, health.NewServer())

	//服务注册
	cfg := api.DefaultConfig()
	cfg.Address = fmt.Sprintf("%s:%d", global.ServerConfig.ConsulInfo.Host,
		global.ServerConfig.ConsulInfo.Port)

	client, err := api.NewClient(cfg)
	if err != nil {
		panic(err)
	}
	//生成对应的检查对象
	check := &api.AgentServiceCheck{
		GRPC:                           fmt.Sprintf("192.168.124.9:%d", *Port),
		Timeout:                        "5s",
		Interval:                       "5s",
		DeregisterCriticalServiceAfter: "15s",
	}

	//生成注册对象
	registration := new(api.AgentServiceRegistration)
	registration.Name = global.ServerConfig.Name
	registration.ID = global.ServerConfig.Name
	registration.Port = *Port
	registration.Tags = []string{"imooc", "bobby", "user", "srv"}
	registration.Address = "192.168.124.9"
	registration.Check = check

	err = client.Agent().ServiceRegister(registration)
	if err != nil {
		panic(err)
	}

	err = server.Serve(lis)
	if err != nil {
		panic("failed to start grpc:" + err.Error())
	}
}

16、注册中心-consul_第6张图片


七、gin集成consul

user_web层需既要完成服务发现、也需要完成服务注册

1 - UserSrvClient优化

  • UserSrvClient优化:每个接口都需要UserSrvClient,每次如果都重新获取都需要tcp三次握手,优化成统一初始化全局的UserSrvClient;当然这里还会存在一个问题,就是多个连接都使用了同一个UserSrvClient,这个后续再优化
  • user_web/global/global.go:添加全局UserSrvClient对象
package global

import (
	ut "github.com/go-playground/universal-translator"

	"web_api/user_web/config"
	"web_api/user_web/proto"
)

var (
	Trans         ut.Translator
	ServerConfig  *config.ServerConfig = &config.ServerConfig{}
	UserSrvClient proto.UserClient
)

  • user_web/initialize/init_srv_conn.go:添加初始化的业务逻辑
package initialize

import (
	"fmt"
	"github.com/hashicorp/consul/api"
	"go.uber.org/zap"
	"google.golang.org/grpc"
	"google.golang.org/grpc/credentials/insecure"
	"web_api/user_web/global"
	"web_api/user_web/proto"
)

func InitSrvConn() {
	cfg := api.DefaultConfig()
	consulInfo := global.ServerConfig.ConsulInfo
	cfg.Address = fmt.Sprintf("%s:%d", consulInfo.Host, consulInfo.Port)

	userSrvHost := ""
	userSrvPort := 0

	client, err := api.NewClient(cfg)
	if err != nil {
		panic(err)
	}

	data, err := client.Agent().ServicesWithFilter(fmt.Sprintf("Service == \"%s\"", global.ServerConfig.UserSrvInfo.Name))
	if err != nil {
		panic(err)
	}

	// 只要获取一个就可以了
	for _, value := range data {
		userSrvHost = value.Address
		userSrvPort = value.Port
		break
	}

	if userSrvHost == "" {
		zap.S().Fatal("[InitSrvConn] 连接 【用户服务失败】")
		return
	}

	//拨号连接用户grpc服务器
	userConn, err := grpc.Dial(fmt.Sprintf("%s:%d", userSrvHost, userSrvPort),
		grpc.WithTransportCredentials(insecure.NewCredentials()))
	if err != nil {
		zap.S().Errorw("[GetUserList] 连接 【用户服务失败】", "msg", err.Error())
	}
	// 1. 后续用户服务下线了如何处理  2. 该端口了 3. 改ip了
	// 已经事先建立好了链接,这样后续就不用再进行tcp的三次握手了
	// 一个连接多个groutine公用,性能问题 - 连接池
	userClient := proto.NewUserClient(userConn)
	global.UserSrvClient = userClient
}

  • user_web/main.go:添加初始化srv的连接
package main

import (
	"fmt"
	"web_api/user_web/global"
	"web_api/user_web/initialize"

	"github.com/gin-gonic/gin/binding"
	ut "github.com/go-playground/universal-translator"
	"github.com/go-playground/validator/v10"
	"go.uber.org/zap"

	myvalidator "web_api/user_web/validator"
)

func main() {
	//1. 初始化logger
	initialize.InitLogger()
	//2. 初始化配置文件
	initialize.InitConfig()
	//3. 初始化routers
	Router := initialize.Routers()
	//4. 初始化翻译
	if err := initialize.InitTrans("zh"); err != nil {
		panic(err)
	}

	//5. 初始化srv的连接
	initialize.InitSrvConn()

	//注册验证器
	if v, ok := binding.Validator.Engine().(*validator.Validate); ok {
		_ = v.RegisterValidation("mobile", myvalidator.ValidateMobile)
		_ = v.RegisterTranslation("mobile", global.Trans, func(ut ut.Translator) error {
			return ut.Add("mobile", "{0} 非法的手机号码!", true) // see universal-translator for details
		}, func(ut ut.Translator, fe validator.FieldError) string {
			t, _ := ut.T("mobile", fe.Field())
			return t
		})
	}

	/*
		1. S()可以获取一个全局的sugar,可以让我们自己设置一个全局的logger
		2. 日志是分级别的,debug, info , warn, error, fetal
			debug最低,fetal最高,如果配置成info,所有比info低的都不会输出
			NewProduction默认日志级别为info
			NewDevelopment默认日志级别为debug
		3. S函数和L函数很有用, 提供了一个全局的安全访问logger的途径
	*/
	zap.S().Debugf("启动服务器, 端口: %d", global.ServerConfig.Port)

	if err := Router.Run(fmt.Sprintf(":%d", global.ServerConfig.Port)); err != nil {
		zap.S().Panic("启动失败:", err.Error())
	}
}

2 - gin集成consul

  • yaml配置:添加user_srv的name,consul的host和ip
//user_web/config_debug.yaml
name: 'user-web'
port: '8081'
user_srv:
  host: '127.0.0.1'
  port: '50051'
  name: 'user_srv'
jwt:
  key: 'VYLDYq3&hGWjWqF$K1ih'
sms:
  key: ''
  secrect: ''
  expire: 300
redis:
  host: '192.168.124.51'
  port: '6379'
consul:
  host: '192.168.124.51'
  port: '8500'

//user_web/config_pro.yaml
name: 'user-web'
port: '8081'
user_srv:
  host: '127.0.0.1'
  port: '50051'
  name: 'user_srv'
jwt:
  key: 'VYLDYq3&hGWjWqF$K1ih'
sms:
  key: ''
  secrect: ''
  expire: 300
redis:
  host: '192.168.124.51'
  port: '6379'
consul:
  host: '192.168.124.51'
  port: '8500'

  • user_web/api/api_user.go:删除掉初始化UserSrvClient的操作,将对应的UserSrvClient都替换为global.UserSrvClient
package api

import (
	"context"
	"fmt"
	"net/http"
	"strconv"
	"strings"
	"time"

	"github.com/dgrijalva/jwt-go"
	"github.com/gin-gonic/gin"
	"github.com/go-playground/validator"
	"github.com/go-redis/redis"
	"go.uber.org/zap"
	"google.golang.org/grpc/codes"
	"google.golang.org/grpc/status"

	"web_api/user_web/forms"
	"web_api/user_web/global"
	"web_api/user_web/global/response"
	"web_api/user_web/middlewares"
	"web_api/user_web/models"
	"web_api/user_web/proto"
)

func HandleGrpcErrorToHttp(err error, c *gin.Context) {
	//将grpc的code转换成http的状态码
	if err != nil {
		if e, ok := status.FromError(err); ok {
			switch e.Code() {
			case codes.NotFound:
				c.JSON(http.StatusNotFound, gin.H{
					"msg": e.Message(),
				})
			case codes.Internal:
				c.JSON(http.StatusInternalServerError, gin.H{
					"msg:": "内部错误",
				})
			case codes.InvalidArgument:
				c.JSON(http.StatusBadRequest, gin.H{
					"msg": "参数错误",
				})
			case codes.Unavailable:
				c.JSON(http.StatusInternalServerError, gin.H{
					"msg": "用户服务不可用",
				})
			default:
				c.JSON(http.StatusInternalServerError, gin.H{
					"msg": e.Code(),
				})
			}
			return
		}
	}
}

func HandleValidatorError(c *gin.Context, err error) {
	errs, ok := err.(validator.ValidationErrors)
	if !ok {
		c.JSON(http.StatusOK, gin.H{
			"msg": err.Error(),
		})
	}
	c.JSON(http.StatusBadRequest, gin.H{
		"error": removeTopStruct(errs.Translate(global.Trans)),
	})
}

func removeTopStruct(fields map[string]string) map[string]string {
	rsp := map[string]string{}
	for field, err := range fields {
		rsp[field[strings.Index(field, ".")+1:]] = err
	}
	return rsp
}

func GetUserList(ctx *gin.Context) {
	//拨号连接用户grpc服务器 跨域的问题 - 后端解决 也可以前端来解决
	//claims, _ := ctx.Get("claims")
	//currentUser := claims.(*models.CustomClaims)
	//zap.S().Infof("访问用户: %d", currentUser.ID)

	pn := ctx.DefaultQuery("pn", "0")
	pnInt, _ := strconv.Atoi(pn)
	pSize := ctx.DefaultQuery("psize", "10")
	pSizeInt, _ := strconv.Atoi(pSize)

	rsp, err := global.UserSrvClient.GetUserList(context.Background(), &proto.PageInfo{
		Pn:    uint32(pnInt),
		PSize: uint32(pSizeInt),
	})
	if err != nil {
		zap.S().Errorw("[GetUserList] 查询 【用户列表】 失败")
		HandleGrpcErrorToHttp(err, ctx)
		return
	}

	result := make([]interface{}, 0)
	for _, value := range rsp.Data {
		user := response.UserResponse{
			Id:       value.Id,
			NickName: value.NickName,
			//Birthday: time.Time(time.Unix(int64(value.BirthDay), 0)).Format("2006-01-02"),
			Birthday: response.JsonTime(time.Unix(int64(value.BirthDay), 0)),
			Gender:   value.Gender,
			Mobile:   value.Mobile,
		}
		result = append(result, user)
	}

	ctx.JSON(http.StatusOK, result)
}

func PassWordLogin(c *gin.Context) {
	//表单验证
	passwordLoginForm := forms.PassWordLoginForm{}
	if err := c.ShouldBind(&passwordLoginForm); err != nil {
		HandleValidatorError(c, err)
		return
	}

	if store.Verify(passwordLoginForm.CaptchaId, passwordLoginForm.Captcha, false) {
		c.JSON(http.StatusBadRequest, gin.H{
			"captcha": "验证码错误",
		})
		return
	}

	//登录逻辑
	if rsp, err := global.UserSrvClient.GetUserByMobile(context.Background(), &proto.MobileRequest{
		Mobile: passwordLoginForm.Mobile,
	}); err != nil {
		if e, ok := status.FromError(err); ok {
			switch e.Code() {
			case codes.NotFound:
				c.JSON(http.StatusBadRequest, map[string]string{
					"mobile": "用户不存在",
				})
			default:
				c.JSON(http.StatusInternalServerError, map[string]string{
					"mobile": "登录失败",
				})
			}
			return
		}
	} else {
		//只是查询到用户了而已,并没有检查密码
		if passRsp, pasErr := global.UserSrvClient.CheckPassWord(context.Background(), &proto.PasswordCheckInfo{
			Password:          passwordLoginForm.PassWord,
			EncryptedPassword: rsp.PassWord,
		}); pasErr != nil {
			c.JSON(http.StatusInternalServerError, map[string]string{
				"password": "登录失败",
			})
		} else {
			if passRsp.Success {
				//生成token
				j := middlewares.NewJWT()
				claims := models.CustomClaims{
					ID:          uint(rsp.Id),
					NickName:    rsp.NickName,
					AuthorityId: uint(rsp.Role),
					StandardClaims: jwt.StandardClaims{
						NotBefore: time.Now().Unix(),               //签名的生效时间
						ExpiresAt: time.Now().Unix() + 60*60*24*30, //30天过期
						Issuer:    "imooc",
					},
				}
				token, err := j.CreateToken(claims)
				if err != nil {
					c.JSON(http.StatusInternalServerError, gin.H{
						"msg": "生成token失败",
					})
					return
				}

				c.JSON(http.StatusOK, gin.H{
					"id":         rsp.Id,
					"nick_name":  rsp.NickName,
					"token":      token,
					"expired_at": (time.Now().Unix() + 60*60*24*30) * 1000,
				})
			} else {
				c.JSON(http.StatusBadRequest, map[string]string{
					"msg": "登录失败",
				})
			}
		}
	}
}

func Register(c *gin.Context) {
	//用户注册
	registerForm := forms.RegisterForm{}
	if err := c.ShouldBind(&registerForm); err != nil {
		HandleValidatorError(c, err)
		return
	}

	//验证码
	rdb := redis.NewClient(&redis.Options{
		Addr: fmt.Sprintf("%s:%d", global.ServerConfig.RedisInfo.Host, global.ServerConfig.RedisInfo.Port),
	})
	value, err := rdb.Get(registerForm.Mobile).Result()
	if err == redis.Nil {
		c.JSON(http.StatusBadRequest, gin.H{
			"code": "验证码错误",
		})
		return
	} else {
		if value != registerForm.Code {
			c.JSON(http.StatusBadRequest, gin.H{
				"code": "验证码错误",
			})
			return
		}
	}

	user, err := global.UserSrvClient.CreateUser(context.Background(), &proto.CreateUserInfo{
		NickName: registerForm.Mobile,
		PassWord: registerForm.PassWord,
		Mobile:   registerForm.Mobile,
	})

	if err != nil {
		zap.S().Errorf("[Register] 查询 【新建用户失败】失败: %s", err.Error())
		HandleGrpcErrorToHttp(err, c)
		return
	}

	j := middlewares.NewJWT()
	claims := models.CustomClaims{
		ID:          uint(user.Id),
		NickName:    user.NickName,
		AuthorityId: uint(user.Role),
		StandardClaims: jwt.StandardClaims{
			NotBefore: time.Now().Unix(),               //签名的生效时间
			ExpiresAt: time.Now().Unix() + 60*60*24*30, //30天过期
			Issuer:    "imooc",
		},
	}
	token, err := j.CreateToken(claims)
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{
			"msg": "生成token失败",
		})
		return
	}

	c.JSON(http.StatusOK, gin.H{
		"id":         user.Id,
		"nick_name":  user.NickName,
		"token":      token,
		"expired_at": (time.Now().Unix() + 60*60*24*30) * 1000,
	})
}

16、注册中心-consul_第7张图片


八、完整源码

mxshop_srvsV6.0.rar

你可能感兴趣的:(Go微服务实战-电商系统,consul,golang,微服务,中间件,架构)