赞
踩
mkdir grpc-go-client
cd grpc-go-client
go mod grpc-go-client
# 下载并安装gin
go get -u github.com/gin-gonic/gin
Main.go
package main import ( "fmt" "net/http" "github.com/gin-gonic/gin" ) func main(){ r := gin.Default() // 测试一个get请求 r.GET("/rest/n/:name", func(c *gin.Context) { name := c.Param("name") c.JSON(http.StatusOK, gin.H{ "result": fmt.Sprint(name), }) }) // Run http server if err := r.Run(":8052"); err != nil { fmt.Printf("could not run server: %v", err) } }
运行http服务
go run main.go
1、创建pb文件夹和protos文件夹
mkdir pb
mkdir protos
2、创建传输图片的proto文件
photo.proto
syntax = "proto3"; package pb; option go_package = ".;pb"; message PhotoRuquest{ string name = 1; bytes databytes = 2; } message PhotoResponse{ string res = 1; } service Photoer { rpc SendPhoto (PhotoRuquest) returns (PhotoResponse){} }
3、编译生成
go get google.golang.org/grpc
protoc --proto_path=./protos/ ./protos/*.proto --go_out=plugins=grpc:./pb
4、在启动文件中引入连接grpc服务器
import (
"grpc-go-client/pb"
"google.golang.org/grpc"
)
const port = ":50051" // 服务器端口
conn,err :=grpc.Dial("localhost"+port,grpc.WithInsecure())
if err !=nil{
log.Fatal(err.Error())
}
defer conn.Close()
// conn 调用相关服务的“通道”
// 用于main函数中调用
func sendImg(client pb.PhotoerClient,data []byte,imgName string){
res,err :=client.SendPhoto(context.Background(),&pb.PhotoRuquest{Databytes:data,Name:imgName})
if err !=nil{
log.Fatal(err.Error())
}
fmt.Println(res.Res)
}
}
//main函数中调用
sendImg(pb.NewPhotoerClient(conn),data,imgFile.Filename)
完整main函数代码
package main import ( "fmt" "grpc-go-client/pb" "io/ioutil" "log" "net/http" "os" "context" "github.com/gin-gonic/gin" "google.golang.org/grpc" ) const port = ":50051" func main(){ conn,err :=grpc.Dial("localhost"+port,grpc.WithInsecure()) if err !=nil{ log.Fatal(err.Error()) } defer conn.Close() r := gin.Default() // 上传文件 r.POST("/upload", func(c *gin.Context) { // 单个文件 imgFile, err := c.FormFile("img") f,_:=imgFile.Open() data,_ :=ioutil.ReadAll(f) sendImg(pb.NewPhotoerClient(conn),data,imgFile.Filename) if err != nil { c.JSON(http.StatusInternalServerError, gin.H{ "message": err.Error(), }) return } log.Println(imgFile.Filename) c.JSON(http.StatusOK, gin.H{ "message": fmt.Sprintf("'%s' uploaded!", imgFile.Filename), }) }) // Run http server if err := r.Run(":8052"); err != nil { log.Fatalf("could not run server: %v", err) } } // 功能函数 func sendImg(client pb.PhotoerClient,data []byte,imgName string){ res,err :=client.SendPhoto(context.Background(),&pb.PhotoRuquest{Databytes:data,Name:imgName}) if err !=nil{ log.Fatal(err.Error()) } fmt.Println(res.Res) } }
1、通过viper管理项目的日志配置及后续其他的配置
安装viper
go get -u github.com/spf13/viper
安装zap记录日志
// 安装 lumberjack,zap
go get -u github.com/natefinch/lumberjack
go get -u go.uber.org/zap
2、创建日志相关的文件夹和配置文件
# 创建logs文件夹和config配置文件夹
mkdir logs # 存放日志
mkdir config
3、编写日志的配置文件
logconf.json
{
"level": "info",
"filename": "logs/viper_zap_gin.log",
"maxsize": "1",
"max_age": "30",
"max_backups": "5",
"version": "v1.8"
}
4、使用zap日志库
创建logger文件夹,编写zap库的设置logger.go
package logger import ( // "grpc-go-client/config" // 配置文件 "github.com/gin-gonic/gin" "github.com/natefinch/lumberjack" "go.uber.org/zap" "go.uber.org/zap/zapcore" "net" "net/http" "net/http/httputil" "os" "runtime/debug" "strings" "time" ) var Logger *zap.Logger type logConfig struct { Level string `json:"level"` Filename string `json:"filename"` MaxSize int `json:"maxsize"` MaxAge int `json:"max_age"` MaxBackups int `json:"max_backups"` Version string `json:"version"` } // InitLogger 初始化Logger func InitLogger(cfg *logConfig) (err error) { writeSyncer := getLogWriter(cfg.Filename, cfg.MaxSize, cfg.MaxBackups, cfg.MaxAge) encoder := getEncoder() // for human operators. var l = new(zapcore.Level) err = l.UnmarshalText([]byte(cfg.Level)) if err != nil { return } // 打印到控制台和日志文件 core := zapcore.NewCore(encoder, zapcore.NewMultiWriteSyncer(zapcore.AddSync(os.Stderr), writeSyncer), l) Logger = zap.New(core, zap.AddCaller()) return } func getEncoder() zapcore.Encoder { encoderConfig := zap.NewProductionEncoderConfig() encoderConfig.EncodeTime = zapcore.ISO8601TimeEncoder encoderConfig.TimeKey = "time" encoderConfig.EncodeLevel = zapcore.CapitalLevelEncoder encoderConfig.EncodeDuration = zapcore.SecondsDurationEncoder encoderConfig.EncodeCaller = zapcore.ShortCallerEncoder return zapcore.NewJSONEncoder(encoderConfig) } func getLogWriter(filename string, maxSize, maxBackup, maxAge int) zapcore.WriteSyncer { lumberJackLogger := &lumberjack.Logger{ Filename: filename, MaxSize: maxSize, MaxBackups: maxBackup, MaxAge: maxAge, } return zapcore.AddSync(lumberJackLogger) } // GinLogger 接收gin框架默认的日志 func GinLogger(logger *zap.Logger) gin.HandlerFunc { return func(c *gin.Context) { start := time.Now() path := c.Request.URL.Path query := c.Request.URL.RawQuery c.Next() cost := time.Since(start) logger.Info(path, zap.Int("status", c.Writer.Status()), zap.String("method", c.Request.Method), zap.String("path", path), zap.String("query", query), zap.String("ip", c.ClientIP()), zap.String("user-agent", c.Request.UserAgent()), zap.String("errors", c.Errors.ByType(gin.ErrorTypePrivate).String()), zap.Duration("cost", cost), ) } } // GinRecovery recover掉项目可能出现的panic,并使用zap记录相关日志 func GinRecovery(logger *zap.Logger, stack bool) gin.HandlerFunc { return func(c *gin.Context) { defer func() { if err := recover(); err != nil { // Check for a broken connection, as it is not really a // condition that warrants a panic stack trace. var brokenPipe bool if ne, ok := err.(*net.OpError); ok { if se, ok := ne.Err.(*os.SyscallError); ok { if strings.Contains(strings.ToLower(se.Error()), "broken pipe") || strings.Contains(strings.ToLower(se.Error()), "connection reset by peer") { brokenPipe = true } } } httpRequest, _ := httputil.DumpRequest(c.Request, false) if brokenPipe { logger.Error(c.Request.URL.Path, zap.Any("error", err), zap.String("request", string(httpRequest)), ) // If the connection is dead, we can't write a status to it. c.Error(err.(error)) // nolint: errcheck c.Abort() return } if stack { logger.Error("[Recovery from panic]", zap.Any("error", err), zap.String("request", string(httpRequest)), zap.String("stack", string(debug.Stack())), ) } else { logger.Error("[Recovery from panic]", zap.Any("error", err), zap.String("request", string(httpRequest)), ) } c.AbortWithStatus(http.StatusInternalServerError) } }() c.Next() } }
main.go文件中启用viper和zap
viper.SetConfigFile("./config/conf.json") //指定json配置文件的路径
// viper.SetConfigFile("./config/conf.yaml") //指定yaml配置文件的路径
err := viper.ReadInConfig() // 读取配置信息
if err != nil { // 配置失败
panic(fmt.Errorf("Fatal error config file: %s \n", err))
}
if err = viper.Unmarshal(logConf); err != nil {
panic(fmt.Errorf("unmarshal conf failed, err:%s \n", err))
}
// 监控配置文件变化
viper.WatchConfig()
// 在gin中使用日志中间件
r.Use(logger.GinLogger(logger.Logger), logger.GinRecovery(logger.Logger, true))
1、创建APP文件夹,用来放置不同的AI应用
mkdir app
# 在app下创建ping文件夹用于测试
mkdir app/ping
2、路由分组
#在ping下创建hander.go和router.go
touch hander.go router.go
3、创建routers文件夹初始化路由
mkdir routers
touch routers/routers.go
routers.go
package routers import ( "net/http" "github.com/gin-gonic/gin" "grpc-go-client/logger" ) type Option func(*gin.Engine) var options = []Option{} func Include(opts ...Option) { options = append(options, opts...) } func cors() gin.HandlerFunc { return func(c *gin.Context) { method := c.Request.Method c.Header("Access-Control-Allow-Origin", "*") c.Header("Access-Control-Allow-Headers", "Content-Type,AccessToken,X-CSRF-Token, Authorization, Token") c.Header("Access-Control-Allow-Methods", "POST, GET, OPTIONS") c.Header("Access-Control-Expose-Headers", "Content-Length, Access-Control-Allow-Origin, Access-Control-Allow-Headers, Content-Type") c.Header("Access-Control-Allow-Credentials", "true") if method == "OPTIONS" { c.AbortWithStatus(http.StatusNoContent) } c.Next() } } // 初始化 gin的引擎 func RouterInit() *gin.Engine { r := gin.New() // 跨域 r.Use(cors()) // 使用zap日志 r.Use(logger.GinLogger(logger.Logger), logger.GinRecovery(logger.Logger, true)) for _, opt := range options { opt(r) } return r }
调整后的main.go文件内容
package main import ( "fmt" "grpc-go-client/app/ping" "grpc-go-client/logger" "grpc-go-client/routers" "log" "github.com/spf13/viper" ) var logConf = new(logger.LogConfig) func main() { // viper viper.SetConfigFile("./config/logconf.json") //指定json配置文件的路径 err := viper.ReadInConfig() // 读取配置信息 if err != nil { // 配置失败 panic(fmt.Errorf("Fatal error config file: %s \n", err)) } if err = viper.Unmarshal(logConf); err != nil { panic(fmt.Errorf("unmarshal conf failed, err:%s \n", err)) } // 监控配置文件变化 viper.WatchConfig() // zap 初始化 if err := logger.InitLogger(logConf); err != nil { fmt.Printf("init logger failed, err:%v\n", err) return } // 加载多个APP的路由配置 routers.Include(ping.Routers) // 初始化所有的app的路由 r := routers.RouterInit() // Run http server if err := r.Run(":8052"); err != nil { log.Fatalf("could not run server: %v", err) } }
项目代码略
2、创建grpc服务端
python编译
# 在protos文件夹中
python -m grpc_tools.protoc --python_out=../pb --grpc_python_out=../pb -I . ocrPic.proto
golang编译
protoc --proto_path=./ ocrPic.proto --go_out=plugins=grpc:../pb
报错:
AttributeError: module 'google.protobuf.descriptor' has no attribute '_internal_create_key'
解决
pip install --upgrade protobuf
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。