首页 >资讯参考 >

天天看热讯:基于Go/Grpc/kubernetes/Istio开发微服务的最佳实践尝试 - 1/3

发布日期:2023-03-31 14:30:22 来源:博客园 分享

基于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-gatewayprotoc-gen-openapiv2protoc-gen-goprotoc-gen-go-grpcbufwire

下面是安装这些工具的教程地址:

protoc-gen-grpc-gateway& protoc-gen-openapiv2& protoc-gen-go& protoc-gen-go-grpc这四个工具的安装教程请查看:install protoc-gen* tools

wirewire工具的安装教程请查看:install wire tool

bufbuf工具的安装教程请查看: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

versionbreaking之间添加如下依赖代码:

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 fileif err := viper.ReadInConfig(); err != nil {panic("read config failed.[ERROR]=>" + err.Error())}conf := &Config{}// Assign the overloaded configuration to the globalif 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.Portctx, 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.UnimplementedPongServiceServerpongClient v1.PongServiceClientconf       *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.gohttp.gorun.gowire.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 serverRunGrpcServer(initServer(conf), conf)// run http serverRunHttpServer(conf)// listen exit server eventHandleExitServer(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)<-chctx, 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

标签:

Copyright ©  2015-2022 西南兽药网  版权所有  备案号:皖ICP备2022009963号-8   联系邮箱:39 60 29 14 2@qq.com