对Hello World GRPC的基本更改

huangapple go评论78阅读模式
英文:

Basic changes to Hello World GRPC

问题

我正在使用GolanggRPC

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

我的代码包含在下面,但似乎不起作用,因为它期望pb变量是一个HelloWorld对象,或者其他什么东西,如下面命令行中的错误消息所示。请问有人可以告诉我我哪里出错了,因为应该只是一些很小的改动。

我已经在原始帖子的代码中修复了一些拼写错误。

命令行:

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

命令行protoc文件:

> protoc --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
}

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)
	}
}

我希望这个示例教程不要把我粘得这么紧!

英文:

I am using gRPC with Golang.

After successfully running their HelloWorld tutorial which required me to add a second function, compile the protoc file, I decided to make some further changes.

My code is included, which appears not to work because it expects the pb variable to be a HelloWorld object, or something, as shown in the error message in command line below. Could someone please tell me where I have gone wrong, for what should be some very small changes.

I have fixed some typos in my original post's code below.

Command line:

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

Command line protoc file:

> protoc --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
}

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)
	}
}

I wish the example tutorial did not glue me to it so roughly!

答案1

得分: 2

你的go_packageclient.goserver.go包导入是错误的。这是gRPC中一个比较棘手的部分,因为协议必须支持许多不同的语言和它们的包解决方案。

你还重命名了服务和方法,所以不能再使用GreeterService,因为它已经不存在了。大多数编辑器(如VSCode)会帮助你导航到包以查找命名错误。

pb "google.golang.org/grpc/examples/helloworld/helloworld"
  1. 假设你的代码在一个名为${REPO}的Go模块中(可能是形如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. 在仓库中创建一个名为helloworld的目录。你要求protoc将Go源代码(go_package)生成到名为${REPO}/helloworld的包中,而Go要求文件位于仓库中名为helloworld的目录中。

  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"
)

// 确保 `server` 实现了接口
var _ pb.TheGRPCNotificationServiceServer = &server{}

// server 用于实现 helloworld.GreeterServer
type server struct {
	pb.UnimplementedTheGRPCNotificationServiceServer
}

// 这是方法的正确签名
func (s *server) ReceiveNotificationWithGRPC(
    stream pb.TheGRPCNotificationService_ReceiveNotificationWithGRPCServer,
) error {
    // 实现方法
	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而不是服务来枚举服务(在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
英文:

Your go_package and client.go and server.go package imports are incorrect. This is a "fiddly" part of gRPC but it results because the protocol must support many (varied) languages and their package solutions.

You've also renamed the service and methods so can't use e.g. GreeterService as this no longer exists. Most editors (e.g. VSCode) will help you navigate packages to find naming errors.

pb "google.golang.org/grpc/examples/helloworld/helloworld"
  1. Assuming your code is in a Go Module called ${REPO} (and probably something of the form github.com/YOUR-ORG/YOUR-REPO, i.e. you:
go mod init ${REPO}
  1. In the proto file, for Golang (I'll leave the other languages for you to solve), you'll want:
option go_package = "${REPO}/helloworld";

> NOTE Replace ${REPO} with its value i.e. github.com/....

  1. I would encourage you to be consistent with proto package name name grpcnotificationpackage and your generated code package names but, for now, your proto package is grpcnotificationpackage but you're generating code into helloworld.

  2. Create a directory in the repo called helloworld. You're asking protoc to generate Go source (go_package) into a package called ${REPO}/helloworld and Go requires that the files be in a directory called helloworld in your repo.

  3. Then:

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

There are various flavors of this command but this will generate the sources into ${PWD}/helloworld in your repo.

  1. Then you must import correctly:
import (
  ...
  ${REPO}/helloworld
)
  1. Then you must correct your references. If you call a service TheGRPCNotificationService and a method SendNotificationWithGRPC and a message TheGRPCNotificationMessage, this is what protoc will generate (you can review the generated Go sources to confirm this). So:
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. I don't recommend returns (google.protobuf.Empty). I don't know whether there's a best practice for this but I think it's better to return a message that contains no fields, e.g.:
rpc Foo (FooRequest) returns (FooResponse);

message FooResponse {}
  1. Consider using a tool such as gRPCurl to debug:

In this example, gRPCurl uses the proto not the service to enumerate the services (defined in the proto):

grpcurl \
-plaintext \
--proto *.proto \
localhost:50051 list

Yields:

grpcnotificationpackage.TheGRPCNotificationService

Same-same but for the methods for the given service:

grpcurl \
-plaintext \
--proto *.proto \
localhost:50051 list grpcnotificationpackage.TheGRPCNotificationService

Yields the methods:

grpcnotificationpackage.TheGRPCNotificationService.ReceiveNotificationWithGRPC
grpcnotificationpackage.TheGRPCNotificationService.SendNotificationWithGRPC

Invoke the server streaming method:

grpcurl \
-plaintext \
--proto *.proto \
localhost:50051 grpcnotificationpackage.TheGRPCNotificationService.ReceiveNotificationWithGRPC

Yields (correctly an Unimplemented error)

ERROR:
Code: Unimplemented
Message: not implemented

huangapple
  • 本文由 发表于 2021年10月19日 23:11:56
  • 转载请务必保留本文链接:https://go.coder-hub.com/69633441.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定