ES源码四:网络通信层流程

听说ES网络层很难?今天来卷它😄

前言

ES网络层比较复杂,分为两个部分:

  1. 基于HTTP协议的REST服务端
  2. 基于TCP实现的PRC框架

插件化设计的网络层模块(NetworkModule)

入口还是上一章的创建Node构造方法的地方:
image.png
一直往下会找到之前说的加载module和服务的地方:
image.png

NetworkModule:

NetworkModule属性

image.png
有两个map,一个map存放tcp层的实现,一个map存放http层的实现

NetworkModule构造方法

image.png
上面的参数分别为:

  1. settings:settings配置
  2. transportClient: 一般为false
  3. plugins:NetworkPlugin 提供了获取TCPTransport和HTTPTransport的接口,NetworkModule通过遍历全部networkPlugin来加载所有的网络层实现,也就说说,ES的网络层不一定就是一种实现。你也可以通过实现NetworkPlugin去提供网络层实现。我们主要看的网络层是Netty4Plugin
  4. threadPool:线程池
  5. bigArrays:大数组,通信的时候会使用它的空间
  6. pageCacheRecycler:和核心流程没关系,跳过
  7. circuitBreakerService:和核心流程没关系,跳过
  8. namedWritableRegistry:和核心流程没关系,跳过
  9. xContentRegistry:和核心流程没关系,跳过
  10. networkService:网络服务,主要是将host地址转换为 Java Network地址对象,解析地址使用
  11. dispatcher:它的实现是RestController,RestController内部注册了很多处理器,对外提供访问路由的功能,也就说Http请求会转换为RestRequest,RestRequest最终会交给RestController去找到合适的handler,然后去访问最终的义务层逻辑,(后面源码会分析到)
  12. clusterSettings:集群配置

image.png
我们来看这里做了几件事(我们这里看的是Netty4Plugin):

  1. plugin.getHttpTransports:获取Http网络层的实现,这里返回的是一个map,key是netty4,value是一个Netty4Plugin lambda表达式

image.png
image.png

  1. 遍历httpTransportFactory,将key和value值放到之前的transportHttpFactories

image.png

  1. 后面的RPC逻辑类似,直接过😄

## HTTP通信层**(Netty4HttpServerTransport)** ### **Netty4HttpServerTransport构造方法** ![image.png](https://img-blog.csdnimg.cn/img_convert/0ed921b3bb374f83bf44c1438706d1be.png)
和之前NetworkModule的参数基本差不多,多了一个sharedGroupFactory:**这里是一个netty NioEventGroup。学过netty源码应该都懂,如果不懂,可以看我写的netty源码解析。**

Netty4HttpServerTransport.doStart方法

入口:

Node->start() => injector.get(HttpServerTransport.class).start()=>Netty4HttpServerTransport.doStart
在node节点启动的时候,会存在image.png,这个httpServerTransport实现了AbstractLifecycleComponent,在它的start最终会调用到子类Netty4HttpServerTransport的doStart方法
****image.png
image.png

doStart方法

image.png
image.png
都是netty服务端常规的配置,如果看不懂,还是要学一下netty源码的。学过netty的都知道,肯定是有一个服务端的请求入口Handler类的,这个类才是我们最要注意看的:Netty4HttpRequestHandler

Netty4HttpRequestHandler.channelRead0

请求处理入口:Netty4HttpRequestHandler->channelRead0(ctx, httpRequest)
image.png
又把请求转接给了Netty4HttpServerTransport.incomingRequest

Netty4HttpServerTransport.incomingRequest

