gRPC基本使用
通过RPC,客户端的应用程序可以方便地调用另外一台机器上的服务端程序,就像调用本地函数一样。
安装gRPC
标准安装可参考gRPC Install
这里摘录不同语言安装gRPC时的操作
C++ 从源代码编译
C# NuGet包Grpc
Go go get google.golang.org/grpc
Node npm install grpc
PHP pecl install grpc
Python pip install grpcio
Ruby gem install grpc
Java 参考 github.com/grpc/grpc-java
Objective-C 参考 github.com/grpc/grpc/tree/master/src/objective-c
默认gRPC使用的是Protocol buffers,所以默认还需要安装protobuf。
如果是Mac操作系统,可以这样同时安装gRPC系列
brew tap grpc/grpc
brew install --with-plugins grpc
这样安装的gRPC会同时安装protoc
,grpc_cpp_plugin
, grpc_node_plugin
, grpc_php_plugin
, grpc_python_plugin
, grpc_csharp_plugin
, grpc_objective_c_plugin
, grpc_ruby_plugin
这些可执行文件。
编写protobuf文件
由于gRPC默认使用protobuf作为传输格式,所以这里需要先编写相应proto文件。示例如下:
syntax = "proto3";
option java_multiple_files = true;
option java_package = "io.grpc.examples.hello";
option java_outer_classname = "HelloProto";
option objc_class_prefix = "HL";
package hello;
// The greeting service definition.
service Greeter {
// Sends a greeting
rpc SayHello (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;
}
这里使用的是proto3语法,详细规则请参看Protobuf项目。
编译成相应源文件
不同的语言进行编译的时候,所使用的参数大同小异,但是略有差别(特别是生成的文件),使用过程中需要留意。
各语言编译(注意此处的grpc_xxx_plugin是上面安装的,如果没有安装或者是其它系统,请自行先安装)
# 假设上面的proto文件保存在当前目录的protos目录下,文件名为hello.proto
# Python; 生成文件 ./hello/hello_pb2.py
protoc -I protos --plugin=grpc_python_plugin --python_out=./hello ./protos/*.proto
# PHP; 生成目录./hello/Hello/ 与./hello/GPBMetadata/,目录下有相关文件
protoc -I protos --plugin=grpc_php_plugin --php_out=./hello ./protos/*.proto
# Node.js; 生成文件 ./hello/hello_grpc_pb.js与./hello/hello_pb.js
protoc -I protos --js_out=import_style=commonjs,binary:./hello/ --grpc_out=./hello --plugin=protoc-gen-grpc=$(which grpc_node_plugin) ./protos/*.proto
# Java
#protoc --plugin=protoc-gen-grpc-java=/path/to/protoc-gen-grpc-java --grpc-java_out=$DST_DIR --proto_path=$SRC_DIR $SRC_DIR/jr.proto
编译Go代码
protoc -I protos --go_out=plugins=grpc:./hello ./protos/*.proto
# -I 导入路径,默认是当前文件夹,使用-I参数可以去掉生成代码目录树继承的问题
# --go_out 编译成go代码时输出
注意 在gRPC的编译时,Java与Go的编译与其它语言稍有区别,Java使用可参考gRPC 初探。
启动gRPC服务端
这里以Go语言为例,上面proto文件生成的Go语言代码hello.pb.go
文件,
其文件内容与gRPC helloworld.pb.go这个文件类似(唯一不同之处是包名由hello变成了helloworld)。
这里在项目根目录 trpc 下创建一个main.go
文件
package main
import (
"fmt"
"net"
pb "trpc/hello"
"golang.org/x/net/context"
"google.golang.org/grpc"
)
type server struct{}
func (s *server) SayHello(ctx context.Context, in *pb.HelloRequest) (re *pb.HelloReply, err error) {
fmt.Println("gRPC called!")
re = &pb.HelloReply{Message: "Hello, " + in.Name}
return
}
func main() {
ln, err := net.Listen("tcp", ":8081")
if err != nil {
panic(err)
}
s := grpc.NewServer()
pb.RegisterGreeterServer(s, &server{})
s.Serve(ln)
}
将项目编译后运行,其监听在8081端口。
运行gRPC客户端
这里以Node.js客户端为例,Node的客户端调用有两种方式:动态引用与静态生成。
首先新建一个项目,安装gRPC,然后
mkdir noderpc
cd noderpc
npm init
npm i grpc --save
动态引用(动态引用方式无须先编译proto文件)
const grpc = require('grpc'); const protoPath = './protos/hello.proto'; let loader = grpc.load(protoPath); let helloProto = loader.hello; function greet() { let client = new helloProto.Greeter('localhost:8081', grpc.credentials.createInsecure()); client.sayHello({name: 'beijing'}, (err, resp) => { if (err) { console.error(err); } else { console.log(resp.message); } }); } greet();
静态编译方式
静态编译需要先将proto文件编译成javascript文件。
protoc -I protos --js_out=import_style=commonjs,binary:./lib/ --grpc_out=./lib --plugin=protoc-gen-grpc=$(which grpc_node_plugin) ./protos/hello.proto
然后在代码中引用这两个文件
const grpc = require('grpc'); const svc = require('./lib/hello_grpc_pb.js'); const msg = require('./lib/hello_pb.js'); function greet() { let req = new msg.HelloRequest(); req.setName("beijing"); let client = new svc.GreeterClient('localhost:8041', grpc.credentials.createInsecure()); client.sayHello(req, (err, resp) => { if (err) { console.error(err); } else { console.log(resp.getMessage()); } }); } greet();
使用SSL进行通信
这里以Go语言为例:
服务端代码:
var (
cert = "certs/server.crt"
key = "certs/server.key"
)
creds, err := credentials.NewServerTLSFromFile(cert, key)
if err != nil {
return fmt.Errorf("Load TLS keys error: %v", err)
}
server := grpc.NewServer(grpc.Creds(creds))
客户端代码:
var cert = "server.crt"
creds, err := credentials.NewClientTLSFromFile(cert, "")
if err != nil {
return fmt.Errorf("Load TLS cert error: %v", err)
}
conn, err := grpc.Dial(addr, grpc.WithTransportCredentials(creds))
if err != nil {
return fmt.Errorf("Dial error: %s %v", addr, err)
}
client := pb.NewSecureClient(conn) // 调用自定义 protobuf 函数
更多更详细的示例请阅读gRPC examples