首页 > 解决方案 > Hello World GRPC 的基本更改

问题描述

我正在gRPC使用Golang.

在成功运行他们HelloWorld tutorial需要我添加第二个函数、编译 protoc 文件后,我决定进行一些进一步的更改。

包含我的代码,这似乎不起作用,因为它希望pb变量是 HelloWorld 对象或其他东西,如下面命令行中的错误消息所示。有人可以告诉我哪里出了问题,因为应该是一些非常小的变化。

我在下面的原始帖子代码中修复了一些错别字。

命令行:

go run server/server.go
# command-line-arguments
server/server.go:24:71: undefined: helloworld.TheGRPCNotificationMessage

命令行协议文件:

协议 --go_out=。--go_opt=paths=source_relative --go-grpc_out=. --go-grpc_opt=paths=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
}

客户端.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...")
}

服务器.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)
    }
}

我希望示例教程没有让我如此粗略地粘住它!

标签: linuxgotcpgrpcgrpc-go

解决方案


您的go_packageandclient.goserver.go包导入不正确。这是 gRPC 的“繁琐”部分,但它的结果是因为协议必须支持许多(不同的)语言及其包解决方案。

您还重命名了服务和方法,因此不能使用,例如GreeterService,因为它不再存在。大多数编辑器(例如 VSCode)将帮助您浏览包以查找命名错误。

pb "google.golang.org/grpc/examples/helloworld/helloworld"
  1. 假设您的代码位于名为的 Go 模块中${REPO}(可能是某种形式github.com/YOUR-ORG/YOUR-REPO,即您:
go mod init ${REPO}
  1. 在 proto 文件中,对于 Golang(我将留给您解决其他语言),您需要:
option go_package = "${REPO}/helloworld";

注意用它的值替换${REPO},即github.com/....

  1. 我鼓励您与 proto 包名称grpcnotificationpackage和生成的代码包名称保持一致,但是目前,您的 proto 包是grpcnotificationpackage但您正在将代码生成到helloworld.

  2. 在 repo 中创建一个名为helloworld. 您要求protoc将 Go 源代码 ( go_package) 生成到一个名为的包${REPO}/helloworld中,而 Go 要求文件helloworld位于您的 repo 中调用的目录中。

  3. 然后:

protoc \
--go_out=${PWD}/helloworld \
--go_opt=paths=source_relative \
--go-grpc_out=${PWD}/helloworld \
--go-grpc_opt=paths=source_relative \
GRPCNotification.proto

此命令有多种风格,但这会将源代码生成到${PWD}/helloworld您的存储库中。

  1. 然后你必须正确导入:
import (
  ...
  ${REPO}/helloworld
)
  1. 然后你必须更正你的参考资料。如果你调用一个服务TheGRPCNotificationService和一个方法SendNotificationWithGRPC和一个消息TheGRPCNotificationMessage,这就是 protoc 将生成的(你可以查看生成的 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)
    }
}
  1. 我不推荐returns (google.protobuf.Empty)。我不知道这是否有最佳实践,但我认为最好返回不包含字段的消息,例如:
rpc Foo (FooRequest) returns (FooResponse);

message FooResponse {}
  1. 考虑使用gRPCurl 之类的工具进行调试:

在此示例中,gRPCurl使用 proto 而不是 service 来枚举服务(在 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

推荐阅读