gRPC 是一个高性能、开源和通用的 RPC 框架,面向移动和 HTTP/2 设计。目前提供 C、Java 和 Go 语言版本,分别是:grpc, grpc-java, grpc-go. 其中 C 版本支持 C, C++, Node.js, Python, Ruby, Objective-C, PHP 和 C# 支持.
gRPC 基于 HTTP/2 标准设计,带来诸如双向流、流控、头部压缩、单 TCP 连接上的多复用请求等特。这些特性使得其在移动设备上表现更好,更省电和节省空间占用。
在 gRPC 里客户端应用可以像调用本地对象一样直接调用另一台不同的机器上服务端应用的方法,使得您能够更容易地创建分布式应用和服务。与许多 RPC 系统类似,gRPC 也是基于以下理念:定义一个服务,指定其能够被远程调用的方法(包含参数和返回类型)。在服务端实现这个接口,并运行一个 gRPC 服务器来处理客户端调用。在客户端拥有一个存根能够像服务端一样的方法。
gRPC 默认使用 protocol buffers,这是 Google 开源的一套成熟的结构数据序列化机制。
我们使用 protocol buffers 接口定义语言来定义服务方法,用 protocol buffer 来定义参数和返回类型。客户端和服务端均使用服务定义生成的接口代码。
我们来实现一个sayHello方法,这里采用官网提供的proto文件,新建一个helloworld.proto,Greeter
服务有一个方法 SayHello
,可以让服务端从远程客户端接收一个包含用户名的 HelloRequest
消息后,在一个 HelloReply
里发送回一个 Greeter
syntax = "proto3";
option java_package = "com.grpc.test";
package helloworld;
// The greeter 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;
}
服务到这定义好了,现在开始配置环境。
一、java安装
参照https://github.com/grpc/grpc-java 步骤进行安装
创建Maven 项目
修改pom.xml,引入依赖 和插件。
4.0.0
com.demo
GrpcTest
1.0-SNAPSHOT
io.grpc
grpc-netty-shaded
1.16.1
io.grpc
grpc-protobuf
1.16.1
io.grpc
grpc-stub
1.16.1
kr.motd.maven
os-maven-plugin
1.5.0.Final
org.xolstice.maven.plugins
protobuf-maven-plugin
0.5.1
com.google.protobuf:protoc:3.5.1-1:exe:${os.detected.classifier}
grpc-java
io.grpc:protoc-gen-grpc-java:1.16.1:exe:${os.detected.classifier}
compile
compile-custom
二、python安装
python安装相对简单。安装以下三个依赖包
1)gRPC 的安装,执行命令:pip install grpcio
2)ProtoBuf 相关的 python 依赖库,执行:pip install protobuf
3)安装 python grpc 的 protobuf 编译工具,执行:pip install grpcio-tools
安装成功后开始撸代码首先从java开始
首先需要把之前创建的helloworld.proto放到项目里,不能随便放,有默认位置,在src/main/下新建proto文件夹,并把helloworld.proto放进去。
在控制台输入 mvn compile。会在target下生产一堆文件,在target/generated-sources/protobuf/java 和grpc-java下会生成我们需要的文件
接下来开始写代码,首先创建一个服务端处理消息的类,实现我们服务定义的生成的服务接口,做我们的服务的实际的“工作”。
package com.grpc.test;
import io.grpc.stub.StreamObserver;
/**
* @author luoaojin
* @CreateTime 2018-11-14
* @Description
*/
public class SayHelloImpl extends GreeterGrpc.GreeterImplBase {
@Override
public void sayHello(Helloworld.HelloRequest request, StreamObserver responseObserver) {
// 这一行一定要删除,否则会提示你没有实现这个方法。
// super.sayHello(request, responseObserver);
System.out.println(request.getName());
Helloworld.HelloReply data = Helloworld.HelloReply.newBuilder().setMessage("Hello " + request.getName()).build();
responseObserver.onNext(data);
responseObserver.onCompleted();
}
}
编写服务端
package com.grpc.test;
import io.grpc.Server;
import io.grpc.ServerBuilder;
import java.io.IOException;
/**
* @author luoaojin
* @CreateTime 2018-11-14
* @Description
*/
public class DefinedServer {
private Server server;
private void start() throws IOException {
server = ServerBuilder.forPort(50051)//服务端只用指定端口号
.addService(new SayHelloImpl())
.build()
.start();
Runtime.getRuntime().addShutdownHook(new Thread() {
@Override
public void run() {
// Use stderr here since the logger may have been reset by its JVM shutdown hook.
System.err.println("*** shutting down gRPC server since JVM is shutting down");
this.stop();
System.err.println("*** server shut down");
}
});
}
private void stop() {
if (server != null) {
server.shutdown();
}
}
/**
* Await termination on the main thread since the grpc library uses daemon threads.
*/
private void blockUntilShutdown() throws InterruptedException {
if (server != null) {
server.awaitTermination();
}
}
/**
* Main launches the server from the command line.
*/
public static void main(String[] args) throws IOException, InterruptedException {
final DefinedServer server = new DefinedServer();
server.start();
server.blockUntilShutdown();
}
}
编写客户端:
package com.grpc.test;
import io.grpc.ManagedChannel;
import io.grpc.ManagedChannelBuilder;
import io.grpc.StatusRuntimeException;
import java.util.concurrent.TimeUnit;
/**
* @author luoaojin
* @CreateTime 2018-11-14
* @Description
*/
public class Client {
private final ManagedChannel channel;
private final GreeterGrpc.GreeterBlockingStub blockingStub;
/**
* Construct client connecting to HelloWorld server at {@code host:port}.
*/
public Client(String host, int port) {
channel = ManagedChannelBuilder.forAddress(host, port)
.usePlaintext()
.build();
blockingStub = GreeterGrpc.newBlockingStub(channel);
}
public void shutdown() throws InterruptedException {
channel.shutdown().awaitTermination(5, TimeUnit.SECONDS);
}
/**
* Say hello to server.
*/
public void greet(String name) {
Helloworld.HelloRequest request = Helloworld.HelloRequest.newBuilder().setName(name).build();
Helloworld.HelloReply response;
try {
response = blockingStub.sayHello(request);
} catch (StatusRuntimeException e) {
System.out.println("RPC failed: {0}"+ e.getStatus());
return;
}
System.out.println(("Greeting: " + response.getMessage()));
}
/**
* Greet server. If provided, the first element of {@code args} is the name to use in the
* greeting.
*/
public static void main(String[] args) throws Exception {
Client client = new Client("localhost", 50051);
try {
String user = "luo";
if (args.length > 0) {
user = args[0];
}
client.greet(user);
} finally {
client.shutdown();
}
}
}
逻辑比较简单,主要就是把名称给服务端发送过去。
这样简单的demo就完成了,那我们先启动服务端,再起客户端。见到下图结果。大功告成。
同样,我们也需要实现服务端和客户端,那我们也采用同一个proto文件,因为未来要实现python 和java互相通信,所以必须要用同一套接口。
在python 项目文件夹下,将proto文件贴过来。
需要编译的helloworld.proto 文件存放在 . 目录下,生成的文件放在.下,则可以使用命令:
python -m grpc_tools.protoc -I=. --python_out=. --grpc_python_out=. helloworld.proto
然后项目会出现生成两个文件,由于对python不太熟悉,所以就简单叙述叙述怎么使用。
helloworld_pb2.py
: 用来和 protobuf 数据进行交互
helloworld_pb2_grpc.py
: 用来和 grpc 进行交互
接着开启编写我们的服务端。 依赖于我们生成的两个文件。
import time
from concurrent import futures
import grpc
import helloworld_pb2
import helloworld_pb2_grpc
_ONE_DAY_IN_SERVICE = 60 * 60 * 24
IP = "[::]:50051"
class service(helloworld_pb2_grpc.GreeterServicer):
def SayHello(self, request, context):
txt = request.name
return helloworld_pb2.HelloReply(message ='hello {msg}'.format(msg = txt))
def serve():
grpcServer = grpc.server(futures.ThreadPoolExecutor(max_workers=4))
helloworld_pb2_grpc.add_GreeterServicer_to_server(service(), grpcServer)
grpcServer.add_insecure_port(IP)
grpcServer.start()
print("server start success")
try:
while True:
time.sleep(_ONE_DAY_IN_SERVICE)
except KeyboardInterrupt:
grpcServer.stop(0)
if __name__ == '__main__':
serve()
接着是客户端
import grpc
import helloworld_pb2
import helloworld_pb2_grpc
IP = 'localhost:50051'
def run():
channel = grpc.insecure_channel(IP)
stub = helloworld_pb2_grpc.GreeterStub(channel=channel)
response = stub.SayHello(helloworld_pb2.HelloRequest(name='luo'))
print("recevied: " + response.message)
if __name__ == '__main__':
run()
创建完毕后项目的结构是这样的
整体逻辑跟java差不多。同样是先启动服务端,再启动客户端,出现下面界面,大功告成。
启动java 或python服务端,然后再启动不同语言的服务端,这样可以测试通过。
第一次写博客,有问题欢迎斧正。同时也感谢参考的大神的文章。
grpc官网 :http://doc.oschina.net/grpc?t=60134
https://blog.csdn.net/u010626747/article/details/80824582
https://www.colabug.com/2904867.html
https://blog.csdn.net/lluozh2015/article/details/53106475