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()
}
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"`
}
package global
import (
"gorm.io/gorm"
"nd/user_srv/config"
)
var (
DB *gorm.DB
ServerConfig config.ServerConfig
)
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)
}
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)
}
}
package initialize
import "go.uber.org/zap"
func InitLogger() {
logger, _ := zap.NewDevelopment()
zap.ReplaceGlobals(logger)
}
//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'
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())
}
}
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"`
}
//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'
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())
}
}
user_web层需既要完成服务发现、也需要完成服务注册
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
)
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
}
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())
}
}
//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'
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(®isterForm); 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,
})
}
mxshop_srvsV6.0.rar