基于Go/Grpc/kubernetes/Istio开发微服务的最佳实践尝试 - 1/3 - 杨建勇
source link: https://www.cnblogs.com/yangjianyong-bky/p/17276194.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
基于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
Recommend
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK