wget https://github.com/protocolbuffers/protobuf/releases/download/v24.0-rc2/protoc-24.0-rc-2-linux-x86_64.zip
# 用于将 *.proto 文件生成一个后缀为 *.pb.go 的文件。生成文件中包含所有 .proto 文件中定义的类型及其序列化方法
go install google.golang.org/protobuf/cmd/protoc-gen-go@latest
# 用于生成 gRPC 服务端需要实现的接口
go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@latest
可以参考官方:https://github.com/grpc/grpc-go/blob/master/examples/route_guide/routeguide/route_guide.proto
客户端发送请求到服务端,服务端马上响应、返回,如同普通的函数调用。
rpc GetFeature(Point) returns (Feature) {}
客户端向服务端发送请求,服务端发送流到客户端,客户端从流中读取数据,直到服务端关闭该流。
rpc ListFeatures(Rectangle) returns (stream Feature) {}
客户端向服务端发送流式请求。端向流中多次写入数据,服务端从流中循环获取数据,直到客户端关闭该流。服务端接收完所有数据后,向客户端返回一个普通响应。
rpc RecordRoute(stream Point) returns (RouteSummary) {}
rpc RouteChat(stream RouteNote) returns (stream RouteNote) {}
完整的proto文件如下:
// Copyright 2015 gRPC authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
syntax = "proto3";
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 helloworld;
// The greeting service definition.
service Greeter {
// Sends a greeting
rpc SayHello (HelloRequest) returns (HelloReply) {}
rpc SayHelloAgain (HelloRequest) returns (HelloReply) {}
rpc ServerStreamingSayHello(HelloRequest) returns (stream HelloReply) {}
rpc ClientStreamingSayHello(stream HelloRequest) returns (HelloReply) {}
rpc BidirectionalStreamingSayHello(stream HelloRequest) returns (stream HelloReply) {}
}
// The request message containing the user's name.
message HelloRequest {
string name = 1;
}
// The response message containing the greetings
message HelloReply {
string message = 1;
}
异步模式使用场景:当一个任务的处理时间比较长时,不能一直占用工作线程,需要异步启动另一个线程去做,完成之后通知回server的工作线程处理,总之,就是不能阻塞工作线程。
grpc异步模式使用【ServerCompletionQueue】队列承载客户端的请求事件(事件类型需要提前注册),当客户端发来请求,从队列中取出请求,并对其进行处理。
总流程:
注册事件---等待请求---获取请求---入队列---出队获取任务---处理---返回---完成状态入队列---清理本次处理申请的资源。
比较重要的四个数据结构:
请求队列(客户端请求到达后,首先入此队列):ServerCompletionQueue
请求的数据结构:HelloRequest
返回的数据结构:HelloReply
异步返回writer:ServerAsyncResponseWriter
注册sayhello函数,client端调用sayhello时,服务端可以做出响应。
Greeter::AsyncService* service_;
service_->RequestSayHello(&ctx_, &request_, &responder_, cq_, cq_,this);
grpc异步模式,请求事件存储在ServerCompletionQueue队列中,当请求到达时入队列,通过以下api从队列中获取请求:
std::unique_ptr cq_;
cq_->Next(&tag, &ok)
其中tag即为上一步注册事件的【this】,为一个自定义的类对象,之后调用对应的处理函数。
服务端函数代码实现,可以参考helloworld_grpc.pb.go文件中GreeterServer接口中函数的定义:
type GreeterServer interface {
// Sends a greeting
SayHello(context.Context, *HelloRequest) (*HelloReply, error)
SayHelloAgain(context.Context, *HelloRequest) (*HelloReply, error)
ServerStreamingSayHello(*HelloRequest, Greeter_ServerStreamingSayHelloServer) error
ClientStreamingSayHello(Greeter_ClientStreamingSayHelloServer) error
BidirectionalStreamingSayHello(Greeter_BidirectionalStreamingSayHelloServer) error
mustEmbedUnimplementedGreeterServer()
}
对于客户端可以调用的函数,可以参考GreeterClient接口的函数定义:
type GreeterClient interface {
// Sends a greeting
SayHello(ctx context.Context, in *HelloRequest, opts ...grpc.CallOption) (*HelloReply, error)
SayHelloAgain(ctx context.Context, in *HelloRequest, opts ...grpc.CallOption) (*HelloReply, error)
ServerStreamingSayHello(ctx context.Context, in *HelloRequest, opts ...grpc.CallOption) (Greeter_ServerStreamingSayHelloClient, error)
ClientStreamingSayHello(ctx context.Context, opts ...grpc.CallOption) (Greeter_ClientStreamingSayHelloClient, error)
BidirectionalStreamingSayHello(ctx context.Context, opts ...grpc.CallOption) (Greeter_BidirectionalStreamingSayHelloClient, error)
}
// Copyright 2015 gRPC authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
syntax = "proto3";
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 helloworld;
// The greeting service definition.
service Greeter {
// Sends a greeting
rpc SayHello (HelloRequest) returns (HelloReply) {}
rpc SayHelloAgain (HelloRequest) returns (HelloReply) {}
}
// The request message containing the user's name.
message HelloRequest {
string name = 1;
}
// The response message containing the greetings
message HelloReply {
string message = 1;
}
生成grpc代码命令行,代码生成在当前路径下:
protoc --go_out=. --go-grpc_out=. helloworld.proto
命令运行后,会在当前路径下生成一个文件夹,路径:google.golang.org/grpc/examples/helloworld/helloworld,生成两个文件,分别为:
helloworld_grpc.pb.go
helloworld.pb.go
/*
*
* Copyright 2015 gRPC authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
// Package main implements a server for Greeter service.
package main
import (
"context"
"flag"
"fmt"
"log"
"net"
"google.golang.org/grpc"
pb "google.golang.org/grpc/examples/helloworld/helloworld"
)
var (
port = flag.Int("port", 50051, "The server port")
)
// server is used to implement helloworld.GreeterServer.
type server struct {
pb.UnimplementedGreeterServer
}
// SayHello implements helloworld.GreeterServer
func (s *server) SayHello(ctx context.Context, in *pb.HelloRequest) (*pb.HelloReply, error) {
log.Printf("Received: %v", in.GetName())
return &pb.HelloReply{Message: "Hello " + in.GetName()}, nil
}
// SayHelloAgain implements helloworld.GreeterServer
func (s *server) SayHelloAgain(ctx context.Context, in *pb.HelloRequest) (*pb.HelloReply, error) {
log.Printf("Received: %v", in.GetName())
return &pb.HelloReply{Message: "Hello again " + in.GetName()}, nil
}
func main() {
flag.Parse()
lis, err := net.Listen("tcp", fmt.Sprintf(":%d", *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)
}
}
/*
*
* Copyright 2015 gRPC authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
// Package main implements a client for Greeter service.
package main
import (
"context"
"flag"
"log"
"time"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials/insecure"
pb "google.golang.org/grpc/examples/helloworld/helloworld"
)
const (
defaultName = "world"
)
var (
addr = flag.String("addr", "localhost:50051", "the address to connect to")
name = flag.String("name", defaultName, "Name to greet")
)
func main() {
flag.Parse()
// Set up a connection to the server.
conn, err := grpc.Dial(*addr, grpc.WithTransportCredentials(insecure.NewCredentials()))
if err != nil {
log.Fatalf("did not connect: %v", err)
}
defer conn.Close()
c := pb.NewGreeterClient(conn)
// Contact the server and print out its response.
ctx, cancel := context.WithTimeout(context.Background(), time.Second)
defer cancel()
r, err := c.SayHello(ctx, &pb.HelloRequest{Name: *name})
if err != nil {
log.Fatalf("could not greet: %v", err)
}
log.Printf("Greeting: %s", r.GetMessage())
r, err = c.SayHelloAgain(ctx, &pb.HelloRequest{Name: *name})
if err != nil {
log.Fatalf("could not greet: %v", err)
}
log.Printf("Greeting again: %s", r.GetMessage())
}
1、启动server
2、启动client
在server Greeter中增加三个函数定义:
rpc ServerStreamingSayHello(HelloRequest) returns (stream HelloReply) {}
rpc ClientStreamingSayHello(stream HelloRequest) returns (HelloReply) {}
rpc BidirectionalStreamingSayHello(stream HelloRequest) returns (stream HelloReply) {}
// Copyright 2015 gRPC authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
syntax = "proto3";
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 helloworld;
// The greeting service definition.
service Greeter {
// Sends a greeting
rpc SayHello (HelloRequest) returns (HelloReply) {}
rpc SayHelloAgain (HelloRequest) returns (HelloReply) {}
rpc ServerStreamingSayHello(HelloRequest) returns (stream HelloReply) {}
rpc ClientStreamingSayHello(stream HelloRequest) returns (HelloReply) {}
rpc BidirectionalStreamingSayHello(stream HelloRequest) returns (stream HelloReply) {}
}
// The request message containing the user's name.
message HelloRequest {
string name = 1;
}
// The response message containing the greetings
message HelloReply {
string message = 1;
}
重新生成proto代码:
protoc --go_out=. --go-grpc_out=. helloworld.proto
实现该函数(服务端):
func (s *server) ServerStreamingSayHello(in *pb.HelloRequest, stream pb.Greeter_ServerStreamingSayHelloServer) error {
log.Printf("Received: %v", in.GetName())
for i := 0; i < 10; i++ {
fmt.Printf("echo message %v\n", in.GetName())
err := stream.Send(&pb.HelloReply{Message: "hello " + in.GetName()})
if err != nil {
return err
}
}
return nil
}
全部代码如下:
/*
*
* Copyright 2015 gRPC authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
// Package main implements a server for Greeter service.
package main
import (
"context"
"flag"
"fmt"
"log"
"net"
"google.golang.org/grpc"
pb "google.golang.org/grpc/examples/helloworld/helloworld"
)
var (
port = flag.Int("port", 50051, "The server port")
)
// server is used to implement helloworld.GreeterServer.
type server struct {
pb.UnimplementedGreeterServer
}
// SayHello implements helloworld.GreeterServer
func (s *server) SayHello(ctx context.Context, in *pb.HelloRequest) (*pb.HelloReply, error) {
log.Printf("Received: %v", in.GetName())
return &pb.HelloReply{Message: "Hello " + in.GetName()}, nil
}
// SayHelloAgain implements helloworld.GreeterServer
func (s *server) SayHelloAgain(ctx context.Context, in *pb.HelloRequest) (*pb.HelloReply, error) {
log.Printf("Received: %v", in.GetName())
return &pb.HelloReply{Message: "Hello again " + in.GetName()}, nil
}
func (s *server) ServerStreamingSayHello(in *pb.HelloRequest, stream pb.Greeter_ServerStreamingSayHelloServer) error {
log.Printf("Received: %v", in.GetName())
for i := 0; i < 10; i++ {
fmt.Printf("echo message %v\n", in.GetName())
err := stream.Send(&pb.HelloReply{Message: "hello " + in.GetName()})
if err != nil {
return err
}
}
return nil
}
func main() {
flag.Parse()
lis, err := net.Listen("tcp", fmt.Sprintf(":%d", *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)
}
}
客户端实现:
stream, err := c.ServerStreamingSayHello(context.Background(), &pb.HelloRequest{Name: "csdn csdn"})
if err != nil {
log.Fatalf("could not greet: %v", err)
}
for {
r, err := stream.Recv()
if err != nil {
break
}
fmt.Printf(" - %s\n", r.Message)
}
全部代码:
/*
*
* Copyright 2015 gRPC authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
// Package main implements a client for Greeter service.
package main
import (
"context"
"flag"
"fmt"
"log"
"time"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials/insecure"
pb "google.golang.org/grpc/examples/helloworld/helloworld"
)
const (
defaultName = "world"
)
var (
addr = flag.String("addr", "localhost:50051", "the address to connect to")
name = flag.String("name", defaultName, "Name to greet")
)
func main() {
flag.Parse()
// Set up a connection to the server.
conn, err := grpc.Dial(*addr, grpc.WithTransportCredentials(insecure.NewCredentials()))
if err != nil {
log.Fatalf("did not connect: %v", err)
}
defer conn.Close()
c := pb.NewGreeterClient(conn)
// Contact the server and print out its response.
ctx, cancel := context.WithTimeout(context.Background(), time.Second)
defer cancel()
r, err := c.SayHello(ctx, &pb.HelloRequest{Name: *name})
if err != nil {
log.Fatalf("could not greet: %v", err)
}
log.Printf("Greeting: %s", r.GetMessage())
r, err = c.SayHelloAgain(ctx, &pb.HelloRequest{Name: *name})
if err != nil {
log.Fatalf("could not greet: %v", err)
}
log.Printf("Greeting again: %s", r.GetMessage())
stream, err := c.ServerStreamingSayHello(context.Background(), &pb.HelloRequest{Name: "csdn csdn"})
if err != nil {
log.Fatalf("could not greet: %v", err)
}
for {
r, err := stream.Recv()
if err != nil {
break
}
fmt.Printf(" - %s\n", r.Message)
}
}
运行结果:
服务端代码:
func (s *server) ClientStreamingSayHello(stream pb.Greeter_ClientStreamingSayHelloServer) error {
var message string
for {
in, err := stream.Recv()
if err == io.EOF {
fmt.Printf("echo last received message\n")
return stream.SendAndClose(&pb.HelloReply{Message: "done"})
}
message = in.GetName()
fmt.Printf("client stream request received: %v\n", message)
if err != nil {
return err
}
}
}
客户端代码:
// 客户端流式
sstream, err := c.ClientStreamingSayHello(context.Background())
if err != nil {
log.Fatalf("could not greet: %v", err)
}
for i := 0; i < 10; i++ {
sstream.Send(&pb.HelloRequest{Name: "client stream csdn"})
}
r, err = sstream.CloseAndRecv()
if err != nil {
log.Fatalf("failed to CloseAndRecv: %v\n", err)
}
fmt.Printf("client stream response:\n")
fmt.Printf(" - %s\n\n", r.Message)
运行结果:
服务端代码:
func (s *server) BidirectionalStreamingSayHello(stream pb.Greeter_BidirectionalStreamingSayHelloServer) error {
for {
in, err := stream.Recv()
if err == io.EOF {
return nil
}
if err != nil {
return err
}
fmt.Printf("bidirectional request received %v\n", in)
if err := stream.Send(&pb.HelloReply{Message: "bidirectional done"}); err != nil {
return err
}
}
}
客户端代码:
// 双端流式
bstream, err := c.BidirectionalStreamingSayHello(context.Background())
if err != nil {
log.Fatalf("failed to CloseAndRecv: %v\n", err)
}
go func() {
// 异步发送信息
for i := 0; i < 10; i++ {
bstream.Send(&pb.HelloRequest{Name: "bidirection stream client"})
}
bstream.CloseSend()
}()
fmt.Printf("response:\n")
for {
r, err := bstream.Recv()
if err != nil {
break
}
fmt.Printf(" - %s\n", r.Message)
}
运行记录:
全部代码(最终版):
服务端:
/*
*
* Copyright 2015 gRPC authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
// Package main implements a server for Greeter service.
package main
import (
"context"
"flag"
"fmt"
"io"
"log"
"net"
"google.golang.org/grpc"
pb "google.golang.org/grpc/examples/helloworld/helloworld"
)
var (
port = flag.Int("port", 50051, "The server port")
)
// server is used to implement helloworld.GreeterServer.
type server struct {
pb.UnimplementedGreeterServer
}
// SayHello implements helloworld.GreeterServer
func (s *server) SayHello(ctx context.Context, in *pb.HelloRequest) (*pb.HelloReply, error) {
log.Printf("Received: %v", in.GetName())
return &pb.HelloReply{Message: "Hello " + in.GetName()}, nil
}
// SayHelloAgain implements helloworld.GreeterServer
func (s *server) SayHelloAgain(ctx context.Context, in *pb.HelloRequest) (*pb.HelloReply, error) {
log.Printf("Received: %v", in.GetName())
return &pb.HelloReply{Message: "Hello again " + in.GetName()}, nil
}
func (s *server) ServerStreamingSayHello(in *pb.HelloRequest, stream pb.Greeter_ServerStreamingSayHelloServer) error {
log.Printf("Received: %v", in.GetName())
for i := 0; i < 10; i++ {
fmt.Printf("echo message %v\n", in.GetName())
err := stream.Send(&pb.HelloReply{Message: "hello " + in.GetName()})
if err != nil {
return err
}
}
return nil
}
func (s *server) ClientStreamingSayHello(stream pb.Greeter_ClientStreamingSayHelloServer) error {
var message string
for {
in, err := stream.Recv()
if err == io.EOF {
fmt.Printf("echo last received message\n")
return stream.SendAndClose(&pb.HelloReply{Message: "done"})
}
message = in.GetName()
fmt.Printf("client stream request received: %v\n", message)
if err != nil {
return err
}
}
}
func (s *server) BidirectionalStreamingSayHello(stream pb.Greeter_BidirectionalStreamingSayHelloServer) error {
for {
in, err := stream.Recv()
if err == io.EOF {
return nil
}
if err != nil {
return err
}
fmt.Printf("bidirectional request received %v\n", in)
if err := stream.Send(&pb.HelloReply{Message: "bidirectional done"}); err != nil {
return err
}
}
}
func main() {
flag.Parse()
lis, err := net.Listen("tcp", fmt.Sprintf(":%d", *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)
}
}
客户端:
/*
*
* Copyright 2015 gRPC authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
// Package main implements a client for Greeter service.
package main
import (
"context"
"flag"
"fmt"
"log"
"time"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials/insecure"
pb "google.golang.org/grpc/examples/helloworld/helloworld"
)
const (
defaultName = "world"
)
var (
addr = flag.String("addr", "localhost:50051", "the address to connect to")
name = flag.String("name", defaultName, "Name to greet")
)
func main() {
flag.Parse()
// Set up a connection to the server.
conn, err := grpc.Dial(*addr, grpc.WithTransportCredentials(insecure.NewCredentials()))
if err != nil {
log.Fatalf("did not connect: %v", err)
}
defer conn.Close()
c := pb.NewGreeterClient(conn)
// Contact the server and print out its response.
ctx, cancel := context.WithTimeout(context.Background(), time.Second)
defer cancel()
r, err := c.SayHello(ctx, &pb.HelloRequest{Name: *name})
if err != nil {
log.Fatalf("could not greet: %v", err)
}
log.Printf("Greeting: %s", r.GetMessage())
r, err = c.SayHelloAgain(ctx, &pb.HelloRequest{Name: *name})
if err != nil {
log.Fatalf("could not greet: %v", err)
}
log.Printf("Greeting again: %s", r.GetMessage())
// 服务端流式
stream, err := c.ServerStreamingSayHello(context.Background(), &pb.HelloRequest{Name: "server stream csdn"})
if err != nil {
log.Fatalf("could not greet: %v", err)
}
for {
r, err := stream.Recv()
if err != nil {
break
}
fmt.Printf(" - %s\n", r.Message)
}
// 客户端流式
sstream, err := c.ClientStreamingSayHello(context.Background())
if err != nil {
log.Fatalf("could not greet: %v", err)
}
for i := 0; i < 10; i++ {
sstream.Send(&pb.HelloRequest{Name: "client stream"})
}
r, err = sstream.CloseAndRecv()
if err != nil {
log.Fatalf("failed to CloseAndRecv: %v\n", err)
}
fmt.Printf("client stream response:\n")
fmt.Printf(" - %s\n\n", r.Message)
// 双端流式
bstream, err := c.BidirectionalStreamingSayHello(context.Background())
if err != nil {
log.Fatalf("failed to CloseAndRecv: %v\n", err)
}
go func() {
// 异步发送信息
for i := 0; i < 10; i++ {
bstream.Send(&pb.HelloRequest{Name: "bidirection stream client"})
}
bstream.CloseSend()
}()
fmt.Printf("response:\n")
for {
r, err := bstream.Recv()
if err != nil {
break
}
fmt.Printf(" - %s\n", r.Message)
}
}
服务端:
注册rpc函数,注册完成后,客户端即可调用调用SayHelloAgain函数。
Greeter::AsyncService* service_;
service_->RequestSayHelloAgain(&ctx_, &request_, &responder_, cq_, cq_,
this);
server端全部代码:
/*
*
* Copyright 2015 gRPC authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
#include
#include
#include
#include
#include "absl/flags/flag.h"
#include "absl/flags/parse.h"
#include "absl/strings/str_format.h"
#include
#include
#ifdef BAZEL_BUILD
#include "examples/protos/helloworld.grpc.pb.h"
#else
#include "helloworld.grpc.pb.h"
#endif
ABSL_FLAG(uint16_t, port, 50051, "Server port for the service");
using grpc::Server;
using grpc::ServerAsyncResponseWriter;
using grpc::ServerBuilder;
using grpc::ServerCompletionQueue;
using grpc::ServerContext;
using grpc::Status;
using helloworld::Greeter;
using helloworld::HelloReply;
using helloworld::HelloRequest;
class ServerImpl final {
public:
~ServerImpl() {
server_->Shutdown();
// Always shutdown the completion queue after the server.
cq_->Shutdown();
}
// There is no shutdown handling in this code.
void Run(uint16_t port) {
std::string server_address = absl::StrFormat("0.0.0.0:%d", port);
ServerBuilder builder;
// Listen on the given address without any authentication mechanism.
builder.AddListeningPort(server_address, grpc::InsecureServerCredentials());
// Register "service_" as the instance through which we'll communicate with
// clients. In this case it corresponds to an *asynchronous* service.
builder.RegisterService(&service_);
// Get hold of the completion queue used for the asynchronous communication
// with the gRPC runtime.
cq_ = builder.AddCompletionQueue();
// Finally assemble the server.
server_ = builder.BuildAndStart();
std::cout << "Server listening on " << server_address << std::endl;
// Proceed to the server's main loop.
HandleRpcs();
}
private:
// Class encompasing the state and logic needed to serve a request.
class CallData {
public:
// Take in the "service" instance (in this case representing an asynchronous
// server) and the completion queue "cq" used for asynchronous communication
// with the gRPC runtime.
CallData(Greeter::AsyncService* service, ServerCompletionQueue* cq)
: service_(service), cq_(cq), responder_(&ctx_), status_(CREATE) {
// Invoke the serving logic right away.
std::cout << "++++++++call data construct+++++++" << std::endl;
Proceed();
}
void Proceed() {
std::cout << "proceed()" << std::endl;
if (status_ == CREATE) {
// Make this instance progress to the PROCESS state.
status_ = PROCESS;
// As part of the initial CREATE state, we *request* that the system
// start processing SayHello requests. In this request, "this" acts are
// the tag uniquely identifying the request (so that different CallData
// instances can serve different requests concurrently), in this case
// the memory address of this CallData instance.
// service_->RequestSayHello(&ctx_, &request_, &responder_, cq_, cq_,
// this);
service_->RequestSayHelloAgain(&ctx_, &request_, &responder_, cq_, cq_,
this);
std::cout << "proceed()====status_create" << std::endl;
} else if (status_ == PROCESS) {
// Spawn a new CallData instance to serve new clients while we process
// the one for this CallData. The instance will deallocate itself as
// part of its FINISH state.
new CallData(service_, cq_);
std::cout << "after new call data" << std::endl;
// The actual processing.
std::string prefix("Hello ");
reply_.set_message(prefix + request_.name());
// And we are done! Let the gRPC runtime know we've finished, using the
// memory address of this instance as the uniquely identifying tag for
// the event.
status_ = FINISH;
std::this_thread::sleep_for(
std::chrono::milliseconds(5000)); // 睡眠1000毫秒(1秒)
std::cout << "proceed()====status_done" << std::endl;
// 调用responder.Finish发送结果,如果发送成功,也会以tag为标识加入队列中
responder_.Finish(reply_, Status::OK, this);
} else {
GPR_ASSERT(status_ == FINISH);
// Once in the FINISH state, deallocate ourselves (CallData).
std::cout << "rpc finish. status = " << status_ << std::endl;
delete this;
}
}
private:
// The means of communication with the gRPC runtime for an asynchronous
// server.
Greeter::AsyncService* service_;
// The producer-consumer queue where for asynchronous server notifications.
ServerCompletionQueue* cq_;
// Context for the rpc, allowing to tweak aspects of it such as the use
// of compression, authentication, as well as to send metadata back to the
// client.
ServerContext ctx_;
// What we get from the client.
HelloRequest request_;
// What we send back to the client.
HelloReply reply_;
// The means to get back to the client.
ServerAsyncResponseWriter responder_;
// Let's implement a tiny state machine with the following states.
enum CallStatus { CREATE, PROCESS, FINISH };
CallStatus status_; // The current serving state.
};
// This can be run in multiple threads if needed.
void HandleRpcs() {
// Spawn a new CallData instance to serve new clients.
new CallData(&service_, cq_.get());
void* tag; // uniquely identifies a request.
bool ok;
while (true) {
// Block waiting to read the next event from the completion queue. The
// event is uniquely identified by its tag, which in this case is the
// memory address of a CallData instance.
// The return value of Next should always be checked. This return value
// tells us whether there is any kind of event or cq_ is shutting down.
std::cout << "enter loop" << std::endl;
GPR_ASSERT(cq_->Next(&tag, &ok));
GPR_ASSERT(ok);
std::cout << "recive a envent" << std::endl;
static_cast(tag)->Proceed();
}
}
std::unique_ptr cq_;
Greeter::AsyncService service_;
std::unique_ptr server_;
};
int main(int argc, char** argv) {
absl::ParseCommandLine(argc, argv);
ServerImpl server;
server.Run(absl::GetFlag(FLAGS_port));
return 0;
}
client端代码:
/*
*
* Copyright 2015 gRPC authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
#include
#include
#include
#include "absl/flags/flag.h"
#include "absl/flags/parse.h"
#include
#include
#ifdef BAZEL_BUILD
#include "examples/protos/helloworld.grpc.pb.h"
#else
#include "helloworld.grpc.pb.h"
#endif
ABSL_FLAG(std::string, target, "localhost:50051", "Server address");
using grpc::Channel;
using grpc::ClientAsyncResponseReader;
using grpc::ClientContext;
using grpc::CompletionQueue;
using grpc::Status;
using helloworld::Greeter;
using helloworld::HelloReply;
using helloworld::HelloRequest;
class GreeterClient {
public:
explicit GreeterClient(std::shared_ptr channel)
: stub_(Greeter::NewStub(channel)) {}
// Assembles the client's payload, sends it and presents the response back
// from the server.
std::string SayHello(const std::string& user) {
// Data we are sending to the server.
HelloRequest request;
request.set_name(user);
// Container for the data we expect from the server.
HelloReply reply;
// Context for the client. It could be used to convey extra information to
// the server and/or tweak certain RPC behaviors.
ClientContext context;
// The producer-consumer queue we use to communicate asynchronously with the
// gRPC runtime.
CompletionQueue cq;
// Storage for the status of the RPC upon completion.
Status status;
std::unique_ptr > rpc(
stub_->AsyncSayHelloAgain(&context, request, &cq));
// Request that, upon completion of the RPC, "reply" be updated with the
// server's response; "status" with the indication of whether the operation
// was successful. Tag the request with the integer 1.
rpc->Finish(&reply, &status, (void*)1);
void* got_tag;
bool ok = false;
std::cout << "reply liupeng:" << reply.message() << std::endl;
// Block until the next result is available in the completion queue "cq".
// The return value of Next should always be checked. This return value
// tells us whether there is any kind of event or the cq_ is shutting down.
GPR_ASSERT(cq.Next(&got_tag, &ok));
std::cout << "reply liupeng-1:" << reply.message() << std::endl;
// Verify that the result from "cq" corresponds, by its tag, our previous
// request.
GPR_ASSERT(got_tag == (void*)1);
// ... and that the request was completed successfully. Note that "ok"
// corresponds solely to the request for updates introduced by Finish().
GPR_ASSERT(ok);
// Act upon the status of the actual RPC.
if (status.ok()) {
return reply.message();
} else {
return "RPC failed";
}
}
private:
// Out of the passed in Channel comes the stub, stored here, our view of the
// server's exposed services.
std::unique_ptr stub_;
};
int main(int argc, char** argv) {
absl::ParseCommandLine(argc, argv);
// Instantiate the client. It requires a channel, out of which the actual RPCs
// are created. This channel models a connection to an endpoint specified by
// the argument "--target=" which is the only expected argument.
std::string target_str = absl::GetFlag(FLAGS_target);
// We indicate that the channel isn't authenticated (use of
// InsecureChannelCredentials()).
GreeterClient greeter(
grpc::CreateChannel(target_str, grpc::InsecureChannelCredentials()));
std::string user("world");
std::string reply = greeter.SayHello(user); // The actual RPC call!
std::cout << "Greeter received: " << reply << std::endl;
return 0;
}