Netty的深入浅出--19.gRPC和gradle的整合

接以前的项目

创建proto文件(默认在src下面),然后根据官方文档加入.gradle的相关依赖

创建Student.proto文件,定义传输的相关数据内容

 后来发现执行一直不能通过。发现gRPC指定的proto文件路径必须在main下面(除非你手动去修改):

执行代码

生成的文件:

 在这里讲解一下service方法,我个人感觉它是整个gRPC的核心所在;

首先客户端准备好MyRequest的请求信息传入到rpc方法中,然后gRPC将请求数据传入到服务端的rpc方法进行处理,然后将处理完之后的响应MyResponse返回给客户端。

创建接口继承类,实现gRPC的接口:

重点来了,仔细观察继承的方法,你会发现request就是客户端要发送给服务端的请求,而responseObserver就是返回给客户端的响应,哈哈,有没有感觉突然恍然大悟^_^。

这里对比一下之前的Thrift,我们这里返回的数据进入是在传入的参数的位置,而之前使用的Thrift是通过return返回响应给客户端的

打印客户端传过来的请求

前面提到了responseObserver用来返回响应数据给客户端,具体方法就是使用.onNext()方法。

 然后调用onCompleted()方法来调用,告诉.onNext()方法,我执行完毕了,可以返回响应数据给客户端了

现在定义服务端

下面是服务器的启动代码,代码使用的是官方提供的启动方式

服务器关闭方法

解释一下:当没有调用start()方法的时候,server为null,调用了之后就不为空:

 gRPC和Thrift不同,Thrift是阻塞式的,而gRPC是非阻塞式的,所以需要调用方法来保持阻塞,不然它执行一次就会结束

书写主函数 ,这里你可以尝试不调用awaitTermination()方法,你会发现main方法执行完一次之后就关闭了

 到现在为止服务端就写完了,写客户端代码:

这里面为什么调用的是StudentServiceGrpc而不是和服务端那一样用StudentServerImpl呢?在服务端的理解我们能够看懂,但是这里使用它来调用就有点迷了。底层的东西一下子说不清楚,你只要知道在服务端使用的是StudentServerImpl来调用getRealNameByUsername()方法,在客户端使用的是StudentServerGrpc来调用的就可以了。

他们两本来就是:StudentServerGrpc是StudentServerImpl的父类

启动服务器:

启动客户端

解释一下:打印张三是说明客户端进入到了StudentServiceImpl()方法中,说明进入到里面打印之后并且修改了“zhangsan”变为“张三”,这里你一定要理解啊!

StudentServiceImpl这个类对于客户端来说,它只是一个数据传输处理,一定要记住它只能从这个传输处理中进行两个操作:第一个是传入请求信息,第二个是获取响应,而该方法中内部的操作对客户端来说相当于没有。

这里也很好的说明了为什么客户端使用的是StudentServerGrpc了,而不是直接使用StudentServerImpl了。

 服务端打印出来的其实就是方法体中的内容。

总结性的回想一下,是不是现在脑回路很清晰了o(* ̄︶ ̄*)o

 这样我又重新提一下,http在进行数据传输的时候,是当客户端发送完一次请求之后,服务端收到请求之后,返回给客户端,然后就把自己给关闭了。而以上说到的所有rpc框架都是基于socket编程实现的,相当于客户端发送给服务端请求之后,服务端收到请求之后,返回响应给客户端,然后服务端又继续等待下一次客服端发送请求。

但是你会问一个问题,那它服务端什么时候停止呢?

大部分成熟的rpc框架(Apache Thrift和gRPC)都具备了心跳检测机制,至于什么是心跳检测的话,你可以看我另外一篇博客:Netty的深入浅出--5.Netty读写检测(心跳检测)与长连接

做到这里千万别以为已经做完了,我们服务端的stop方法还没有调用呢,但是我们stop方法该怎么调用呢,这里使用到钩子(hook)

执行服务端,你会发现并没有执行钩子程序,这就是我们要实现的效果

 当我们关闭服务端之后:

 现在我们回来继续看代码:

我们为什么要在main方法中写一个awaitTermination()?

它的源码中的描述是,等待程序变成终止

 然后我们通过该方法的重载方法,来测试一下,等待3秒之后,看发生什么

启动服务器:

过了3秒之后执行了关闭termination。并且调用了构造方法 ,而且code 0,说明是正常退出,和我们之前手动关闭服务端是不同的

然后我们分析一个很重要的东西,runtime的回调钩子

这个东西在程序里面是一个很有用的东西,但是很多人确很少会使用

源码中描述到说:在jvm中只有一个runtime对象,在整个项目过程中它是单例的

