前一篇分析对nettytransport的启动及连接,本篇主要分析transport请求的发送和处理过程。cluster中各个节点之间需要相互发送很多信息,如master检测其它节点是否存在,node节点定期检测master节点是否存储,cluster状态的发布及搜索数据请求等等。为了保证信息传输,elasticsearch定义了一个19字节长度的信息头HEADER_SIZE = 2 + 4 + 8 + 1 + 4,以'E','S'开头,接着是4字节int信息长度,然后是8字节long型信息id,接着是一个字节的status,最后是4字节int型version。所有的节点间的信息都是以这19个字节开始。同时elasticsearch对于节点间的所有action都定义 了名字,如对master的周期检测action,internal:discovery/zen/fd/master_ping,每个action对应着相应的messagehandler。接下来会进行详分析。

首先看一下request的发送过程,代码在nettytransport中如下所示:

 public void sendRequest(final DiscoveryNode node, final long requestId, final String action, final TransportRequest request, TransportRequestOptions options) throws IOException, TransportException {
//参数说明:node发送的目的节点,requestId请求id,action action名称,request请求,options包括以下几种操作 RECOVERY,BULK,REG,STATE,PING;
     Channel targetChannel = nodeChannel(node, options);//获取对应节点的channel,channel在连接节点时初始化完成(请参考上一篇) if (compress) {
options.withCompress(true);
} byte status = 0;
     //设置status 包括以下几种STATUS_REQRES = 1 << 0; STATUS_ERROR = 1 << 1; STATUS_COMPRESS = 1 << 2;
    status = TransportStatus.setRequest(status);
    ReleasableBytesStreamOutput bStream = new ReleasableBytesStreamOutput(bigArrays);//初始写出流
boolean addedReleaseListener = false;
try {
bStream.skip(NettyHeader.HEADER_SIZE);//留出message header的位置
StreamOutput stream = bStream;
// only compress if asked, and, the request is not bytes, since then only
// the header part is compressed, and the "body" can't be extracted as compressed
if (options.compress() && (!(request instanceof BytesTransportRequest))) {
status = TransportStatus.setCompress(status);
stream = CompressorFactory.defaultCompressor().streamOutput(stream);
}
stream = new HandlesStreamOutput(stream); // we pick the smallest of the 2, to support both backward and forward compatibility
// note, this is the only place we need to do this, since from here on, we use the serialized version
// as the version to use also when the node receiving this request will send the response with
Version version = Version.smallest(this.version, node.version()); stream.setVersion(version);
stream.writeString(transportServiceAdapter.action(action, version)); ReleasableBytesReference bytes;
ChannelBuffer buffer;
// it might be nice to somehow generalize this optimization, maybe a smart "paged" bytes output
// that create paged channel buffers, but its tricky to know when to do it (where this option is
// more explicit).
if (request instanceof BytesTransportRequest) {
BytesTransportRequest bRequest = (BytesTransportRequest) request;
assert node.version().equals(bRequest.version());
bRequest.writeThin(stream);
stream.close();
bytes = bStream.bytes();
ChannelBuffer headerBuffer = bytes.toChannelBuffer();
ChannelBuffer contentBuffer = bRequest.bytes().toChannelBuffer();
buffer = ChannelBuffers.wrappedBuffer(NettyUtils.DEFAULT_GATHERING, headerBuffer, contentBuffer);
} else {
request.writeTo(stream);
stream.close();
bytes = bStream.bytes();
buffer = bytes.toChannelBuffer();
}
NettyHeader.writeHeader(buffer, requestId, status, version);//写信息头
ChannelFuture future = targetChannel.write(buffer);//写buffer同时获取future,发送信息发生在这里
ReleaseChannelFutureListener listener = new ReleaseChannelFutureListener(bytes);
future.addListener(listener);//添加listener
addedReleaseListener = true;
transportServiceAdapter.onRequestSent(node, requestId, action, request, options);
} finally {
if (!addedReleaseListener) {
Releasables.close(bStream.bytes());
}
}
}

以上就是request的发送过程,获取目标node的channel封装请求写入信息头,然后发送并使用listener监听,这里transportRequest是一个抽象类,它继承了TransportMessage同时实现了streamable接口。cluster中对它的实现非常多,各个功能都有相应的request,这里就不一一列举,后面的代码分析中会时常涉及。

