当前位置:网站首页>13. User web layer services (I)
13. User web layer services (I)
2022-07-27 13:51:00 【Endless character】
Catalog
Preface
- Prior to 6、 Microservice architecture analysis in , We will split srv Layer services and web Layer services
- srv We are already in 12、 User microservices Realized
- Next we need to achieve web User service of layer
One 、 High performance log Library zap
1 - zap Quick Start
zap The official website address of the logstore :https://github.com/uber-go/zap
zap The advantages of : High performance
Zap There are two types of loggers available —Sugared Logger and Logger
- In a context where performance is good but not critical , Use SugaredLogger. It's faster than other structured logging packages 4-10 times , And support structured and printf Style logging
- In the context where every microsecond and every memory allocation is important , Use Logger. It's even better than SugaredLogger faster , Less memory allocation , But it only supports strongly typed structured logging
Why? logger More efficient : because logger Indicates the type ,zap Will not enable go Reflection of , So the efficiency is better than Sugared Logger Higher ; But even if it's Sugared Logger It is also much higher than the general log Library
SugaredLogger Use
package main
import (
"time"
"go.uber.org/zap"
)
func main() {
url := "https//www.baidu.com"
logger, _ := zap.NewProduction() // Use in production environment
// logger, _ := zap.NewDevelopment() // Use... In a development environment
defer logger.Sync() // flushes buffer, if any
sugar := logger.Sugar() // Use sugar Example , It is more convenient to record logs
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)
}
- logger Use
package main
import (
"go.uber.org/zap"
)
func main() {
url := "https//www.baidu.com"
logger, _ := zap.NewProduction() // Use in production environment
// logger, _ := zap.NewDevelopment() // Use... In a development environment
defer logger.Sync() // flushes buffer, if any
// This is more efficient , Because it indicates the type ,zap Will not enable go Reflection of , In this way, the efficiency is relatively high
logger.Info("failed to fetch URL",
zap.String("url", url),
zap.Int("nums", 3),
)
}
2 - zap File output
package main
import (
"time"
"go.uber.org/zap"
)
func NewLogger() (*zap.Logger, error) {
cfg := zap.NewProductionConfig()
cfg.OutputPaths = []string{
// You can navigate to multiple files
"./myproject.log",
"stderr",
"stdout",
}
return cfg.Build()
}
func main() {
logger, err := NewLogger()
if err != nil {
panic(err)
//panic(" initialization logger Failure ")
}
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),
)
}

Two 、 Project integration zap and router
Here are two main things to do :
1、 initialization zap And initialize router Separate independent modules
2、 In the module router To achieve and api The binding of
- user_web\initialize\init_logger.go: Initialize log
package initialize
import "go.uber.org/zap"
func InitLogger() {
logger, _ := zap.NewDevelopment()
zap.ReplaceGlobals(logger)
}
- user_web\initialize\init_router.go: initialization 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
}
- user_web\router\router_user.go:user Of 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(" Configure user related url")
{
UserRouter.GET("list", api.GetUserList)
}
}
- user_web\api\api_user.go:api External interface
package api
import (
"github.com/gin-gonic/gin"
"go.uber.org/zap"
)
func GetUserList(ctx *gin.Context) {
zap.S().Debug(" Get the list of users ")
}
- user_web\main.go:main
package main
import (
"fmt"
"web_api/user_web/initialize"
"go.uber.org/zap"
)
func main() {
port := 8081
//2. initialization logger
initialize.InitLogger()
//3. initialization routers
Router := initialize.Routers()
/* 1. S() You can get a global sugar, We can set a global logger 2. Logs are graded ,debug, info , warn, error, fetal debug The minimum ,fetal The highest , If configured to info, All than info Low ones will not output NewProduction The default log level is info NewDevelopment The default log level is debug 3. S Functions and L Functions are useful , Provides a global security access logger Way */
zap.S().Debugf(" Start the server , port : %d", port)
if err := Router.Run(fmt.Sprintf(":%d", port)); err != nil {
zap.S().Panic(" Boot failure :", err.Error())
}
}

3、 ... and 、gin call grpc service
1 - YApi test
- start-up YApi test : Reference address
- user_web\initialize\init_router.go Change the path to
ApiGroup := Router.Group("/u/v1") - YApi Note that the port number is modified to 8081
- user_web\initialize\init_router.go Change the path to

