前文

  关于grpc是什么以及python如何搭建,可以参考这篇:https://blog.csdn.net/weixin_42681866/article/details/121509556?spm=1001.2014.3001.5501,本篇说下如何基于golang来搭建;基本上grpc在golang的应用是非常广泛的,主要是用于agent和master的通讯,同时也是云原生的通讯必备;比如k8s就是用的grpc。

实操

  当我们定义好proto文件的时候,需要通过golang-tools将其转换为go文件(这里通过protoc来当转译工具),来支持项目内使用。
  golang protoc工具安装:

  • 进入protobuf release添加链接描述 页面,选择适合自己操作系统的压缩包文件
  • 解压protoc-3.14.0-osx-x86_64.zip并进入protoc-3.14.0-osx-x86_64

$ cd protoc-3.14.0-osx-x86_64/bin

  • 将启动的protoc二进制文件移动到被添加到环境变量的任意path下,如$GOPATH/bin,这里不建议直接将其和系统的一下path放在一起。

$ mv protoc $GOPATH/bin

  • 通过上述命令,将protoc绑定到命令行工具,才能进行编译

  当工具安装完后,需要安装对应包

生成proto的包:go get -u github.com/golang/protobuf/protoc-gen-go
项目内需要用到的grpc:go get -u google.golang.org/grpc

生成文件命令:

protoc -I. --go_out=plugins=grpc:./pb pb/helloworld.proto

注意:如果执行的时候,报路径无法解析的话,则需要在proto文件里手动指定路径,增加:

// 指定 go 的包路径及包名
option go_package="/batch;tasks";

搭建客户端和服务端

  网上相关的很多,这里简单提一下,主要是介绍如何增加权限校验(简易,如果需要tls层构成https可以看其他相关文章)和过滤器:
搭建服务端:

package server

import (
	"context"
	"google.golang.org/grpc"
	"google.golang.org/grpc/codes"
	"google.golang.org/grpc/metadata"
	"google.golang.org/grpc/reflection"
	"google.golang.org/grpc/status"
	"net"
)

type rpcServer struct{}

// 占位
func (s *rpcServer) Hello(ctx context.Context, req *helloWorldPb.HelloRequest) (*helloWorldPb.HelloReply, error) {
     // 做权限校验
	if err := checkSecret(ctx); err != nil {
		return &helloWorldPb.HelloReply{}, err
	}
	return &helloWorldPb.HelloReply{}, nil
}

func RunServer() {
	lis, err := net.Listen("tcp", "127.0.0.1")
	if err != nil {
		logger.Log.Fatalf("failed to listen: %v", err)
	}
	// 创建 RPC 服务容器; 这里的filter是过滤器,失败后不阻塞
	s := grpc.NewServer(grpc.UnaryInterceptor(filter))
	helloWorldPb.RegisterBatchRouteServer(s, &rpcServer{}) // 在gRPC服务端注册服务
	reflection.Register(s)                            //注册服务器反射服务
	// 每个grpc连接对应着gorouting,当请求进来对应分配来处理
	err = s.Serve(lis)
	if err != nil {
		logger.Log.Fatalf("failed to serve: %v", err)
	}
}

这里附上权限校验和过滤器:

// grpc过滤器捕获异常,不使服务停止
func filter(
	ctx context.Context, req interface{},
	info *grpc.UnaryServerInfo,
	handler grpc.UnaryHandler,
) (resp interface{}, err error) {
	logger.Log.Println("filter:", info)
	defer func() {
		if r := recover(); r != nil {
			logger.Log.Errorf("panic: %v", r)
		}
	}()
	return handler(ctx, req)
}

// grpc 统一校验函数
func checkSecret(ctx context.Context) error {
	var (
		appID     string
		appSecret string
	)
	md, ok := metadata.FromIncomingContext(ctx)
	if !ok {
		var authError exception.AuthError
		return status.Errorf(codes.Unauthenticated, authError.Error())

	}
	if value, ok := md["app_id"]; ok {
		appID = value[0]
	}
	if value, ok := md["app_secret"]; ok {
		appSecret = value[0]
	}
	if appID != config.Conf.AppId || appSecret != config.Conf.AppSecret {
		var authError exception.AuthError
		return status.Errorf(codes.Unauthenticated, authError.Error())
	}
	return nil
}

这里插一句,对应到python的grpc校验,就是直接在stub.HelloWorld(hello_request, metadata=self.metadata)的形式将app_id和app_secret传入即可。
golang客户端加权限认证:

package client

import (
	"context"
	"google.golang.org/grpc"
)

type Client struct {
	ServerUrl string
}

type PerRPCCredentials interface {
	GetRequestMetadata(ctx context.Context, uri ...string) (map[string]string, error)
	RequireTransportSecurity() bool
}

// customCredential 自定义认证
type customCredential struct{}

func (c customCredential) GetRequestMetadata(ctx context.Context, uri ...string) (map[string]string, error) {
	return map[string]string{
		"app_id":     Conf.AppId,
		"app_secret": Conf.AppSecret,
	}, nil
}

func (c customCredential) RequireTransportSecurity() bool {
	return false
}

func (c *Client) initClient() (*grpc.ClientConn, error) {
	var opts []grpc.DialOption
	opts = append(opts, grpc.WithInsecure())
	//opts = append(opts, grpc.WithBlock())  // 这个参数开启则当目标服务器连接不上的时候,会阻塞而不是立刻返回失败
	// 使用自定义认证
	opts = append(opts, grpc.WithPerRPCCredentials(new(customCredential)))
	conn, err := grpc.Dial(c.ServerUrl, opts...)
	return conn, err
}
 // 接下来根据initClient返回的连接,结合pb提供的函数可完成客户端的请求
Logo

K8S/Kubernetes社区为您提供最前沿的新闻资讯和知识内容

更多推荐