然后生成源文件,编写服务端:
func (s *Server) SayHello (ctx context.Context, request *yoyoyo.HelloRequest) (*yoyoyo.HelloResponse, error) { res := new(yoyoyo.HelloResponse) res.Info = map[string]string{"name": "夏色祭", "age": "16", "hobby": "斯哈斯哈"} return res, nil }其它地方不变,只把方法里面的内容修改一下即可,然后是客户端:
package main import ( "context" "fmt" "google.golang.org/grpc" "matsuri/yoyoyo" ) func main() { conn, err := grpc.Dial("127.0.0.1:33333", grpc.WithInsecure()) if err != nil { fmt.Println(err) return } defer func() { _ = conn.Close() }() client := yoyoyo.NewHelloClient(conn) response, _ := client.SayHello(context.Background(), &yoyoyo.HelloRequest{}) fmt.Println(response.Info) // map[age:16 hobby:斯哈斯哈 name:夏色祭] fmt.Println(response.Info["name"]) // 夏色祭 }最后还是用 Python 来调用一下,看看会返回什么结果,首先我们猜测肯定是返回一个字典:
import grpc import hello_pb2_grpc as pb2_grpc import hello_pb2 as pb2 channel = grpc.insecure_channel("127.0.0.1:33333") client = pb2_grpc.HelloStub(channel=channel) response = client.SayHello( pb2.HelloRequest() ) print(response.info) # {\'name\': \'夏色祭\', \'age\': \'16\', \'hobby\': \'斯哈斯哈\'}和我们猜测是一样的,不过说实话,它不返回字典还能返回啥。
protobuf 中的 timestamp 类型尽管当前已经有很多类型让我们使用,但是还少了一个重要的时间类型。当然时间类型我们可以通过 int64 来实现,也就是传递一个时间戳,然后拿到这个时间戳之后再转化即可。但是我们可以不可以通过 protobuf 直接定义这个类型呢?答案是可以的,只不过这个类型不是内置的,而是需要导入标准 proto 文件,在那里面有相关的实现。
syntax = "proto3"; import "google/protobuf/timestamp.proto"; option go_package = ".;yoyoyo"; // 对 Python 无影响 message HelloRequest { } message Response { string hobby = 1; } message HelloResponse { map<string, string> info = 1; google.protobuf.Timestamp t = 2; } service Hello { rpc SayHello(HelloRequest) returns (HelloResponse) {} }我们多加一个成员,表示当前时间。但是问题来了,这个 Timestamp 是内置的 proto 文件里面的,我们在使用的时候显然不能直接用。因为它不是在我们的编写的 proto 文件中定义的,所以生成 hello.pb.go 里面是没有相关类型的。所以需要从其它地方中导入,而导入的 proto 文件也已经告诉我们位置了,我们不是导入了 google/protobuf/timestamp.proto 吗?那就去里面看看呗。
syntax = "proto3"; package google.protobuf; option csharp_namespace = "Google.Protobuf.WellKnownTypes"; option cc_enable_arenas = true; option go_package = "github.com/golang/protobuf/ptypes/timestamp"; option java_package = "com.google.protobuf"; option java_outer_classname = "TimestampProto"; option java_multiple_files = true; option objc_class_prefix = "GPB";我们看到里面有很多的 package,但是我们只关注 go 的 package,我们看到了后面的一串路径,导入的时候就从这里面导入即可。
注意:如果你想查看这个 proto 文件的话,你需要使用 Pycharm 或者 Goland,并且里面的内容是放在 IDE 内部的 jar 里面的。
package main import ( "context" "fmt" "google.golang.org/grpc" "google.golang.org/protobuf/types/known/timestamppb" "matsuri/yoyoyo" "net" "time" ) type Server struct { } func (s *Server) SayHello (ctx context.Context, request *yoyoyo.HelloRequest) (*yoyoyo.HelloResponse, error) { res := new(yoyoyo.HelloResponse) res.Info = map[string]string{"name": "夏色祭", "age": "16", "hobby": "斯哈斯哈"} // 但是我们不直接从 proto 文件中指定的路径去找,不是说找不到,只是说我们实例化不方便 // 我们可以通过 timestamppb.New 来实例化,这样会方便很多 res.T = timestamppb.New(time.Now()) return res, nil } func main() { server := grpc.NewServer() yoyoyo.RegisterHelloServer(server, &Server{}) listener, err := net.Listen("tcp", ":33333") if err != nil { fmt.Println(err) return } if err = server.Serve(listener); err != nil { fmt.Println(err) return } }然后客户端来调用,这里不使用 Go 的客户端,而是使用 Python 的客户端。如果 Python 能正常调用,那么 Go 一定能。
import grpc import hello_pb2_grpc as pb2_grpc import hello_pb2 as pb2 channel = grpc.insecure_channel("127.0.0.1:33333") client = pb2_grpc.HelloStub(channel=channel) response = client.SayHello( pb2.HelloRequest() ) print(response.info) # {\'name\': \'夏色祭\', \'age\': \'16\', \'hobby\': \'斯哈斯哈\'} # 我们可以通过 seconds 来获取对应的时间戳,然后转换成时间,Go 也是同理,只不过需要首字母大写 print(response.t.seconds)