• golang 工程组件 grpc-gateway—yaml定义http规则,和自定义实现网关路由


    yaml定义http规则,和自定义实现网关路由

    proto定义http规则总归是麻烦的,因为proto文件还是定义消息,grpc接口好一些。配置http规则有更好的方式。我们可以使用yaml文件定义接口的http规则。 同时有些接口不是只是让网关转发这么简单 有时需要自己定网关接口handler

    yaml定义http规则
    type: google.api.Service
    config_version: 3
    
    http:
      rules:
        # {package}.{message}.{method}
        - selector: user.User.Get
          get: "/user/{id}"
        - selector: user.User.AddOrUpdate
          post: "/user"
          body: "*"
          additional_bindings:
            - put: "/user"
              body: "*"
            - patch: "/user"
              body: "addr"
        - selector: user.User.Delete
          delete: "/user/{id}"
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    proto文件

    syntax = "proto3";
    package  user;
    option go_package = "user/proto";
    
    message Member{
      int64 id = 1;
      string userName = 2[json_name = "user_name"];
      int32 age = 3;
      string phone = 4;
      Addr addr = 5;
    }
    message Addr {
      string province = 1;
      string city = 2;
      string county = 3;
    }
    
    message UploadRequest {
      int64  size = 1;
      bytes content = 2;
    }
    message UploadResponse {
      string filePath= 1[json_name = "file_path"];
    }
    service User{
      rpc Get(Member) returns (Member) {}
      rpc AddOrUpdate(Member) returns (Member) { }
      rpc Delete(Member) returns (Member) {}
      rpc Upload(stream UploadRequest) returns (UploadResponse){}
    }
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32

    生成消息,grpc,网关

    # 生成message 
    protoc --proto_path=proto --go_out=proto --go_opt=paths=source_relative proto/user.proto 
    # 生成grpc service 
    protoc --proto_path=proto --go-grpc_out=proto --go-grpc_opt=paths=source_relative proto/user.proto
    #生成gateway
    protoc --proto_path=proto  --grpc-gateway_out=proto  --grpc-gateway_opt logtostderr=true  --grpc-gateway_opt paths=source_relative  --grpc-gateway_opt grpc_api_configuration=proto/user.yaml  proto/user.proto
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    参考grpc-gateway入门中的启动代码就能调用对应接口啦

    自定义实现网关路由

    在生成gateway后,前面proto文件中我们预留了一个文件上传grpc接口。然后在yaml中我们是没有定义对应http规则。 所以需要自定义实现对应的网关路由,来应对复杂的业务情况。

    gateway.go

    通过mux.HandlePath添加自定义处理的路由和对应handler函数

    package gateway
    
    import (
    	"context"
    	"flag"
    	"google.golang.org/grpc/health/grpc_health_v1"
    	"net/http"
    	"user/user-server/gateway/middleware"
    
    	"github.com/grpc-ecosystem/grpc-gateway/v2/runtime"
    	"google.golang.org/grpc"
    	"google.golang.org/grpc/credentials/insecure"
    
    	gw "user/proto"
    )
    
    var (
    	grpcServerEndpoint = flag.String("grpc-server-endpoint", "localhost:50051", "gRPC server endpoint")
    )
    
    func Run() error {
    	ctx := context.Background()
    	ctx, cancel := context.WithCancel(ctx)
    	defer cancel()
    	inComingOpt := runtime.WithIncomingHeaderMatcher(func(s string) (string, bool) {
    		switch s {
    		case "Service-Authorization":
    			return "service-authorization", true
    		default:
    			return "", false
    		}
    		return "", false
    	})
    
    	//创建连接,用于健康检查
    	conn, err := grpc.Dial(*grpcServerEndpoint, grpc.WithTransportCredentials(insecure.NewCredentials()))
    	if err != nil {
    		return err
    	}
    
    	mux := runtime.NewServeMux(inComingOpt, runtime.WithHealthzEndpoint(grpc_health_v1.NewHealthClient(conn)))
    	//添加自定义处理函数
    	mux.HandlePath("POST", "/upload", uploadHandler)
    	handler := middleware.Cors(mux)
    	opts := []grpc.DialOption{grpc.WithTransportCredentials(insecure.NewCredentials())}
    	err = gw.RegisterUserHandlerFromEndpoint(ctx, mux, *grpcServerEndpoint, opts)
    	if err != nil {
    		return err
    	}
    
    	return http.ListenAndServe(":8081", handler)
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53

    upload.go

    写对应网关需要注册的handler

    package gateway
    
    import (
    	"context"
    	"fmt"
    	"github.com/golang/protobuf/jsonpb"
    	"google.golang.org/grpc"
    	"google.golang.org/grpc/credentials/insecure"
    	"google.golang.org/grpc/metadata"
    	"io"
    	"net/http"
    	"user/proto"
    )
    
    func uploadHandler(w http.ResponseWriter, r *http.Request, pathParams map[string]string) {
    	serviceAuthorization := r.Header.Get("Service-Authorization")
    	fmt.Println(serviceAuthorization)
    	err := r.ParseForm()
    	if err != nil {
    		http.Error(w, fmt.Sprintf("上传失败:%s", err.Error()), http.StatusInternalServerError)
    		return
    	}
    	f, header, err := r.FormFile("attachment")
    	if err != nil {
    		http.Error(w, fmt.Sprintf("上传失败:%s", err.Error()), http.StatusInternalServerError)
    		return
    	}
    	defer f.Close()
    
    	conn, err := grpc.Dial(*grpcServerEndpoint, grpc.WithTransportCredentials(insecure.NewCredentials()))
    	if err != nil {
    		http.Error(w, fmt.Sprintf("上传失败:%s", err.Error()), http.StatusInternalServerError)
    		return
    	}
    	defer conn.Close()
    	c := proto.NewUserClient(conn)
    	ctx := context.Background()
    	ctx = metadata.NewOutgoingContext(ctx, metadata.New(map[string]string{"file_name": header.Filename, "service-authorization": serviceAuthorization}))
    	stream, err := c.Upload(ctx)
    	if err != nil {
    		http.Error(w, fmt.Sprintf("上传失败:%s", err.Error()), http.StatusInternalServerError)
    		return
    	}
    	buf := make([]byte, 100)
    	for {
    		n, err := f.Read(buf)
    		if err != nil && err != io.EOF {
    			http.Error(w, fmt.Sprintf("上传失败:%s", err.Error()), http.StatusInternalServerError)
    			return
    		}
    		if n == 0 {
    			break
    		}
    		stream.Send(&proto.UploadRequest{
    			Content: buf[:n],
    			Size:    int64(n),
    		})
    	}
    	res, err := stream.CloseAndRecv()
    	if err != nil {
    		http.Error(w, fmt.Sprintf("上传失败:%s", err.Error()), http.StatusInternalServerError)
    		return
    	}
    	m := jsonpb.Marshaler{}
    	str, err := m.MarshalToString(res)
    	if err != nil {
    		http.Error(w, fmt.Sprintf("上传失败:%s", err.Error()), http.StatusInternalServerError)
    		return
    	}
    	w.Header().Add("Content-Type", "application/json")
    	fmt.Fprint(w, str)
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73

    重新启动即可

  • 相关阅读:
    Java多线程(二) 线程池
    mysql字符串函数大全(更新完成)
    【解决方案】SkeyeVSS+ SkeyeARS“国土卫士”农田水利视频监管系统,实现国土资源监管智能化
    通过数据导入导出功能批量重命名文件名称更简单
    到底什么是AQS?面试时你能说明白吗!
    R语言使用ggplot2包可视化指定连续变量和风险比HR值的关系、、基于限制性立方样条分析方法、限制性立方样条cox回归模型
    10.20作业
    我的python-web基础(Flask前后端不分类)
    查看数据库中全部的表
    JSON概念、基础语法及数据和对象转换
  • 原文地址:https://blog.csdn.net/qq_43058348/article/details/134028601