• 13、用户web层服务(一)


    前言


    一、高性能日志库zap

    1 - zap Quick Start

    • zap日志库官网地址https://github.com/uber-go/zap

    • zap的优点:性能高

    • Zap提供了两种类型的日志记录器—Sugared Logger和Logger

      • 在性能很好但不是很关键的上下文中,使用SugaredLogger。它比其他结构化日志记录包快4-10倍,并且支持结构化和printf风格的日志记录
      • 在每一微秒和每一次内存分配都很重要的上下文中,使用Logger。它甚至比SugaredLogger更快,内存分配次数也更少,但它只支持强类型的结构化日志记录
    • 为什么logger的效率更高:因为logger指明了类型,zap就不会启用go的反射,这样效率就比Sugared Logger更高;但即使是Sugared Logger也比一般的日志库性能高很多了

    • SugaredLogger使用

    package main
    
    import (
    	"time"
    
    	"go.uber.org/zap"
    )
    
    func main() {
    	url := "https//www.baidu.com"
    	logger, _ := zap.NewProduction() // 生产环境下使用
    	// logger, _ := zap.NewDevelopment() // 开发环境下使用
    
    	defer logger.Sync()     // flushes buffer, if any
    	sugar := logger.Sugar() // 使用sugar的实例,更方便的记录日志
    	sugar.Infow("failed to fetch URL",
    		// Structured context as loosely typed key-value pairs.
    		"url", url,
    		"attempt", 3,
    		"backoff", time.Second,
    	)
    	sugar.Infof("Failed to fetch URL: %s", url)
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • logger使用
    package main
    
    import (
    	"go.uber.org/zap"
    )
    
    func main() {
    	url := "https//www.baidu.com"
    	logger, _ := zap.NewProduction() // 生产环境下使用
    	// logger, _ := zap.NewDevelopment() // 开发环境下使用
    	defer logger.Sync() // flushes buffer, if any
    
    	// 这种效率比较高,就是因为指明了类型,zap就不会启用go的反射,这样效率就比较高
    	logger.Info("failed to fetch URL",
    		zap.String("url", url),
    		zap.Int("nums", 3),
    	)
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    2 - zap文件输出

    package main
    
    import (
    	"time"
    
    	"go.uber.org/zap"
    )
    
    func NewLogger() (*zap.Logger, error) {
    	cfg := zap.NewProductionConfig()
    	cfg.OutputPaths = []string{ //可以定位到多个文件中
    		"./myproject.log",
    		"stderr",
    		"stdout",
    	}
    	return cfg.Build()
    }
    
    func main() {
    	logger, err := NewLogger()
    	if err != nil {
    		panic(err)
    		//panic("初始化logger失败")
    	}
    	su := logger.Sugar()
    	defer su.Sync()
    	url := "https://www.baidu.com"
    	su.Info("failed to fetch URL",
    		// Structured context as strongly typed Field values.
    		zap.String("url", url),
    		zap.Int("attempt", 3),
    		zap.Duration("backoff", time.Second),
    	)
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35

    在这里插入图片描述


    二、项目集成zap和router

    这里主要要做的两件事情:
    1、初始化zap和初始化router分离出独立的模块
    2、在模块router中实现与api的绑定

    • user_web\initialize\init_logger.go:初始化日志
    package initialize
    
    import "go.uber.org/zap"
    
    func InitLogger() {
    	logger, _ := zap.NewDevelopment()
    	zap.ReplaceGlobals(logger)
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • user_web\initialize\init_router.go:初始化router
    package initialize
    
    import (
    	"web_api/user_web/router"
    
    	"github.com/gin-gonic/gin"
    )
    
    func Routers() *gin.Engine {
    	Router := gin.Default()
    	ApiGroup := Router.Group("v1")
    
    	router.InitUserRouter(ApiGroup)
    	return Router
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • user_web\router\router_user.go:user的RouterGroup
    package router
    
    import (
    	"web_api/user_web/api"
    
    	"github.com/gin-gonic/gin"
    	"go.uber.org/zap"
    )
    
    func InitUserRouter(Router *gin.RouterGroup) {
    	UserRouter := Router.Group("user")
    	zap.S().Info("配置用户相关的url")
    	{
    		UserRouter.GET("list", api.GetUserList)
    	}
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • user_web\api\api_user.go:api对外接口
    package api
    
    import (
    	"github.com/gin-gonic/gin"
    	"go.uber.org/zap"
    )
    
    func GetUserList(ctx *gin.Context) {
    	zap.S().Debug("获取用户列表")
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • user_web\main.go:main
    package main
    
    import (
    	"fmt"
    	"web_api/user_web/initialize"
    
    	"go.uber.org/zap"
    )
    
    func main() {
    
    	port := 8081
    	//2. 初始化logger
    	initialize.InitLogger()
    	//3. 初始化routers
    	Router := initialize.Routers()
    
    	/*
    		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", port)
    
    	if err := Router.Run(fmt.Sprintf(":%d", port)); err != nil {
    		zap.S().Panic("启动失败:", err.Error())
    	}
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32

    在这里插入图片描述


    三、gin调用grpc服务

    1 - YApi测试

    • 启动YApi测试参考地址
      • user_web\initialize\init_router.go中将路径修改为ApiGroup := Router.Group("/u/v1")
      • YApi中注意修改端口号为8081

    在这里插入图片描述

    2 - proto生成

    • 拷贝user_srv项目下的user.proto,并重新生成protoc --go_out=. --go_opt=paths=source_relative --go-grpc_out=. --go-grpc_opt=paths=source_relative *.proto
    syntax = "proto3";
    import "google/protobuf/empty.proto";
    option go_package = ".;proto";
    
    service User{
        rpc GetUserList(PageInfo) returns (UserListResponse); // 用户列表
        rpc GetUserByMobile(MobileRequest) returns (UserInfoResponse); //通过mobile查询用户
        rpc GetUserById(IdRequest) returns (UserInfoResponse); //通过id查询用户
        rpc CreateUser(CreateUserInfo) returns (UserInfoResponse); // 添加用户
        rpc UpdateUser(UpdateUserInfo) returns (google.protobuf.Empty); // 更新用户
        rpc CheckPassWord(PasswordCheckInfo) returns (CheckResponse); //检查密码
    }
    
    message PageInfo {
        uint32 pn = 1;
        uint32 pSize = 2;
    }
    
    message UserInfoResponse {
        int32 id = 1;
        string passWord = 2;
        string mobile = 3;
        string nickName = 4;
        uint64 birthDay = 5;
        string gender = 6;
        int32 role = 7;
    }
    
    message UserListResponse {
        int32 total = 1;
        repeated UserInfoResponse data = 2;
    }
    
    message CreateUserInfo {
        string nickName = 1;
        string passWord = 2;
        string mobile = 3;
    }
    
    message MobileRequest{
        string mobile = 1;
    }
    
    message IdRequest {
        int32 id = 1;
    }
    
    message UpdateUserInfo {
        int32 id = 1;
        string nickName = 2;
        string gender = 3;
        uint64 birthDay = 4;
    }
    
    message PasswordCheckInfo {
        string password = 1;
        string encryptedPassword = 2;
    }
    
    message CheckResponse{
        bool success = 1;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62

    3 - gin调用grpc服务

    • user_web\global\response\rsp_user.go:增加返回消息,用户对象的封装
    package response
    
    import (
    	"fmt"
    	"time"
    )
    
    type JsonTime time.Time
    
    // 内部自动调用MarshalJSON方法
    func (j JsonTime) MarshalJSON() ([]byte, error) {
    	var stmp = fmt.Sprintf("\"%s\"", time.Time(j).Format("2006-01-02"))
    	return []byte(stmp), nil
    }
    
    type UserResponse struct {
    	Id       int32    `json:"id"`
    	NickName string   `json:"name"`
    	Birthday JsonTime `json:"birthday"`
    	Gender   string   `json:"gender"`
    	Mobile   string   `json:"mobile"`
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • user_web\api\api_user.go
      • 统一规范错误类型提示
      • 实现用户列表接口查询
    package api
    
    import (
    	"context"
    	"fmt"
    	"net/http"
    	"time"
    
    	"github.com/gin-gonic/gin"
    	"go.uber.org/zap"
    	"google.golang.org/grpc"
    	"google.golang.org/grpc/codes"
    	"google.golang.org/grpc/credentials/insecure"
    	"google.golang.org/grpc/status"
    
    	"web_api/user_web/global/response"
    	"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 GetUserList(ctx *gin.Context) {
    	ip := "127.0.0.1"
    	port := 50051
    
    	//拨号连接用户grpc服务器
    	userConn, err := grpc.Dial(fmt.Sprintf("%s:%d", ip, port), grpc.WithTransportCredentials(insecure.NewCredentials()))
    	if err != nil {
    		zap.S().Errorw("[GetUserList] 连接 【用户服务失败】", "msg", err.Error())
    	}
    	//生成grpc的client并调用接口
    	userSrvClient := proto.NewUserClient(userConn)
    	rsp, err := userSrvClient.GetUserList(context.Background(), &proto.PageInfo{
    		Pn:    0,
    		PSize: 0,
    	})
    	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)
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 测试结论
      • YApi的docker需要开启
      • user_srv需要开启,端口50051
      • user_web的http端口,8081

    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述


    四、viper配置管理

    1 - viper简介

    • Viper简介:Viper适用于Go应用程序的完整配置解决方案;它被设计用于在应用程序中工作,并且可以处理所有类型的配置需求和格式
    • Viper的特性
      • 设置默认值
      • 从JSON、TOML、YAML、HCL、envfile和Java properties格式的配置文件读取配置信息
      • 实时监控和重新读取配置文件(可选)
      • 从环境变量中读取
      • 从远程配置系统(etcd或Consul)读取并监控配置变化
      • 从命令行参数读取配置
      • 从buffer读取配置
      • 显式配置值
    • Viper地址https://github.com/spf13/viper
    • config.yaml
    name: 'user-web'
    mysql:
      host: '127.0.0.1'
      port: 3306
    
    • 1
    • 2
    • 3
    • 4
    package main
    
    import (
    	"fmt"
    	"github.com/spf13/viper"
    )
    
    type ServerConfig struct {
    	ServiceName string `mapstructure:"name"`
    	Port        int    `mapstructure:"port"`
    }
    
    func main() {
    	v := viper.New()
    	//文件的路径如何设置
    	v.SetConfigFile("viper_test/config.yaml")
    	if err := v.ReadInConfig(); err != nil {
    		panic(err)
    	}
    	serverConfig := ServerConfig{}
    	if err := v.Unmarshal(&serverConfig); err != nil {
    		panic(err)
    	}
    	fmt.Println(serverConfig)
    	fmt.Printf("%v", v.Get("name"))
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26

    2 - viper环境隔离与动态监控

    • 需求思考:如何实现,不用改任何代码而且线上和线上的配置文件能隔离开
    • 解决方案
      • 采用环境变量的方式来确定是生产环境还是开发环境
      • 使用fsnotify文件变化通知库来实现动态监控
        在这里插入图片描述
    • main.go
    package main
    
    import (
    	"fmt"
    	"github.com/fsnotify/fsnotify"
    	"github.com/spf13/viper"
    	"time"
    )
    
    //如何将线上和线下的配置文件隔离
    //不用改任何代码而且线上和线上的配置文件能隔离开
    
    type MysqlConfig struct {
    	Host string `mapstructure:"host"`
    	Port int    `mapstructure:"port"`
    }
    
    type ServerConfig struct {
    	ServiceName string      `mapstructure:"name"`
    	MysqlInfo   MysqlConfig `mapstructure:"mysql"`
    }
    
    func GetEnvInfo(env string) bool {
    	viper.AutomaticEnv()
    	return viper.GetBool(env)
    	//刚才设置的环境变量 想要生效 我们必须得重启goland
    }
    
    func main() {
    	debug := GetEnvInfo("DEV_CONFIG")
    	configFilePrefix := "config"
    	configFileName := fmt.Sprintf("viper_test/%s_pro.yaml", configFilePrefix)
    	if debug {
    		configFileName = fmt.Sprintf("viper_test/%s_debug.yaml", configFilePrefix)
    	}
    
    	v := viper.New()
    	//文件的路径如何设置
    	v.SetConfigFile(configFileName)
    	if err := v.ReadInConfig(); err != nil {
    		panic(err)
    	}
    	serverConfig := ServerConfig{}
    	if err := v.Unmarshal(&serverConfig); err != nil {
    		panic(err)
    	}
    	fmt.Println(serverConfig)
    	fmt.Printf("%v", v.Get("name"))
    
    	//viper的功能 - 动态监控变化
    	v.WatchConfig()
    	v.OnConfigChange(func(e fsnotify.Event) {
    		fmt.Println("config file channed: ", e.Name)
    		_ = v.ReadInConfig()
    		_ = v.Unmarshal(&serverConfig)
    		fmt.Println(serverConfig)
    	})
    
    	time.Sleep(time.Second * 300)
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • yaml
    //config_pro.yaml
    name: 'user-web'
    mysql:
      host: '127.0.0.2'
      port: 3309
    
    //config_debug.yaml
    name: 'user-web2'
    mysql:
      host: '127.0.0.1'
      port: 3306
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    在这里插入图片描述

    3 - gin集成viper

    • 需要集成的配置包含
      • user_srv的服务配置信息
      • user_web的服务配置信息
    • yaml
    //config_debug.yaml
    name: 'user-web'
    port: '8081'
    user_srv:
      host: '127.0.0.1'
      port: '50051'
    //config_pro.yaml
    name: 'user-web'
    port: '8031'
    user_srv:
      host: '127.0.0.1'
      port: '50052'
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • user_web\config\config.go:添加user_srv和user_web的配置struct
    package config
    
    type UserSrvConfig struct {
    	Host string `mapstructure:"host" json:"host"`
    	Port int    `mapstructure:"port" json:"port"`
    }
    
    type ServerConfig struct {
    	Name        string        `mapstructure:"name" json:"name"`
    	Port        int           `mapstructure:"port" json:"port"`
    	UserSrvInfo UserSrvConfig `mapstructure:"user_srv" json:"user_srv"`
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • user_web\global\global.go:为了让ServerConfig可以在其他文件中读取,在global.go中添加全局变量
    package global
    
    import "web_api/user_web/config"
    
    var (
    	ServerConfig *config.ServerConfig = &config.ServerConfig{}
    )
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • user_web\initialize\init_config.go:初始化读取配置信息
    package initialize
    
    import (
    	"fmt"
    
    	"github.com/fsnotify/fsnotify"
    	"github.com/spf13/viper"
    	"go.uber.org/zap"
    
    	"web_api/user_web/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)
    
    	//viper的功能 - 动态监控变化
    	v.WatchConfig()
    	v.OnConfigChange(func(e fsnotify.Event) {
    		zap.S().Infof("配置文件产生变化: &s", e.Name)
    		_ = v.ReadInConfig()
    		_ = v.Unmarshal(&global.ServerConfig)
    		zap.S().Infof("配置信息: &v", global.ServerConfig)
    	})
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • main.go
      • 添加viper的初始化:initialize.InitConfig()
      • 服务启动端口修改为全局的对象:global.ServerConfig.Port
    package main
    
    import (
    	"fmt"
    	"web_api/user_web/global"
    	"web_api/user_web/initialize"
    
    	"go.uber.org/zap"
    )
    
    func main() {
    	//1. 初始化logger
    	initialize.InitLogger()
    	//2. 初始化配置文件
    	initialize.InitConfig()
    	//3. 初始化routers
    	Router := initialize.Routers()
    
    	/*
    		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())
    	}
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33

    在这里插入图片描述

    五、完整源码

    源码地址:https://download.csdn.net/download/qq23001186/86261620

  • 相关阅读:
    01_SHELL编程之变量定义(一)
    docker基础篇:安装tomcat
    ES6——ES6内置对象
    Meta收购德国触觉技术公司Lofelt,同时FTC严格要求Meta收购事宜
    自定义类加载器加载网络Class
    20.RTSP取流实现方法
    关于无障碍适配的笔记
    机器学习(七)朴素贝叶斯、决策树与随机森林
    学了一天java,我总结了这些知识点
    质量管理五大工具详解
  • 原文地址:https://blog.csdn.net/qq23001186/article/details/125892200