GRPC

 ·  ☕ 3  · 👀...

介绍

gRPC是一个高性能、开源和通用的RPC框架,面向移动和HTTP/2设计。gRPC基于HTTP/2标准设计,带来诸如双向流、流控、头部压缩、单TCP链接上的多路复用请求等特性,这些特性使得其在移动设备上表现更好,更省电和节省空间占用。

环境搭建

环境准备

安装protobuf。

安装go语言grpc包

1
> go get -u google.golang.org/grpc

Go语言grpc环境测试

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
> git clone https://github.com/grpc/grpc-go.git        $GOPATH/src/google.golang.org/grpc
> git clone https://github.com/golang/net.git          $GOPATH/src/golang.org/x/net
> git clone https://github.com/golang/text.git         $GOPATH/src/golang.org/x/text
> git clone https://github.com/google/go-genproto.git  $GOPATH/src/google.golang.org/genproto
> cd $GOPATH/src/
> go install google.golang.org/grpc
##### 测试
> cd $GOPATH/src/google.golang.org/grpc/examples/helloworld/greeter_server/
> go run main.go
> cd $GOPATH/src/google.golang.org/grpc/examples/helloworld/greeter_client
> go run main.go

grpc Go教程

定义服务

/proto/helloworld.proto

syntax = "proto3";
// 定义包名
package proto;

// 定义Greeter服务
service Greeter {
  // 定义SayHello方法,接受HelloRequest消息, 并返回HelloReply消息
  rpc SayHello (HelloRequest) returns (HelloReply) {}
}

// 定义HelloRequest消息
message HelloRequest {
  // name字段
  string name = 1;
}

// 定义HelloReply消息
message HelloReply {
  // message字段
  string message = 1;
}

编译proto协议文件

1
> protoc -I proto/ --go_out=plugins=grpc:proto proto/helloworld.proto

protoc命令参数说明:

  • -I 指定代码输出目录,忽略服务定义的包名,否则会根据包名创建目录
  • --go_out 指定代码输出目录,格式:–go_out=plugins=grpc:目录名
    命令最后面的参数是proto协议文件

实现服务端代码

/server.go

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
package main

import (
	"log"
	"net"

	"golang.org/x/net/context"
	// 导入grpc包
	"google.golang.org/grpc"
	// 导入刚才我们生成的代码所在的proto包。
        pb "tizi365.com/helloworld/proto"
	"google.golang.org/grpc/reflection"
)


// 定义server,用来实现proto文件,里面实现的Greeter服务里面的接口
type server struct{}

// 实现SayHello接口
// 第一个参数是上下文参数,所有接口默认都要必填
// 第二个参数是我们定义的HelloRequest消息
// 返回值是我们定义的HelloReply消息,error返回值也是必须的。
func (s *server) SayHello(ctx context.Context, in *pb.HelloRequest) (*pb.HelloReply, error) {
	// 创建一个HelloReply消息,设置Message字段,然后直接返回。
	return &pb.HelloReply{Message: "Hello " + in.Name}, nil
}

func main() {
	// 监听127.0.0.1:50051地址
	lis, err := net.Listen("tcp", "127.0.0.1:50051")
	if err != nil {
		log.Fatalf("failed to listen: %v", err)
	}

	// 实例化grpc服务端
	s := grpc.NewServer()

        // 注册Greeter服务
	pb.RegisterGreeterServer(s, &server{})

	// 往grpc服务端注册反射服务
	reflection.Register(s)

        // 启动grpc服务
	if err := s.Serve(lis); err != nil {
	    log.Fatalf("failed to serve: %v", err)
	}
}
1
2
##### 运行服务端
> go run server.go

实现客户端代码

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
package main

import (
	"log"
	"os"
	"time"

	"golang.org/x/net/context"
	// 导入grpc包
	"google.golang.org/grpc"
	// 导入刚才我们生成的代码所在的proto包。
        pb "tizi365.com/helloworld/proto"
)

const (
	defaultName = "world"
)

func main() {
	// 连接grpc服务器
	conn, err := grpc.Dial("localhost:50051", grpc.WithInsecure())
	if err != nil {
		log.Fatalf("did not connect: %v", err)
	}
	// 延迟关闭连接
	defer conn.Close()

	// 初始化Greeter服务客户端
	c := pb.NewGreeterClient(conn)

	// 初始化上下文,设置请求超时时间为1秒
	ctx, cancel := context.WithTimeout(context.Background(), time.Second)
	// 延迟关闭请求会话
	defer cancel()

	// 调用SayHello接口,发送一条消息
	r, err := c.SayHello(ctx, &pb.HelloRequest{Name: "world"})
	if err != nil {
		log.Fatalf("could not greet: %v", err)
	}

	// 打印服务的返回的消息
	log.Printf("Greeting: %s", r.Message)
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
##### 运行客户端
> go run client.go

### gprc Java教程

#### 服务端代码

```Java
// 定义GreeterImpl类型,实现我们在.proto定义的服务
private class GreeterImpl extends GreeterGrpc.GreeterImplBase {

  // 实现sayHello方法
  @Override
  public void sayHello(HelloRequest req, StreamObserver<HelloReply> responseObserver) {
    // 初始化HelloReply消息
    HelloReply reply = HelloReply.newBuilder().setMessage("Hello " + req.getName()).build();
    // 返回消息
    responseObserver.onNext(reply);
    responseObserver.onCompleted();
  }

  // 实现sayHelloAgain方法
  @Override
  public void sayHelloAgain(HelloRequest req, StreamObserver<HelloReply> responseObserver) {
    HelloReply reply = HelloReply.newBuilder().setMessage("Hello again " + req.getName()).build();
    responseObserver.onNext(reply);
    responseObserver.onCompleted();
  }
}

客户端代码

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public void greet(String name) {
  logger.info("Will try to greet " + name + " ...");
  // 创建请求消息
  HelloRequest request = HelloRequest.newBuilder().setName(name).build();
  // 定义响应消息
  HelloReply response;
  try {
    // 调用服务端的sayHello方法
    response = blockingStub.sayHello(request);
  } catch (StatusRuntimeException e) {
    logger.log(Level.WARNING, "RPC failed: {0}", e.getStatus());
    return;
  }
  logger.info("Greeting: " + response.getMessage());
  try {
    // 调用服务端的sayHelloAgain方法
    response = blockingStub.sayHelloAgain(request);
  } catch (StatusRuntimeException e) {
    logger.log(Level.WARNING, "RPC failed: {0}", e.getStatus());
    return;
  }
  logger.info("Greeting: " + response.getMessage());
}

Wanglibing
Wanglibing
Engineer,Lifelong learner