基于Go/Grpc/kubernetes/Istio开发微服务的最佳实际尝试 - 1/3
基于Go/Grpc/kubernetes/Istio开发微服务的最佳实际尝试 - 2/3
基于Go/Grpc/kubernetes/Istio开发微服务的最佳实际尝试 - 3/3
我的项目地址:https://github.com/janrs-io/Jgrpc
转载请注明起源:https://janrs.com/br6f
Jgrpc
本我的项目为基于 Go/Grpc/kubernetes/Istio
开发微服务的最佳实际提供参考。
并基于 Jenkins/Gitlab/Harbor
实现了CICD
。
并应用 grpc-gateway
作为网关代理。
本最佳实际分为三个局部:
- 创立一个
pingservice
的微服务 - 创立一个
pongservice
的微服务 - 基于
Jenkins/Gitlab/Harbor
创立CICD
部署流程并部署到k8s/istio
在这一部分中,咱们将创立 pongservice
微服务。
前提
假如曾经装置了以下工具:
protoc-gen-grpc-gateway
protoc-gen-openapiv2
protoc-gen-go
protoc-gen-go-grpc
buf
wire
上面是装置这些工具的教程地址:
protoc-gen-grpc-gateway
& protoc-gen-openapiv2
& protoc-gen-go
& protoc-gen-go-grpc
这四个工具的装置教程请查看:install protoc-gen* tools
wire
wire
工具的装置教程请查看:install wire tool
buf
buf
工具的装置教程请查看:install buf tool
我的项目构造
这部分最终的目录构造如下:
Jgrpc├── devops├── istio-manifests├── kubernetes-manifests└── src └── pongservice ├── buf.gen.yaml ├── cmd │ ├── main.go │ └── server │ ├── grpc.go │ ├── http.go │ ├── run.go │ ├── wire.go │ └── wire_gen.go ├── config │ ├── config.go │ └── config.yaml ├── genproto │ └── v1 │ ├── gw │ │ └── pongservice.pb.gw.go │ ├── pongservice.pb.go │ └── pongservice_grpc.pb.go ├── go.mod ├── go.sum ├── proto │ ├── buf.lock │ ├── buf.yaml │ └── v1 │ ├── pongservice.proto │ └── pongservice.yaml └── service ├── client.go └── server.go14 directories, 20 files
开始
创立我的项目的整体目录构造如下:
Jgrpc├── devops├── istio-manifests├── kubernetes-manifests├── src
创立 pongservice 微服务
创立根本目录
在src
目录下创立名为pongservice
的微服务,目录构造如下:
pongservice├── cmd│ └── server├── config├── proto│ └── v1└── service6 directories, 0 files
生成代码和文件
生成 buf.yaml
在 pongservice/proto 目录下执行以下命令:
buf mod init
此命令创立一个名为 buf.yaml
的文件,位于 ponservice/proto
目录中。buf.yaml
的代码如下:
version: v1breaking: use: - FILElint: use: - DEFAULT
在 version
和 breaking
之间增加如下依赖代码:
deps: - buf.build/googleapis/googleapis
请查看增加此依赖代码的起因:https://github.com/grpc-ecosystem/grpc-gateway#3-external-configuration
增加依赖代码后残缺的 buf.yaml
文件如下:
version: v1deps: - buf.build/googleapis/googleapisbreaking: use: - FILElint: use: - DEFAULT
而后在pongservice/proto
目录下执行如下命令:
buf mod update
执行命令后会生成一个buf.lock
文件,代码如下:
# Generated by buf. DO NOT EDIT.version: v1deps: - remote: buf.build owner: googleapis repository: googleapis commit: 463926e7ee924d46ad0a726e1cf4eacd
生成 pongservice.proto
在 pongservice/proto/v1
目录中应用以下代码创立一个名为 pongservice.proto
的原型文件:
syntax = "proto3";package proto.v1;option go_package = "github.com/janrs-io/Jgrpc/src/pongservice/genproto/v1";service PongService { rpc Pong(PongRequest) returns(PongResponse){}}message PongRequest { string msg = 1 ;}message PongResponse { string msg = 1;}
生成 pongservice.yaml
在 pongservice/proto/v1
目录中应用以下代码创立一个名为 pongservice.yaml
的原型文件:
type: google.api.Serviceconfig_version: 3http: rules: - selector: proto.v1.PongService.Pong get: /pong.v1.pong
生成 buf.gen.yaml
在 pongservice
目录中应用以下代码创立一个名为 buf.gen.yaml
的 yaml 文件:
version: v1plugins: - plugin: go out: genproto/v1 opt: - paths=source_relative - plugin: go-grpc out: genproto/v1 opt: - paths=source_relative - plugin: grpc-gateway out: genproto/v1/gw opt: - paths=source_relative - grpc_api_configuration=proto/v1/pongservice.yaml - standalone=true
在 pongservice
目录中,执行以下命令:
buf generate proto/v1
执行命令后,会在pongservice
目录下主动创立一个genproto目录,该目录下有以下文件:
genproto└── v1 ├── gw │ └── ponservice.pb.gw.go ├── ponservice.pb.go └── ponservice_grpc.pb.go2 directories, 3 files
生成 go.mod
在 pongservice
目录中创立 go.mod
:
go mod init github.com/janrs-io/Jgrpc/src/pongservice && go mod tidy
生成 config.yaml
在 pongservice/config
目录下,创立 config.yaml
文件并增加以下代码:
# grpc configgrpc: host: "" port: ":50051" name: "pong-grpc"# http confighttp: host: "" port: ":9001" name: "pong-http"
生成 config.go
在 pongservice/config
目录下,创立 config.go
文件并增加以下代码:
package configimport ( "net/http" "github.com/spf13/viper" "google.golang.org/grpc")// Config Service configtype Config struct { Grpc Grpc `json:"grpc" yaml:"grpc"` Http Http `json:"http" yaml:"http"`}// NewConfig Initial service's configfunc NewConfig(cfg string) *Config { if cfg == "" { panic("load config file failed.config file can not be empty.") } viper.SetConfigFile(cfg) // Read config file if err := viper.ReadInConfig(); err != nil { panic("read config failed.[ERROR]=>" + err.Error()) } conf := &Config{} // Assign the overloaded configuration to the global if err := viper.Unmarshal(conf); err != nil { panic("assign config failed.[ERROR]=>" + err.Error()) } return conf}// Grpc Grpc server configtype Grpc struct { Host string `json:"host" yaml:"host"` Port string `json:"port" yaml:"port"` Name string `json:"name" yaml:"name"` Server *grpc.Server}// Http Http server configtype Http struct { Host string `json:"host" yaml:"host"` Port string `json:"port" yaml:"port"` Name string `json:"name" yaml:"name"` Server *http.Server}
而后在 pongservice
目录中再次运行 go mod tidy
。
生成 client.go
在 pongservice/service
目录下,创立 client.go
文件并增加以下代码:
package serviceimport ( "context" "time" "google.golang.org/grpc" "google.golang.org/grpc/credentials/insecure" "github.com/janrs-io/Jgrpc/src/pongservice/config" v1 "github.com/janrs-io/Jgrpc/src/pongservice/genproto/v1")// NewClient New service's clientfunc NewClient(conf *config.Config) (v1.PongServiceClient, error) { serverAddress := conf.Grpc.Host + conf.Grpc.Port ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) defer cancel() conn, err := grpc.DialContext(ctx, serverAddress, grpc.WithTransportCredentials(insecure.NewCredentials())) if err != nil { return nil, err } client := v1.NewPongServiceClient(conn) return client, nil}
在 pongservice/service
目录下,创立 server.go
文件并增加以下代码:
package serviceimport ( "context" "github.com/janrs-io/Jgrpc/src/pongservice/config" v1 "github.com/janrs-io/Jgrpc/src/pongservice/genproto/v1")// Server Server structtype Server struct { v1.UnimplementedPongServiceServer pongClient v1.PongServiceClient conf *config.Config}// NewServer New service grpc serverfunc NewServer(conf *config.Config, pongClient v1.PongServiceClient) v1.PongServiceServer { return &Server{ pongClient: pongClient, conf: conf, }}func (s *Server) Pong(ctx context.Context, req *v1.PongRequest) (*v1.PongResponse, error) { return &v1.PongResponse{Msg: "response pong msg:" + req.Msg}, nil}
生成 run server 文件
在pongservice/cmd/server
目录下,创立以下四个文件:
grpc.go
http.go
run.go
wire.go
将以下代码增加到 grpc.go
文件中:
package serverimport ( "fmt" "log" "net" "google.golang.org/grpc" "github.com/janrs-io/Jgrpc/src/pongservice/config" v1 "github.com/janrs-io/Jgrpc/src/pongservice/genproto/v1")// RunGrpcServer Run grpc serverfunc RunGrpcServer(server v1.PongServiceServer, conf *config.Config) { grpcServer := grpc.NewServer() v1.RegisterPongServiceServer(grpcServer, server) fmt.Println("Listening grpc server on port" + conf.Grpc.Port) listen, err := net.Listen("tcp", conf.Grpc.Port) if err != nil { panic("listen grpc tcp failed.[ERROR]=>" + err.Error()) } go func() { if err = grpcServer.Serve(listen); err != nil { log.Fatal("grpc serve failed", err) } }() conf.Grpc.Server = grpcServer}
将以下代码增加到 http.go
文件中:
package serverimport ( "context" "fmt" "net/http" "github.com/grpc-ecosystem/grpc-gateway/v2/runtime" "google.golang.org/grpc" "google.golang.org/grpc/credentials/insecure" "github.com/janrs-io/Jgrpc/src/pongservice/config" v1 "github.com/janrs-io/Jgrpc/src/pongservice/genproto/v1/gw")// RunHttpServer Run http serverfunc RunHttpServer(conf *config.Config) { mux := runtime.NewServeMux() opts := []grpc.DialOption{ grpc.WithTransportCredentials(insecure.NewCredentials()), } if err := v1.RegisterPongServiceHandlerFromEndpoint( context.Background(), mux, conf.Grpc.Port, opts, ); err != nil { panic("register service handler failed.[ERROR]=>" + err.Error()) } httpServer := &http.Server{ Addr: conf.Http.Port, Handler: mux, } fmt.Println("Listening http server on port" + conf.Http.Port) go func() { if err := httpServer.ListenAndServe(); err != nil { fmt.Println("listen http server failed.[ERROR]=>" + err.Error()) } }() conf.Http.Server = httpServer}
将以下代码增加到 run.go
文件中:
package serverimport ( "context" "fmt" "os" "os/signal" "syscall" "time" "github.com/janrs-io/Jgrpc/src/pongservice/config" v1 "github.com/janrs-io/Jgrpc/src/pongservice/genproto/v1")// Run Run service serverfunc Run(cfg string) { conf := config.NewConfig(cfg) // run grpc server RunGrpcServer(initServer(conf), conf) // run http server RunHttpServer(conf) // listen exit server event HandleExitServer(conf)}// SetServer Wire inject service's componentfunc initServer(conf *config.Config) v1.PongServiceServer { server, err := InitServer(conf) if err != nil { panic("run server failed.[ERROR]=>" + err.Error()) } return server}// HandleExitServer Handle service exit eventfunc HandleExitServer(conf *config.Config) { ch := make(chan os.Signal, 1) signal.Notify(ch, syscall.SIGINT, syscall.SIGTERM) <-ch ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) defer cancel() conf.Grpc.Server.GracefulStop() if err := conf.Http.Server.Shutdown(ctx); err != nil { panic("shutdown service failed.[ERROR]=>" + err.Error()) } <-ctx.Done() close(ch) fmt.Println("Graceful shutdown http & grpc server.")}
将以下代码增加到 wire.go
文件中:
//go:build wireinject// +build wireinjectpackage serverimport ( "github.com/google/wire" "github.com/janrs-io/Jgrpc/src/pongservice/config" v1 "github.com/janrs-io/Jgrpc/src/pongservice/genproto/v1" "github.com/janrs-io/Jgrpc/src/pongservice/service")// InitServer Inject service's componentfunc InitServer(conf *config.Config) (v1.PongServiceServer, error) { wire.Build( service.NewClient, service.NewServer, ) return &service.Server{}, nil}
在 pongservice
目录中再次运行 go mod tidy
:
go mod tidy
而后在 pongservice
目录中执行以下 wire 命令:
wire ./...
执行 wire
命令后,将在 pongsevice/cmd/server
目录中主动创立 wire_gen.go
文件。
生成 main.go
最初一步,在 pongservice/cmd
目录下创立 main.go
文件.
package mainimport ( "flag" "github.com/janrs-io/Jgrpc/src/pongservice/cmd/server")var cfg = flag.String("config", "config/config.yaml", "config file location")// main mainfunc main() { server.Run(*cfg)}
启动 service
在 pongservice
目录下执行以下命令启动微服务:
留神
在pongservice
目录而不是pongservice/cmd
目录中执行命令
go run cmd/main.go
启动服务后,会显示如下信息:
Listening grpc server on port:50051Listening http server on port:9001
在浏览器中输出以下地址即可拜访该服务:
127.0.01:9001/pong.v1.pong?msg=best practice
如果胜利,将返回以下数据:
{ "msg": "response pong msg:best practice"}
总结
当初,咱们曾经理解了如何创立能够开发基本功能微服务的我的项目构造。
在接下来的局部中,咱们持续创立一个名为 pingservice
的微服务,并拜访咱们在这部分中创立的 pongservice
。
转载请注明起源:https://janrs.com/br6f