grpc使用总结(golang)

1、环境安装

1.1 下载protoc程序,解压后,将protoc放入gobin目录中。 

wget https://github.com/protocolbuffers/protobuf/releases/download/v24.0-rc2/protoc-24.0-rc-2-linux-x86_64.zip

1.2 安装插件

# 用于将 *.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

2、grpc同步模式

可以参考官方:https://github.com/grpc/grpc-go/blob/master/examples/route_guide/routeguide/route_guide.proto

2.1 普通模式(simple RPC)

客户端发送请求到服务端,服务端马上响应、返回,如同普通的函数调用。

rpc GetFeature(Point) returns (Feature) {}

2.2 服务端流式(server-to-client streaming RPC)

客户端向服务端发送请求,服务端发送流到客户端,客户端从流中读取数据,直到服务端关闭该流。

rpc ListFeatures(Rectangle) returns (stream Feature) {}

2.3 客户端流式(client-to-server streaming RPC)

客户端向服务端发送流式请求。端向流中多次写入数据,服务端从流中循环获取数据,直到客户端关闭该流。服务端接收完所有数据后,向客户端返回一个普通响应。

rpc RecordRoute(stream Point) returns (RouteSummary) {}

2.4 双端流式(Bidirectional streaming RPC)

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

3、异步模式(c++,go好像没有,helloworld为例)

异步模式使用场景:当一个任务的处理时间比较长时,不能一直占用工作线程,需要异步启动另一个线程去做,完成之后通知回server的工作线程处理,总之,就是不能阻塞工作线程。

grpc异步模式使用【ServerCompletionQueue】队列承载客户端的请求事件(事件类型需要提前注册),当客户端发来请求,从队列中取出请求,并对其进行处理。

总流程:

注册事件---等待请求---获取请求---入队列---出队获取任务---处理---返回---完成状态入队列---清理本次处理申请的资源。

比较重要的四个数据结构:

请求队列(客户端请求到达后,首先入此队列):ServerCompletionQueue

请求的数据结构:HelloRequest 

返回的数据结构:HelloReply

异步返回writer:ServerAsyncResponseWriter

3.1 注册处理事件

注册sayhello函数,client端调用sayhello时,服务端可以做出响应。

Greeter::AsyncService* service_;
service_->RequestSayHello(&ctx_, &request_, &responder_, cq_, cq_,this);

3.2 从队列获取请求事件(循环等待)

grpc异步模式,请求事件存储在ServerCompletionQueue队列中,当请求到达时入队列,通过以下api从队列中获取请求:

std::unique_ptr cq_;
cq_->Next(&tag, &ok)

其中tag即为上一步注册事件的【this】,为一个自定义的类对象,之后调用对应的处理函数。

4、代码示例

服务端函数代码实现,可以参考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)
}

4.1 simple rpc

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

// 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

4.1.2 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.
 *
 */

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

4.1.3 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.
 *
 */

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

4.1.4 运行示例

1、启动server

grpc使用总结(golang)_第1张图片

2、启动client

grpc使用总结(golang)_第2张图片server端打印:

grpc使用总结(golang)_第3张图片 4.2 流式(端流式+服务端流式+双端流式)

4.2.1 proto文件

在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

4.2.2 服务端流式

实现该函数(服务端):

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

}

运行结果:

 4.2.3 客户端流式

服务端代码:

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)

运行结果:

 4.2.4 双端流式

服务端代码:

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

4.3 异步模式(c++)

服务端:

注册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;
}

你可能感兴趣的:(golang,开发语言,后端)