共计 9400 个字符,预计需要花费 24 分钟才能阅读完成。
基于 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.go | |
14 directories, 20 files |
开始
创立我的项目的整体目录构造如下:
Jgrpc | |
├── devops | |
├── istio-manifests | |
├── kubernetes-manifests | |
├── src |
创立 pongservice 微服务
创立根本目录
在 src
目录下创立名为 pongservice
的微服务,目录构造如下:
pongservice | |
├── cmd | |
│ └── server | |
├── config | |
├── proto | |
│ └── v1 | |
└── service | |
6 directories, 0 files |
生成代码和文件
生成 buf.yaml
在 pongservice/proto 目录下执行以下命令:
buf mod init
此命令创立一个名为 buf.yaml
的文件,位于 ponservice/proto
目录中。buf.yaml
的代码如下:
version: v1 | |
breaking: | |
use: | |
- FILE | |
lint: | |
use: | |
- DEFAULT |
在 version
和 breaking
之间增加如下依赖代码:
deps: | |
- buf.build/googleapis/googleapis |
请查看增加此依赖代码的起因:https://github.com/grpc-ecosystem/grpc-gateway#3-external-configuration
增加依赖代码后残缺的 buf.yaml
文件如下:
version: v1 | |
deps: | |
- buf.build/googleapis/googleapis | |
breaking: | |
use: | |
- FILE | |
lint: | |
use: | |
- DEFAULT |
而后在 pongservice/proto
目录下执行如下命令:
buf mod update
执行命令后会生成一个 buf.lock
文件,代码如下:
# Generated by buf. DO NOT EDIT. | |
version: v1 | |
deps: | |
- 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.Service | |
config_version: 3 | |
http: | |
rules: | |
- selector: proto.v1.PongService.Pong | |
get: /pong.v1.pong |
生成 buf.gen.yaml
在 pongservice
目录中应用以下代码创立一个名为 buf.gen.yaml
的 yaml 文件:
version: v1 | |
plugins: | |
- 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.go | |
2 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 config | |
grpc: | |
host: ""port:":50051"name:"pong-grpc" | |
# http config | |
http: | |
host: ""port:":9001"name:"pong-http" |
生成 config.go
在 pongservice/config
目录下,创立 config.go
文件并增加以下代码:
package config | |
import ( | |
"net/http" | |
"github.com/spf13/viper" | |
"google.golang.org/grpc" | |
) | |
// Config Service config | |
type Config struct { | |
Grpc Grpc `json:"grpc" yaml:"grpc"` | |
Http Http `json:"http" yaml:"http"` | |
} | |
// NewConfig Initial service's config | |
func 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 config | |
type 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 config | |
type 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 service | |
import ( | |
"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 client | |
func 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 service | |
import ( | |
"context" | |
"github.com/janrs-io/Jgrpc/src/pongservice/config" | |
v1 "github.com/janrs-io/Jgrpc/src/pongservice/genproto/v1" | |
) | |
// Server Server struct | |
type Server struct { | |
v1.UnimplementedPongServiceServer | |
pongClient v1.PongServiceClient | |
conf *config.Config | |
} | |
// NewServer New service grpc server | |
func 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 server | |
import ( | |
"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 server | |
func 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 server | |
import ( | |
"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 server | |
func 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 server | |
import ( | |
"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 server | |
func 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 component | |
func 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 event | |
func 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 wireinject | |
package server | |
import ( | |
"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 component | |
func 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 main | |
import ( | |
"flag" | |
"github.com/janrs-io/Jgrpc/src/pongservice/cmd/server" | |
) | |
var cfg = flag.String("config", "config/config.yaml", "config file location") | |
// main main | |
func main() {server.Run(*cfg) | |
} |
启动 service
在 pongservice
目录下执行以下命令启动微服务:
留神
在pongservice
目录而不是pongservice/cmd
目录中执行命令
go run cmd/main.go
启动服务后,会显示如下信息:
Listening grpc server on port:50051 | |
Listening 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