2 - proto Generate
- Copy user_srv Under the project of user.proto, And regenerate :
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); // User list
rpc GetUserByMobile(MobileRequest) returns (UserInfoResponse); // adopt mobile Query the user
rpc GetUserById(IdRequest) returns (UserInfoResponse); // adopt id Query the user
rpc CreateUser(CreateUserInfo) returns (UserInfoResponse); // Add users
rpc UpdateUser(UpdateUserInfo) returns (google.protobuf.Empty); // Update user
rpc CheckPassWord(PasswordCheckInfo) returns (CheckResponse); // Check the password
}
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;
}
3 - gin call grpc service
- user_web\global\response\rsp_user.go: Add return message , Encapsulation of user objects
package response
import (
"fmt"
"time"
)
type JsonTime time.Time
// Internal automatic call MarshalJSON Method
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"`
}
- user_web\api\api_user.go:
- Unified specification error type prompt
- Implement user list interface query
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) {
// take grpc Of code convert to http The status code
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:": " internal error ",
})
case codes.InvalidArgument:
c.JSON(http.StatusBadRequest, gin.H{
"msg": " Parameter error ",
})
case codes.Unavailable:
c.JSON(http.StatusInternalServerError, gin.H{
"msg": " User services are not available ",
})
default:
c.JSON(http.StatusInternalServerError, gin.H{
"msg": e.Code(),
})
}
return
}
}
}
func GetUserList(ctx *gin.Context) {
ip := "127.0.0.1"
port := 50051
// Dial up connection users grpc The server
userConn, err := grpc.Dial(fmt.Sprintf("%s:%d", ip, port), grpc.WithTransportCredentials(insecure.NewCredentials()))
if err != nil {
zap.S().Errorw("[GetUserList] Connect 【 User service failed 】", "msg", err.Error())
}
// Generate grpc Of client And call the interface
userSrvClient := proto.NewUserClient(userConn)
rsp, err := userSrvClient.GetUserList(context.Background(), &proto.PageInfo{
Pn: 0,
PSize: 0,
})
if err != nil {
zap.S().Errorw("[GetUserList] Inquire about 【 User list 】 Failure ")
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)
}
- Test conclusion
- YApi Of docker Need to open
- user_srv Need to open , port 50051
- user_web Of http port ,8081



Four 、viper Configuration Management
1 - viper brief introduction
- Viper brief introduction :Viper Apply to Go Complete configuration solution for applications ; It's designed to work in applications , And can handle all types of configuration requirements and formats
- Viper Characteristics of
- Set the default value
- from JSON、TOML、YAML、HCL、envfile and Java properties Read the configuration information from the configuration file
- Real time monitoring and rereading configuration files ( Optional )
- Read from environment variable
- Configure the system remotely (etcd or Consul) Read and monitor configuration changes
- Read configuration from command line parameters
- from buffer Reading configuration
- Explicit configuration value
- Viper Address :https://github.com/spf13/viper
- config.yaml:
name: 'user-web'
mysql:
host: '127.0.0.1'
port: 3306
package main
import (
"fmt"
"github.com/spf13/viper"
)
type ServerConfig struct {
ServiceName string `mapstructure:"name"`
Port int `mapstructure:"port"`
}
func main() {
v := viper.New()
// How to set the path of the file
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"))
}
2 - viper Environmental isolation and dynamic monitoring
- Demand thinking : How to achieve , There is no need to change any code, and online and online configuration files can be separated
- Solution :
- Use environment variables to determine whether it is a production environment or a development environment
- Use
fsnotifyFile change notification library to achieve dynamic monitoring
- main.go
package main
import (
"fmt"
"github.com/fsnotify/fsnotify"
"github.com/spf13/viper"
"time"
)
// How to isolate online and offline configuration files
// There is no need to change any code, and online and online configuration files can be separated
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)
// The environment variable just set Want to take effect We have to restart 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()
// How to set the path of the file
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 The function of - Dynamically monitor changes
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)
}
- 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

