基于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 作为网关代理。

本最佳实际分为三个局部:

  1. 创立一个 pingservice 的微服务
  2. 创立一个 pongservice 的微服务
  3. 基于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

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 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