对于addShutdownHook是一个很重要的方法

源码注解中说到该方法是注册一个新的hook,而且在只有两个种事件会触发该方法

 第一种简单描述就是程序执行完了之后自动执行退出;

第二种就是人为的退出

钩子(hook)就是一种简单的初始化了的但是还未开始的线程。

当虚拟机开始执行shutdown 时,所有的hook都会并发的且无序的执行。

但shutdown开始之后,只能通过执行halt来停止虚拟机的终止(termination)

 

2023-07-14 15:19:01.215 WARN 7308 --- [sson-netty-2-15] io.netty.util.concurrent.DefaultPromise : An exception was thrown by org.redisson.misc.RedissonPromise$$Lambda$888/0x00000008008f7440.operationComplete() java.lang.NullPointerException: null 2023-07-14 15:19:01.216 ERROR 7308 --- [sson-netty-2-15] o.r.c.SentinelConnectionManager : Can't execute SENTINEL commands on /172.24.107.11:26379 org.redisson.client.RedisException: ERR No such master with that name. channel: [id: 0x2d66827d, L:/172.23.9.103:46812 - R:/172.24.107.11:26379] command: (SENTINEL SLAVES), params: [mymaster] at org.redisson.client.handler.CommandDecoder.decode(CommandDecoder.java:365) ~[redisson-3.13.3.jar:3.13.3] at org.redisson.client.handler.CommandDecoder.decodeCommand(CommandDecoder.java:196) ~[redisson-3.13.3.jar:3.13.3] at org.redisson.client.handler.CommandDecoder.decode(CommandDecoder.java:134) ~[redisson-3.13.3.jar:3.13.3] at org.redisson.client.handler.CommandDecoder.decode(CommandDecoder.java:104) ~[redisson-3.13.3.jar:3.13.3] at io.netty.handler.codec.ByteToMessageDecoder.decodeRemovalReentryProtection(ByteToMessageDecoder.java:501) ~[netty-codec-4.1.51.Final.jar:4.1.51.Final] at io.netty.handler.codec.ReplayingDecoder.callDecode(ReplayingDecoder.java:366) ~[netty-codec-4.1.51.Final.jar:4.1.51.Final] at io.netty.handler.codec.ByteToMessageDecoder.channelRead(ByteToMessageDecoder.java:276) ~[netty-codec-4.1.51.Final.jar:4.1.51.Final] at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:379) ~[netty-transport-4.1.51.Final.jar:4.1.51.Final] at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:365) ~[netty-transport-4.1.51.Final.jar:4.1.51.Final] at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:357) ~[netty-transport-4.1.51.Final.jar:4.1.51.Final] at io.netty.channel.DefaultChannelPipeline$HeadContext.channelRead(DefaultChannelPipeline.java:1410) ~[netty-transport-4.1.51.Final.jar:4.1.51.Final] at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:379) ~[netty-transport-4.1.51.Final.jar:4.1.51.Final] at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:365) ~[netty-transport-4.1.51.Final.jar:4.1.51.Final] at io.netty.channel.DefaultChannelPipeline.fireChannelRead(DefaultChannelPipeline.java:919) ~[netty-transport-4.1.51.Final.jar:4.1.51.Final] at io.netty.channel.nio.AbstractNioByteChannel$NioByteUnsafe.read(AbstractNioByteChannel.java:163) ~[netty-transport-4.1.51.Final.jar:4.1.51.Final] at io.netty.channel.nio.NioEventLoop.processSelectedKey(NioEventLoop.java:714) ~[netty-transport-4.1.51.Final.jar:4.1.51.Final] at io.netty.channel.nio.NioEventLoop.processSelectedKeysOptimized(NioEventLoop.java:650) ~[netty-transport-4.1.51.Final.jar:4.1.51.Final] at io.netty.channel.nio.NioEventLoop.processSelectedKeys(NioEventLoop.java:576) ~[netty-transport-4.1.51.Final.jar:4.1.51.Final] at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:493) ~[netty-transport-4.1.51.Final.jar:4.1.51.Final] at io.netty.util.concurrent.SingleThreadEventExecutor$4.run(SingleThreadEventExecutor.java:989) ~[netty-common-4.1.51.Final.jar:4.1.51.Final] at io.netty.util.internal.ThreadExecutorMap$2.run(ThreadExecutorMap.java:74) ~[netty-common-4.1.51.Final.jar:4.1.51.Final] at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30) ~[netty-common-4.1.51.Final.jar:4.1.51.Final] at java.base/java.lang.Thread.run(Thread.java:834) ~[na:na] 解决方法
07-15
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值