请求处理入口: Netty4HttpServerTransport->incomingRequest(httpRequest, httpChannel
image.png
image.png

这里的核心逻辑就是:解析HttpRequest为RestRequest,包装HttpChannel为RestChannel。包装之后,最终交给 Dispatcher 方法

RestController.dispatchRequest

image.png
会接着调用RestController.tryAllHandlers方法
image.png
image.png
总结一下一共做了这几件事:

  1. 读取RestRequest请求路径(rawPath)及Method(GET、POST、DELETE…)等信息
  2. 根据rawPath等信息到PathTrie(字典树结构)中找到提供服务的Handler
  3. 调用BaseRestHandler->handleRequest(restRequest, restChannel, nodeClient) (BaseRestHandler为所有Rest*Action的父类…),比如我这里debug用的是写索引数据的rest请求,实际请求到的是RestIndexAction

#### BaseRestHandler.handleRequest ![image.png](https://img-blog.csdnimg.cn/img_convert/1aaec418ff3bcd0bc2137cef3cd5470d.png)
![image.png](https://img-blog.csdnimg.cn/img_convert/581365e4bf36a0dee90c7bee974d95ad.png)
总结一下这里做了几件事:
  1. BaseRestHandler的prepareRequest是模版方法,最终会调用到RestIndexAction里面的prepareRequest方法,里面就是负责解析restRequest请求为具体的 业务请求对象,并生成一个lambda表达式
  2. BaseRestHandler调用action.accept 调用之前的lambda表达式

TCP通信层(ES内置RPC 实现,Netty4Transport)

image.png

Netty4Transport构造方法

image.png
会调用到父类TcpTransport的构造方法

**Netty4Transport.**doStart方法

image.png
这里做了几件事:

  1. 获取netty工作组资源
  2. 创建客户端netty
  3. 根据服务端配置,创建服务端 netty
  4. 绑定端口

其中创建服务端netty比较重要,也就是这个createServerBootstrap方法

image.png
这里面做了几件事:

  1. 创建netty ServerBootStrap服务端启动对象
  2. 配置客户端通道Pipeline的初始化类型->ServerChannelInitializer, 负责客户端Channel创建之后,初始化它的Pipelien。客户端Channel最重要的Handler:Netty4MessageChannelHandler

image.png

Netty4MessageChannelHandler

构造方法

image.png
这里主要是根据构造方法参数 Transport->getRequestHandlers() 拿到全部的“请求处理器”。下一步根据“请求处理器”等其他参数创建出内部的InboundPipline实例

channelRead

image.png
总结一下做了几件事:

  1. 通过ctx读取出Channel包装对象(Netty4TcpChannel:创建客户端时PUT到channel->attr的)
  2. 调用inboundPipeline->handleBytes(netty4TcpChannel, msg) 进行处理。

InboundPipline.handleBytes(channel, reference)

image.png
image.png
主要做了几件事:

  1. 它负责处理TCP传输中的半包粘包问题处理,以及将byteBuf数据解码(Decoder)为InboundMessage,InboundMessage属于ES传输层的协议对象
  2. 解码出InboundMessage之后,交给谁呢?交给上层 TcpTransport->inboundMessage(tcpChannel, inboundMessage)

TcpTransport.inboundMessage

image.png

inboundHandler.inboundMessage

image.png

inboundHandler.messageReceived

image.png
这里代码特别长,大概逻辑如下:
通过分析header可得知 inboundMessage 是 Request 或者 Response。
分支1 requset分支:

  1. 读取header信息=>requestId和action名称
  2. 根据tcpChannel和OutBoundHandler创建出TransportChannel实例
  3. 通过inboundMessage获取content的stream(ES包装过的字节流)
  4. 通过action名称到inboundHandler#requestHandlers获取出该action请求的RequestHandlerRegistry。
  5. 调用requestHandlerRegistry->newRequest(stream) ==> RPC 业务层请求对象。将content字节流解码为具体的对象的逻辑,封装在了newRequest方法内。
  6. 调用requestHandlerRegistry->processMessageReceived(request/** 解码后的 */, transportChannel )

分支2 response 分支:

  1. 读取header信息=>requestId。该ID是谁生成的呢?当然是当前节点在向其它节点发起RPC请求时生成的啦。并且发起RPC时是不是向ResponseHandlers中注册了“响应处理器”,并且使用的key就是ID呢
  2. 根据requestId到responseHandlers中获取出“响应处理器”handler。
  3. 通过inboundMessage获取出content的stream(ES包装过的字节流)
  4. 通过“响应处理器”handler->read(stream) ==> 业务层Response实例。handler实现了Writeable.Reader接口,最终由handler控制如何通过content stream字节流创建出业务层Response实例。
  5. 调用handler.handleResponse(response/*解码后的实例/),进入到业务层逻辑



  • 6
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值