我使用gRPC
和Golang
。
在成功运行他们的HelloWorld tutorial
后,这需要我添加第二个功能,编译协议文件,我决定做一些进一步的改变。
包含了我的代码,它似乎不起作用,因为它期望pb
变量是HelloWorld对象或其他东西,如命令行中的错误消息所示在下面。谁能告诉我,我哪里做错了,应该是一些非常小的变化。
我已经修复了我原来的帖子代码中的一些错别字。
命令行:
go run server/server.go
# command-line-arguments
server/server.go:24:71: undefined: helloworld.TheGRPCNotificationMessage
命令行协议文件:
protoc——go_out =。——go_opt = source_relative——go-grpc_out = =路径。——go-grpc_opt =路径= source_relative GRPCNotification/GRPCNotification.proto
GRPCNotification.proto
syntax = "proto3";
import "google/protobuf/empty.proto";
option go_package = "google.golang.org/grpc/examples/helloworld/helloworld";
option java_multiple_files = true;
option java_package = "io.grpc.examples.helloworld";
option java_outer_classname = "HelloWorldProto";
package grpcnotificationpackage;
// The greeting service definition.
service TheGRPCNotificationService {
// Sends a notification and nothing sent back
rpc SendNotificationWithGRPC (TheGRPCNotificationMessage) returns (google.protobuf.Empty);
// Receives a notification and does not reply
rpc ReceiveNotificationWithGRPC (stream TheGRPCNotificationMessage) returns (google.protobuf.Empty);
}
// The request notification message
message TheGRPCNotificationMessage {
string message = 1;
// ... add more here
}
// Since nothing gets returned from server we need this empty
message Empty {
// Nothing goes here
}
Client.go
// Package main implements a client for Greeter service.
package main
import (
"context"
"log"
"time"
"google.golang.org/grpc"
pb "google.golang.org/grpc/examples/helloworld/helloworld"
)
const (
address = "localhost:50051"
defaultName = "world"
)
func main() {
// Set up a connection to the server.
conn, err := grpc.Dial(address, grpc.WithInsecure(), grpc.WithBlock())
if err != nil {
log.Fatalf("did not connect: %v", err)
}
defer conn.Close()
c := pb.NewGreeterClient(conn)
ctx, cancel := context.WithTimeout(context.Background(), time.Second)
defer cancel()
r, err := c.SendNotificationWithGRPC(ctx, &pb.TheGRPCNotificationMessage{Message: "Horse"})
if err != nil {
log.Fatalf("could not greet: %v", err)
}
log.Printf("Sent a message, please check it reached server...")
}
Server.go
package main
import (
"context"
"log"
"net"
"google.golang.org/grpc"
pb "google.golang.org/grpc/examples/helloworld/helloworld"
)
const (
port = ":50051"
)
// server is used to implement helloworld.GreeterServer
type server struct {
pb.UnimplementedGreeterServer
}
// SendNotificationWithGRPC implements helloworld.GreeterServer <--- Problem?
func (s *server) ReceiveNotificationWithGRPC(ctx context.Context, in *pb.TheGRPCNotificationMessage) {
log.Printf("Received: %v", in.Name)
}
func main() {
lis, err := net.Listen("tcp", port)
if err != nil {
log.Fatalf("failed to listen: %v", err)
}
s := grpc.NewServer()
pb.RegisterGreeterServer(s, &server{})
log.Printf("server listening at %v", lis.Addr())
if err := s.Serve(lis); err != nil {
log.Fatalf("failed to serve: %v", err)
}
}
我希望示例教程没有把我这么粗略地粘在上面!
您的go_package
、client.go
和server.go
包导入不正确。这是一个"棘手的"问题。gRPC的一部分,但它的结果是因为协议必须支持许多(不同的)语言和他们的包解决方案。
你也重命名了服务和方法,所以不能使用例如GreeterService
,因为它不再存在。大多数编辑器(例如VSCode)将帮助您导航包以查找命名错误。
pb "google.golang.org/grpc/examples/helloworld/helloworld"
- 假设你的代码是在一个叫做
${REPO}
的Go模块(可能是github.com/YOUR-ORG/YOUR-REPO
的形式,即你:
go mod init ${REPO}
- 在原型文件中,对于Golang(我将把其他语言留给您解决),您需要:
option go_package = "${REPO}/helloworld";
注意将
${REPO}
替换为github.com/....
我会鼓励你与原型包名称name
grpcnotificationpackage
和你生成的代码包名称保持一致,但是,现在,你的原型包是grpcnotificationpackage
,但你正在生成的代码为helloworld
。在repo中创建名为
helloworld
的目录。你要求protoc
生成Go源代码(go_package
)到一个名为${REPO}/helloworld
的包中,Go要求这些文件位于repo中名为helloworld
的目录中。:
protoc
--go_out=${PWD}/helloworld
--go_opt=paths=source_relative
--go-grpc_out=${PWD}/helloworld
--go-grpc_opt=paths=source_relative
GRPCNotification.proto
这个命令有各种各样的用法,但是这个命令将在你的repo中生成${PWD}/helloworld
的源代码。
- 必须正确导入:
import (
...
${REPO}/helloworld
)
- 那么你必须更正你的参考文献。如果您调用服务
TheGRPCNotificationService
、方法SendNotificationWithGRPC
和消息TheGRPCNotificationMessage
,这就是协议将生成的内容(您可以查看生成的Go源代码来确认这一点)。所以:
package main
import (
"log"
"net"
pb "${REPO}/helloworld"
"google.golang.org/grpc"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
)
const (
port = ":50051"
)
// Ensure that `server` implements the interface
var _ pb.TheGRPCNotificationServiceServer = &server{}
// server is used to implement helloworld.GreeterServer
type server struct {
pb.UnimplementedTheGRPCNotificationServiceServer
}
// This is the correct signature of the method
func (s *server) ReceiveNotificationWithGRPC(
stream pb.TheGRPCNotificationService_ReceiveNotificationWithGRPCServer,
) error {
// Implement
return status.Errorf(codes.Unimplemented, "not implemented")
}
func main() {
lis, err := net.Listen("tcp", port)
if err != nil {
log.Fatalf("failed to listen: %v", err)
}
s := grpc.NewServer()
pb.RegisterTheGRPCNotificationServiceServer(s, &server{})
log.Printf("server listening at %v", lis.Addr())
if err := s.Serve(lis); err != nil {
log.Fatalf("failed to serve: %v", err)
}
}
- 我不推荐
returns (google.protobuf.Empty)
。我不知道是否有最佳实践,但我认为最好返回一个不包含字段的消息,例如:
rpc Foo (FooRequest) returns (FooResponse);
message FooResponse {}
- 考虑使用gRPCurl等工具进行调试:
在本例中,gRPCurl
使用proto而不是服务来枚举服务(在proto中定义):
grpcurl
-plaintext
--proto *.proto
localhost:50051 list
收益率:
grpcnotificationpackage.TheGRPCNotificationService
相同,但适用于给定服务的方法:
grpcurl
-plaintext
--proto *.proto
localhost:50051 list grpcnotificationpackage.TheGRPCNotificationService
产生以下方法:
grpcnotificationpackage.TheGRPCNotificationService.ReceiveNotificationWithGRPC
grpcnotificationpackage.TheGRPCNotificationService.SendNotificationWithGRPC
调用服务器流方法:
grpcurl
-plaintext
--proto *.proto
localhost:50051 grpcnotificationpackage.TheGRPCNotificationService.ReceiveNotificationWithGRPC
产生(正确地说是一个未实现的错误)
ERROR:
Code: Unimplemented
Message: not implemented