码迷,mamicode.com
首页 > 编程语言 > 详细

gRPC中的Netty HTTP/2(基于Java版)

时间:2020-05-21 19:28:07      阅读:188      评论:0      收藏:0      [点我收藏+]

标签:选择   prot   接口   远程调用   使用   异常   应用   port   请求   

 

 

gRPC的技术栈

技术图片

 

(1)远程服务提供者需要以某种形式提供服务调用相关的信息,包括但不限于服务接口定义、数据结构,以及中间态的服务定义文件,例如gRPC 的 proto 文件、WS-RPC 的WSDL文件定义,甚至也可以是服务端的接口说明文档。服务调用者需要通过一定的途径获取远程服务调用相关信息,例如服务端接口定义Jar包导入、获取服务端IDL文件等。

(2)远程代理对象:服务调用者调用的服务实际是远程服务的本地代理,对于 Java语言,它的实现就是 JDK 的动态代理,通过动态代理的拦截机制,将本地调用封装成远程服务调用。

(3)通信:RPC框架与具体的协议无关,例如Spring的远程调用支持HTTP Invoke、RMI Invoke,MessagePack使用的是私有的二进制压缩协议。

(4)序列化:远程通信需要将对象转换成二进制码流进行网络传输,不同的序列化框架支持的数据类型、数据包大小、异常类型及性能等都不同。不同的 RPC框架的应用场景不同,因此技术选择也存在很大差异。一些做得比较好的 RPC框架,支持多种序列化方式,有的甚至支持用户自定义

相比其他开源的RPC框架,gRPC有如下几个特点

(1)支持多种语言。

(2)基于 IDL 文件定义服务,通过 proto3 工具生成指定语言的数据结构、服务端接口及客户端Stub。

(3)通信协议基于标准的HTTP/2设计,支持双向流、消息头压缩、单TCP的多路复用、服务端推送等特性。

(4)序列化支持Protocol buffer和JSON,Protocol buffer是一种语言无关的高性能序列化框架,基于“HTTP/2+Protocol buffer”,保障了RPC调用的高性能。

 

gRPC 底层的通信框架基于Netty 4.1构建,通过集成Netty的HTTP/2协议栈,支持双向流、消息头压缩、单TCP的多路复用、服务端推送等特性,传统的HTTP/1.0或者HTTP/1.1是无状态的,创建HTTP连接之后,客户端发送请求消息,然后等待服务端响应,接收到服务端响应之后,客户端接着发送后续的请求消息,服务端再返回响应,周而复始。请求和响应消息都是成对出现的,采用的是“一请求对应一响应”模式。在某个时刻,一个HTTP连接上只能单向地处理一个消息,就像单行道,一个消息处理得慢,很容易导致后续消息阻塞。

 

采用该模式主要存在如下几个缺点。

(1)单个连接的通信效率不高,无法多路复用。

(2)一个请求消息处理得慢,很容易阻塞后续其他请求消息。

(3)如果客户端读取响应超时,由于消息是无状态的,只能关闭连接,重建连接之后再发送请求。如果频繁地发生客户端超时,就会发生大量的HTTP连接断连和重连,如果采用HTTPS,SSL链路的重建成本很高,很容易导致服务端因负载过重而宕机。

(4)为了解决单个HTTP连接性能不足问题,只能创建一个大的连接池,在大规模集群组网场景下,HTTP连接数会非常多,额外占用大量句柄资源。

采用HTTP/2之后,可以实现多路复用,客户端可以连续发送多个请求,服务端也可以返回一个或者多个响应,而且还可以主动推送数据到服务端,实现双向通信,达到TCP协议的通信效果。

gRPC通过对Netty HTTP/2的封装,向用户屏蔽底层RPC通信的协议细节。

Netty HTTP/2服务端创建的主要流程:

1.NettyServer 具体管理 HTTP/2协议栈的启动和停止,以及 HTTP/2协议相关的各种参数。

2.NettyServer 的 start 方法会创建 NettyServerTransport,通过NettyServerTransport来创建gRPC的Netty HTTP/2 ChannelHandler实例,并加入ChannelPipeline。

(NettyServerHandler主要负责HTTP/2消息的处理,例如HTTP/2请求消息体和消息头的读取、Frame消息的发送、Stream状态消息的处理等)

3.创建 ProtocolNegotiator实例,用于 HTTP/2连接创建的协商。gRPC支持三种协商策略,分别是 PlaintextNegotiator、PlaintextUpgradeNegotiator和 TlsNegotiator。其中PlaintextUpgradeNegotiator通过设置Http2ClientUpgradeCodec,用于协议升级。

4.创建ServerBootstrap、bossGroup和workerGroup,启动HTTP/2服务端,在NettyServer的start方法里

 

gRPC服务端的请求消息由Netty HTTP/2协议栈负责接入,gRPC通过继承Http2FrameAdapter,将自定义的FrameListener添加到Netty的Http2ConnectionDecoder中,在HTTP/2请求消息头和消息体被解析成功之后,回调 gRPC的FrameListener,接收并处理 HTTP/2请求消息,将 Netty的请求消息体 ByteBuf转换成 gRPC内部的NettyReadableBuffer对象,调用deframe方法,完成请求消息体的解码.

 

 服务端发送HTTP/2 响应消息原理:

gRPC 服务端通过将响应消息封装成WriteQueue.AbstractQueuedCommand,异步写入WriteQueue,然后调用 WriteQueue 的 scheduleFlush 操作,将响应消息发送命令放到NioEventLoop 中执行,NioEventLoop调用 channel.write 方法将其发送到ChannelPipeline,由 gRPC 的NettyServerHandler 拦截 write 方法,按照命令的分类进行处理,最后调用 Netty Http2ConnectionEncoder的write方法完成响应消息的发送。

1.通过NettyServerStream的Sink类对需要发送的HTTP/2响应消息进行Task封装,实现消息发送的异步化.

2.WriteQueue 将任务投递到对应 Channel 的 NioEventLoop 线程异步执行消息发送操作,注意WriteQueue的scheduleFlush操作是在业务线程中的,会有多个业务线程,所以这里要加锁,保证多线程的同步性,这里采用了无锁化操作CAS,代码如下

3.NioEventLoop线程循环处理待发送的响应消息,调用 Channel的write方法,将消息发送到ChannelPipeline,由gRPC ChannelHandler拦截处理

4.gRPC的NettyServerHandler拦截write方法,按照命令的类型进行分类处理,如果是 SendGrpcFrameCommand 和SendResponseHeadersCommand,则调用 Netty 的Http2ConnectionEncoder完成HTTP/2消息的发送。

 

即HTTP/2服务端创建、HTTP/2请求消息的接入和响应发送都由Netty NioEventLoop线程负责,gRPC消息的序列化和反序列化业务服务接口的调用由 gRPC的SerializingExecutor线程池负责。

 

Netty NIO线程和gRPC的SerializingExecutor之间没有映射关系(M:N),当线程数量比较多时,锁竞争会非常激烈,可以采用 I/O线程和 gRPC服务调用线程绑定的方式,降低出现锁竞争的概率,提升并发性能,通过线程绑定技术降低锁竞争的概率(1:1).

 

gRPC中的Netty HTTP/2(基于Java版)

标签:选择   prot   接口   远程调用   使用   异常   应用   port   请求   

原文地址:https://www.cnblogs.com/moonyaoo/p/12932602.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!