golang grpc protobuf 接收/发送未知对象

grpc 无法像 http 那也直接把未知对象赋值给 interface 的引用,需要解析为pb定义的具体结构,可以用 google/protobuf/struct.proto 中的 struct 接收 object 类型,然后在golang里面进行解析,代码如下。

可以用于解决一个字段接收或发送多种对象。

效果:

实现转化 pbStruct.struct request.Filter -> golang Struct -> pbStruct.struct response.Data
在这里插入图片描述

proto定义:

syntax = "proto3";

import "google/api/annotations.proto";
import "google/protobuf/struct.proto";

message StringMessage {
  string type = 1;
  google.protobuf.Struct filter = 2;
}

message ListAttrValueReq{
  string attr = 1;
  string keyword = 2;
}

message Parent{
  string type = 1 ;
  string id = 2;
}

message User {
  string name = 1;
  int64  age = 2;
}

message StringResp {
  google.protobuf.Struct data = 2;
}

service EchoService {
  rpc Echo(StringMessage) returns (StringResp) {
    option (google.api.http) = {
      post: "/v1/echo"
      body: "*"
    };
  }
}

server代码:

package main

import (
	"encoding/json"
	"fmt"
	pbstruct "github.com/golang/protobuf/ptypes/struct"
	"golang.org/x/net/context"
	"google.golang.org/grpc"
	pb "grpc-learn/proto"
	"log"
	"net"
)

const (
	port = ":51001"
)

type server struct {
}

type User struct {
	Name string
	Age  int
}

func (s *server) Echo(c context.Context, req *pb.StringMessage) (*pb.StringResp, error) {
	resp := &pb.StringResp{}
	data := &pb.ListAttrValueReq{}
	PbStructToInterface(req.Filter, &data)

	InterfaceToPbStruct(data, &resp.Data)

	fmt.Println(resp.Data)
	return resp, nil
}

func PbStructToInterface(source *pbstruct.Struct, result interface{}) error {
	bytes, err := source.MarshalJSON()
	if err != nil {
		return err
	}
	if err = json.Unmarshal(bytes, &result); err != nil {
		return err
	}
	return nil
}

func InterfaceToPbStruct(source interface{}, result **pbstruct.Struct) error {
	marshal, err := json.Marshal(source)
	if err != nil {
		return err
	}
	if err = json.Unmarshal(marshal, &result); err != nil {
		return err
	}
	fmt.Println(result)
	return nil
}

func main() {
	lis, err := net.Listen("tcp", port)
	if err != nil {
		log.Fatalf("failed to listen: %v", err)
	}
	// Creates a new gRPC server
	s := grpc.NewServer()
	pb.RegisterEchoServiceServer(s, &server{})
	s.Serve(lis)
}

getway代码:

package main

import (
	"flag"
	"log"
	"net/http"

	"github.com/golang/glog"
	"github.com/grpc-ecosystem/grpc-gateway/runtime"
	"golang.org/x/net/context"
	"google.golang.org/grpc"
	gw "grpc-learn/proto"
)

var (
	echoEndpoint = flag.String("echo_endpoint", "localhost:51001", "endpoint of Gateway")
)

func run() error {
	ctx := context.Background()
	ctx, cancel := context.WithCancel(ctx)
	defer cancel()

	mux := runtime.NewServeMux()
	opts := []grpc.DialOption{grpc.WithInsecure()}
	err := gw.RegisterEchoServiceHandlerFromEndpoint(ctx, mux, *echoEndpoint, opts)
	if err != nil {
		return err
	}
	log.Println("服务开启")
	return http.ListenAndServe(":8080", mux)
}

func main() {
	flag.Parse()
	defer glog.Flush()

	if err := run(); err != nil {
		glog.Fatal(err)
	}
}