3 - gin Integrate viper
- Configurations that need to be integrated include
- user_srv Service configuration information
- user_web Service configuration information
- 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'
- user_web\config\config.go: add to user_srv and user_web Configuration of 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"`
}
- user_web\global\global.go: In order to make ServerConfig It can be read in other files , stay global.go Add global variables to
package global
import "web_api/user_web/config"
var (
ServerConfig *config.ServerConfig = &config.ServerConfig{
}
)
- user_web\initialize\init_config.go: Initialize read configuration information
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)
// The environment variable just set Want to take effect We have to restart 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()
// How to set the path of the file
v.SetConfigFile(configFileName)
if err := v.ReadInConfig(); err != nil {
panic(err)
}
// How this object is used in other files - Global variables
if err := v.Unmarshal(&global.ServerConfig); err != nil {
panic(err)
}
zap.S().Infof(" Configuration information : &v", global.ServerConfig)
//viper The function of - Dynamically monitor changes
v.WatchConfig()
v.OnConfigChange(func(e fsnotify.Event) {
zap.S().Infof(" Configuration file changes : &s", e.Name)
_ = v.ReadInConfig()
_ = v.Unmarshal(&global.ServerConfig)
zap.S().Infof(" Configuration information : &v", global.ServerConfig)
})
}
- main.go
- add to viper The initialization :
initialize.InitConfig() - The service startup port is modified to a global object :
global.ServerConfig.Port
- add to viper The initialization :
package main
import (
"fmt"
"web_api/user_web/global"
"web_api/user_web/initialize"
"go.uber.org/zap"
)
func main() {
//1. initialization logger
initialize.InitLogger()
//2. Initialize configuration file
initialize.InitConfig()
//3. initialization routers
Router := initialize.Routers()
/* 1. S() You can get a global sugar, We can set a global logger 2. Logs are graded ,debug, info , warn, error, fetal debug The minimum ,fetal The highest , If configured to info, All than info Low ones will not output NewProduction The default log level is info NewDevelopment The default log level is debug 3. S Functions and L Functions are useful , Provides a global security access logger Way */
zap.S().Debugf(" Start the server , port : %d", global.ServerConfig.Port)
if err := Router.Run(fmt.Sprintf(":%d", global.ServerConfig.Port)); err != nil {
zap.S().Panic(" Boot failure :", err.Error())
}
}

5、 ... and 、 Complete source code
Source code address :https://download.csdn.net/download/qq23001186/86261620
边栏推荐
- Cesium region clipping, local rendering
- 软考 系统架构设计师 简明教程 | 系统设计
- 字节跳动 AI Lab 总监李航:语言模型的过去、现在和未来
- Jianzhi offer 07 rebuild binary tree -- construct binary tree from middle order and post order traversal sequence
- 小程序毕设作品之微信校园洗衣小程序毕业设计成品(1)开发概要
- [leetcode] 592. Fraction addition and subtraction
- Egg swagger doc graphic verification code solution
- 软考 系统架构设计师 简明教程 | 软件测试
- Leetcode error reporting and its solution
- Keras deep learning practice - recommend system data coding
猜你喜欢

Summary of scaling and coding methods in Feature Engineering

js将数组根据指定属性值分组成二维数组

Oppo self-developed large-scale knowledge map and its application in digital intelligence engineering

Go language series: how to build a go language development environment?

建议收藏,PMP应战篇(2)之易混淆知识点
![[300 + selected interview questions from big companies continued to share] big data operation and maintenance sharp knife interview question column (IX)](/img/cf/44b3983dd5d5f7b92d90d918215908.png)
[300 + selected interview questions from big companies continued to share] big data operation and maintenance sharp knife interview question column (IX)

Keras深度学习实战——推荐系统数据编码

Fiddler bag capturing Tool + night God simulator

SCI thesis writing

在“元宇宙空间”UTONMOS将打开虚实结合的数字世界
随机推荐
A brief analysis of the four process pools
Fiddler抓包工具+夜神模拟器
Image features and extraction
建议收藏,PMP应战篇(2)之易混淆知识点
字节跳动 AI Lab 总监李航:语言模型的过去、现在和未来
libevent 之 evconnlistener_new_bind
What are the precautions for using carbon brushes
剑指Offer 07 重建二叉树 -- 从中序与后序遍历序列构造二叉树
MFC FTP creates multi-level folders and uploads files to the specified directory of FTP
evutil_make_internal_pipe_: pipe: Too many open files
opencv图像的缩放平移及旋转
[introduction to C language] zzulioj 1021-1025
双料第一!
将目标检测大尺寸图片裁剪成固定尺寸图片
Add index to the field of existing data (Damon database version)
Unapp prevents continuous click errors
小程序毕设作品之微信校园洗衣小程序毕业设计成品(8)毕业设计论文模板
软考 系统架构设计师 简明教程 | 软件系统建模
[C Advanced] pointer array vs array pointer
小程序毕设作品之微信校园洗衣小程序毕业设计成品(3)后台功能