request发送只是transport的一部分功能,有发送就要有接收,这样transport的功能才完整。接下来就是对接收过程的分析。上一篇中简单介绍过netty的使用,message的处理是通过MessageHandler处理,因此nettyTransport的信息处理逻辑都在MessageChannelHandler的messageReceived()方法中,代码如下所示:

 public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) throws Exception {
Transports.assertTransportThread();
Object m = e.getMessage();
if (!(m instanceof ChannelBuffer)) {//非buffer之间返回
ctx.sendUpstream(e);
return;
}
     //解析message头
ChannelBuffer buffer = (ChannelBuffer) m;
int size = buffer.getInt(buffer.readerIndex() - 4);
transportServiceAdapter.received(size + 6); // we have additional bytes to read, outside of the header
boolean hasMessageBytesToRead = (size - (NettyHeader.HEADER_SIZE - 6)) != 0; int markedReaderIndex = buffer.readerIndex();
int expectedIndexReader = markedReaderIndex + size; // netty always copies a buffer, either in NioWorker in its read handler, where it copies to a fresh
// buffer, or in the cumlation buffer, which is cleaned each time
StreamInput streamIn = ChannelBufferStreamInputFactory.create(buffer, size);
      //读取信息头中的几个重要元数据
long requestId = buffer.readLong();
byte status = buffer.readByte();
Version version = Version.fromId(buffer.readInt()); StreamInput wrappedStream;
      …………
if (TransportStatus.isRequest(status)) {//处理请求
String action = handleRequest(ctx.getChannel(), wrappedStream, requestId, version);
if (buffer.readerIndex() != expectedIndexReader) {
if (buffer.readerIndex() < expectedIndexReader) {
logger.warn("Message not fully read (request) for [{}] and action [{}], resetting", requestId, action);
} else {
logger.warn("Message read past expected size (request) for [{}] and action [{}], resetting", requestId, action);
}
buffer.readerIndex(expectedIndexReader);
}
} else {//处理响应
TransportResponseHandler handler = transportServiceAdapter.onResponseReceived(requestId);
// ignore if its null, the adapter logs it
if (handler != null) {
if (TransportStatus.isError(status)) {
handlerResponseError(wrappedStream, handler);
} else {
handleResponse(ctx.getChannel(), wrappedStream, handler);
}
} else {
// if its null, skip those bytes
buffer.readerIndex(markedReaderIndex + size);
}
…………
wrappedStream.close();
}

以上就是信息处理逻辑,这个方法基础自netty的SimpleChannelUpstreamHandler类。作为MessageHandler会在client和server启动时加入到handler链中,在信息到达后netty会自动调用handler链依次处理。这是netty的内容,就不详细说明,请参考netty文档。接下来看一下request和response是如何被处理的。request的处理代码如下所示:

 protected String handleRequest(Channel channel, StreamInput buffer, long requestId, Version version) throws IOException {
final String action = buffer.readString();//读出action的名字
transportServiceAdapter.onRequestReceived(requestId, action);
final NettyTransportChannel transportChannel = new NettyTransportChannel(transport, transportServiceAdapter, action, channel, requestId, version, profileName);
try {
final TransportRequestHandler handler = transportServiceAdapter.handler(action, version);//获取处理该信息的handler
if (handler == null) {
throw new ActionNotFoundTransportException(action);
}
final TransportRequest request = handler.newInstance();
request.remoteAddress(new InetSocketTransportAddress((InetSocketAddress) channel.getRemoteAddress()));
request.readFrom(buffer);
if (handler.executor() == ThreadPool.Names.SAME) {
//noinspection unchecked
handler.messageReceived(request, transportChannel);//使用该handler处理信息。
} else {
threadPool.executor(handler.executor()).execute(new RequestHandler(handler, request, transportChannel, action));
}
} catch (Throwable e) {
try {
transportChannel.sendResponse(e);
} catch (IOException e1) {
logger.warn("Failed to send error message back to client for action [" + action + "]", e);
logger.warn("Actual Exception", e1);
}
}
return action;
}

几个关键部分在代码中进行了标注。这里仍旧不能看到请求是如何处理的。因为cluster中的请求各种各样,如ping,discovery,index等等,因此不可能使用同一种处理方式。因此request最终又被提交给handler处理。每个功能请求都实现了自己的handler,当请求被提交给handler时会做对应的处理。这里再说一下transportServiceAdapter,消息的处理都是通过它适配转发完成。request的完整处理流程是:messageReceived()方法收到信息判断是request会将其转发到transportServiceAdapter的handler方法,handler方法查找对应的requesthandler,使用将信息转发给该handler进行处理。这里就不举例说明,在后面的discover分析中我们会看到发现,ping等请求的处理过程。

最后来看一下response的处理过程,response通过handleResponse方法进行处理,代码如下:

 protected void handleResponse(Channel channel, StreamInput buffer, final TransportResponseHandler handler) {
final TransportResponse response = handler.newInstance();
response.remoteAddress(new InetSocketTransportAddress((InetSocketAddress) channel.getRemoteAddress()));
response.remoteAddress();
try {
response.readFrom(buffer);
} catch (Throwable e) {
handleException(handler, new TransportSerializationException("Failed to deserialize response of type [" + response.getClass().getName() + "]", e));
return;
}
try {
if (handler.executor() == ThreadPool.Names.SAME) {
//noinspection unchecked
handler.handleResponse(response);//转发给对应的handler
} else {
threadPool.executor(handler.executor()).execute(new ResponseHandler(handler, response));
}
} catch (Throwable e) {
handleException(handler, new ResponseHandlerFailureTransportException(e));
}
}

response的处理过程跟request很类似。每个request都会对应一个handler和一个response的处理handler,会在时候的时候注册到transportService中。请求到达时根据action名称获取到handler处理request,根据requestId获取对应的response handler进行响应。

最后总结一下nettyTransport的信息处理过程:信息通过request方法发送到目标节点,目标节点的messagehandler会受到该信息,确定是request还是response,将他们分别转发给transportServiceAdapter,TransportServiceAdapter会查询到对应的handler,信息最终会被转发给对应的handler处理并反馈。对于nettyTransport信息发送的分析就到这里,在下一篇的cluster discovery分析中,我们会看到信息发送及处理的具体过程。

elasticsearch transport 请求发送和处理的更多相关文章

  1. postman 安装,对elasticsearch进行请求

    1  使用postman对elasticsearch进行测试 :下载插件: https://www.getpostman.com/apps ,下载时exe文件,双击自动安装,首次打开注册.下面就可以使 ...

  2. Elasticsearch Transport 模块创建及启动分析

    Elasticsearch 通信模块的分析从宏观上介绍了ES Transport模块总体功能,于是就很好奇ElasticSearch是怎么把服务启动起来,以接收Client发送过来的Index索引操作 ...

  3. chrome 浏览器的预提取资源机制导致的一个请求发送两次的问题以及ClientAbortException异常

    调查一个 pdf 打印报错: ExceptionConverter: org.apache.catalina.connector.ClientAbortException: java.net.Sock ...

  4. Web爬去的C#请求发送

    public class HttpControler { //post请求发送 private Encoding m_Encoding = Encoding.GetEncoding("gb2 ...

  5. 用Python做大批量请求发送

    大批量请求发送需要考虑的几个因素: 1. 服务器承载能力(网络带宽/硬件配置); 2. 客户端IO情况, 客户端带宽, 硬件配置; 方案: 1. 方案都是相对的; 2. 因为这里我的情况是客户机只有一 ...

  6. ajax请求发送和页面跳转的冲突

    http://blog.csdn.net/allenliu6/article/details/77341538?locationNum=7&fps=1 在A页面中,有一个click事件,点击时 ...

  7. org.elasticsearch.transport.ReceiveTimeoutTransportException[cluster:monitor/nodes/liveness] request_id [31] timed out after [5000ms]

    ES连接超时,异常信息 2017-09-07 10:42:45.042 [elasticsearch[Bantam][transport_client_worker][T#17]{New I/O wo ...

  8. 利用post请求发送内容进行爬虫

    利用post请求发送内容进行爬虫 import requests url = 'http://www.iqianyue.com/mypost' header = {} header['Accept-L ...

  9. 一个http请求发送到后端的详细过程

    我们来看当我们在浏览器输入http://www.mycompany.com:8080/mydir/index.html,幕后所发生的一切. 首先http是一个应用层的协议,在这个层的协议,只是一种通讯 ...

随机推荐

  1. FastDFS学习总结(1)--FastDFS安装和部署

    FastDFS是一个开源的,高性能的的分布式文件系统,他主要的功能包括:文件存储,同步和访问,设计基于高可用和负载均衡,FastDFS非常适用于基于文件服务的站点,例如图片分享和视频分享网站 Fast ...

  2. Android后台进程与前台线程间的区别使用

    博客出自:http://blog.csdn.net/liuxian13183,转载注明出处! All Rights Reserved ! 很早就翻译过Android API的一篇文章Android高级 ...

  3. python学习之--SyntaxError: Non-ASCII character '\xe5'

    在安装好eclipse之后试了一下 创建了了一个pydev project package.module 在test.py中编写最简单的命令 print "helloworld" ...

  4. Hue的三大特点、三大功能和架构

    不多说,直接上干货! Hue 是 Cloudera 的大数据 Web 工具 官方访问网站 : http://gethue.com/ GitHub : https://github.com/cloude ...

  5. 基于 Web 的 Go 语言 IDE - Wide 1.2.0 发布!

    Wide 是什么 Wide 是一个基于 Web 的 Go 语言团队 IDE. 在线开发:打开浏览器就可以进行开发.全快捷键 智能提示:代码自动完成.查看表达式.编译反馈.Lint 实时运行:极速编译. ...

  6. Linux软件万花筒

    650) this.width=650;" border="0" alt="" src="http://img1.51cto.com/att ...

  7. Python(十一) 原生爬虫

    一.分析抓取目的确定抓取页面   #爬取主播人气排行 二.整理爬虫常规思路   爬虫前奏 明确目的 找到数据对应的网页 分析网页的结构找到数据所在的标签位置 模拟 HTTP 请求, 向服务器发送这个请 ...

  8. shell中IF的用法介绍

    一.语法结构 if [ condition ] then      statements  [elif condition      then statements. ..]  [else       ...

  9. mysql 问题排查语句

    1.查询不是sleep或者有状态的sql select * from `information_schema`.processlist where command !='Sleep' or state ...

  10. Vert.x,一个异步、可伸缩、并发应用框架引发的思考

    2012年听说过Vert.x这个框架之后,去年大致了解了下,最近开始进一步熟悉这个框架. Vert.x是一个用于下一代异步.可伸缩.并发应用的框架,旨在为JVM提供一个Node.js的替代方案.开发者 ...