Handler
public class StudentServiceHandler extends StudentServiceGrpc.StudentServiceImplBase{
@Override
public void searchStudent(Message.Request request, StreamObserver<Message.Student> responseObserver) {
String param = request.getParam();
Message.Student student = Message.Student.newBuilder().setAddr("localhost").setId(99).setName(param).build();
responseObserver.onNext(student);
responseObserver.onCompleted();
}
}
先记住这个奇葩,这个方法的返回值是void
。后面stream
的确会有非空的返回值,不过现在先不关注。
我们的入参好像多了一个StreamObserver
,我们凭借这个进行返回。
你可以把它理解为指针,它会把方法内的操作记录并反馈到外部调用。
observer
主要有三个方法
onError
:错误时应该抛出的异常,参数类型为Throwable
onNext
:返回值,我们把需要返回的数据直接添加进去即可
onCompleted
:算作一个标记,也就是return
,这个时候就会推送返回值了
Server
public class ServerMain {
private Server server = null;
private void start() throws IOException {
server = ServerBuilder.forPort(8989).addService(new StudentServiceHandler()).build().start();
}
private void stop(){
if(server != null){
this.server.shutdown();
}
}
private void awaitTermintaion() throws InterruptedException {
if(server != null){
server.awaitTermination();
}
}
public static void main(String[] args) throws Exception{
ServerMain server = new ServerMain();
server.start();
server.awaitTermintaion();
}
}
抄示例代码的,不过可以不封装,然后更简洁
public class SimpleServer {
public static void main(String[] args) throws Exception{
Server server = ServerBuilder.forPort(8989).addService(new StudentServiceHandler()).build();
server.start();
server.awaitTermination();
Runtime.getRuntime().addShutdownHook(new Thread(()->{
server.shutdown();
}));
}
}
创建
Server server = ServerBuilder.forPort(8989)
.addService(new StudentServiceHandler())
.build();
只能说不愧是protobuf
的延展,到哪都是build
。
ServerBuilder
:构建器类
forPort
:指定端口
addService
:指定处理器,类型要注意,不是乱定义类型,记得注意顶层父类类型
build
:构建
也就是说,我们的工作其实就是指定端口和添加处理器。
开启
// 服务还是要开启的,没啥说的
server.start();
等待
server.awaitTermination();
这是等待
,或者称为阻塞
。
一般情况下,我们使用while
死循环进行监听,grpc
底层使用锁的形式进行的阻塞。
如果不进行阻塞,你的服务就是一闪而过,执行完毕就会自动关闭。
默认是无限阻塞,你也可以指定空闲检测。
server.awaitTermination(5, TimeUnit.SECONDS);
当空闲超过这个时间范围,服务器就会自动关闭。
关闭
Runtime.getRuntime().addShutdownHook(new Thread(()->{
server.shutdown();
}));
钩子的话,就是程序死去的最后一口气了。
在程序关闭时,或许是正常执行完毕,也可能是Ctrl+c
等信号关闭,都会检测一下这个东西。
内部定义的方法,不关闭是不执行的。
更多的话自己试试就知道了,没啥好说的。
server.shutdown
:关闭服务
通过拆分出来,对整个流程更清楚了。
我们自己所为的
server
就是封装的底层server
,自定义随意,不过需得有的放矢。
Client
public class ClientMain {
public static void main(String[] args) {
ManagedChannel managedChannel = ManagedChannelBuilder
.forAddress("localhost",8989)
.usePlaintext()
.build();
StudentServiceGrpc.StudentServiceBlockingStub stub = StudentServiceGrpc.newBlockingStub(managedChannel);
Message.Student student = stub.searchStudent(Message.Request.newBuilder().setParam("godme").build());
System.out.println(student.toString());
}
}
客户端没有封装,看起来也更加简洁。
通道
ManagedChannel managedChannel = ManagedChannelBuilder
.forAddress("localhost",8989)
.usePlaintext()
.build();
指定地址创建链接这没问题,当然还需要指定协议。
结合thrift
来理解
thrift | protobuf | description |
---|---|---|
trasnport | channel | 运输 |
protocol | usePlaintext | 识别 |
我们之前说传输东西总共有两次编码:
- 二进制码流识别
- 序列化对象还原
这里的Plaintext
属于第一种,第二种序列化采用的是protobuf
梳理一下
ManagedChannelBuilder
:channel
构建类
forAddress
:指定链接服务地址
usePlaintext
:指定传输协议
build
:构建
创建
StudentServiceGrpc.StudentServiceBlockingStub stub = StudentServiceGrpc.newBlockingStub(managedChannel);
我们一般是这样称呼的server - client
。
专业一点的,细致一点的这样称呼skeleton - stub
。
都不用纠结,一个描述客户端,一个代指服务端。在grpc
中,两者都直接称呼为stub
了。
创建一个stub
,也就是创建了一个client
。
serviceGrpc
自动代码创建时已经创建好了,需要传入的就是我们的channel
。
调用
Message.Student student = stub.searchStudent(Message.Request.newBuilder().setParam("godme").build());
通过client
去调用服务端的功能,没问题。
小结
基本使用就是如此,四种方法调用后续加上。
代码在此