写在前面
从上图和文档中我们可以了解到,用gRPC来进行远程服务调用就仅仅需要gRPC Stub(Client)用Proto Request向远方的gRPC Server发起服务调用,然后远方的gRPC Server通过Proto Response(s)将调用结果返回给gRPC Stub。
上面这段逻辑的背后,gRPC做了什么:
一个gRPC从开始发起请求到返回总共要经历过序列化,编解码,以及网络传输这些内容。这些东西在我们使用gRPC框架做远程服务调用的时候完全感知不到!
至于gRPC的stub之间的连接管理,健康检查,负载均衡,异常重试,优雅启停机,熔断限流等等是需要我们从gRPC源码中获得这些知识的!
从0写一个Java-gRPC项目
本demo项目代码:https://github.com/cld378632668/gRPC-java-introductiongithub.com
首先编写.proto文件,其中会定义service和message,比如这里我们定义:
service user{
rpc login(LoginRequest) returns (APIResponse);
rpc logout(Empty) returns (APIResponse);
}
message LoginRequest{
string username = 1;
string passwd = 2;
}
message APIResponse{
string responsemessage = 1;
int32 responseCode = 2;
}
message Empty{
}
然后不同的语言有不同的工具根据.proto生成源代码文件(如.java,本文以JAVA来讲解)。
例如我们在定义了service HelloService,mesage HelloRequest,message HelloResponse,自动生成的文件有:
Hello.java
HelloRequest.java
HelloRequestOrBuilder.java
HelloResponse.java
HelloResponseOrBuilder.java
在不同的RPC版本中,上面五个类可能全都放在一个Hello.java文件中
GreetingService.java
生成RPC通信代码:
HelloServiceGrpc.java,在其他RPC版本中该文件的名字可能为helloGrpc.java
该文件中包含有类:
helloServiceImplBase
helloStub
helloBlockingStub
helloFutureStub
自定义Service类
接下来我们需要在自己创建的服务端项目中实现一个HelloService类,这个类继承helloServiceImplBase,然后重写login和logout两个方法,这两个方法在后面的客户端中可以通过stub.login(..)的形式被调用。
class HelloService extends HelloGrpc.HelloImplBase {
@Override
public void login(User.LoginRequest request, StreamObserver responseObserver) {
String username = request.getUsername();
String password = request.getPasswd();
User.APIResponse.Builder response = User.APIResponse.newBuilder();
// APIResponse对应.proto里对应的一个message的名字
if (username.equals(password)){
response.setResponseCode(0).setResponsemessage("SUCCESS");
}else{
response.setResponseCode(100).setResponsemessage("INVALID PASSWD");
}
responseObserver.onNext(response.build());
responseObserver.onCompleted();
}
}
自定义服务端
一共三行核心代码。
// 服务的添加:
io.grpc.Server server = ServerBuilder.forPort(9090).addService(new 自定义的Service类名1()).
.addService(new Service2()).addService(new Service3()).build();
// 服务的启动:
server.start();
// 服务的关闭:
server.awaitTermination();
自定义客户端
自定义的客户端通过stub调用服务端的方法,服务端的方法一开始是定义在.proto的service中的,如rpc login(LoginRequest) returns (APIResponse); LoginRequest和APIResponse被定义为两个message.
标准的grpc client调用代码,最简单的方式,就三行代码:
ManagedChannelImpl channel = NettyChannelBuilder.forAddress("127.0.0.1", 6556).build();
// 其他写法1:
// ManagedChannelImpl channel = ManagedChannelBuilder.forAddress(host, port)
.usePlaintext()
.build();
DemoServiceGrpc.DemoServiceBlockingStub stub = DemoServiceGrpc.newBlockingStub(channel);
stub.login(LoginRequest.getDefaultInstance());
这三行代码,完成了grpc客户端调用服务器端最重要的三个步骤:创建连接到远程服务器的 channel
构建使用该channel的客户端stub
通过stub调用服务方法,执行RPC调用
代码参考
完整的代码构建过程可以参考:
1、微信文章.孟君的编程札记.一步步完成gRPC示例。
2、youtube教程:https://www.youtube.com/watch?v=JFzAe9SvNaU&list=PLI5t0u6ye3FGXJMh5kU2RvN0xrul67p7Rwww.youtube.com
Go Generated Code Reference
Thread-safety: note that client-side RPC invocations and server-side RPC handlersare thread-safeand are meant to be run on concurrent goroutines. But also note that forindividual streams, incoming and outgoing data is bi-directional but serial; so e.g.individual streamsdo not supportconcurrent readsorconcurrent writes(but reads are safely concurrentwithwrites).
推荐资料