#简介
阅读ES源码首先需要了解http请求如何转发到具体的实现类中,否则很可能本各种莫名其妙的跳转搞晕。ES 利用Netty框架处理http请求。在ES2.x中用的netty3版本,在5.X中默认使用netty4版本,当然可通过如下启动方式切换到netty3版本:
./bin/elasticsearch -Ehttp.type=netty3
在看源码之前先介绍一下netty的基本概念。
#netty基本介绍
在Netty里,Channel是通讯的载体,而ChannelHandler负责Channel中的逻辑处理。
那么ChannelPipeline是什么呢?我觉得可以理解为ChannelHandler的容器:一个Channel包含一个ChannelPipeline,所有ChannelHandler都会注册到ChannelPipeline中,并按顺序组织起来。
在Netty中,ChannelEvent是数据或者状态的载体,例如传输的数据对应MessageEvent,状态的改变对应ChannelStateEvent。当对Channel进行操作时,会产生一个ChannelEvent,并发送到ChannelPipeline。ChannelPipeline会选择一个ChannelHandler进行处理。这个ChannelHandler处理之后,可能会产生新的ChannelEvent,并流转到下一个ChannelHandler。
例如,一个数据最开始是一个MessageEvent,它附带了一个未解码的原始二进制消息ChannelBuffer,然后某个Handler将其解码成了一个数据对象,并生成了一个新的MessageEvent,并传递给下一步进行处理。
到了这里,可以看到,其实Channel的核心流程位于ChannelPipeline中。
netty部分参考Netty那点事(三)Channel中的Pipeline
#代码走读
####netty的启动
netty的启动是在ES的启动代码中调用的,这里对于ES的启动不做重点解析,仅提供如下代码跳转路径,快速从ES入口代码定位到netty的启动代码:
Elasticsearch::main(final String[] args) -> int status = main(args, elasticsearch, Terminal.DEFAULT);
Elasticsearch::main(final String[] args, final Elasticsearch elasticsearch, final Terminal terminal) -> return elasticsearch.main(args, terminal);
Command::main(String[] args, Terminal terminal) -> mainWithoutErrorHandling(args, terminal);
Command::mainWithoutErrorHandling(String[] args, Terminal terminal) -> execute(terminal, options);
EnvironmentAwareCommand::execute(Terminal terminal, OptionSet options) -> execute(terminal, options, createEnv(terminal, settings));
Elasticsearch::execute(Terminal terminal, OptionSet options, Environment env) -> init(daemonize, pidFile, quiet, env);
Elasticsearch::init(final boolean daemonize, final Path pidFile, final boolean quiet, Environment initialEnv) -> Bootstrap.init(!daemonize, pidFile, quiet, initialEnv);
Bootstrap::init(final boolean foreground, final Path pidFile, final boolean quiet, final Environment initialEnv) -> INSTANCE.start();
Bootstrap::void start() -> node.start();
Node::Node start() -> injector.getInstance(HttpServerTransport.class).start();
AbstractLifecycleComponent::void start() -> doStart();
Netty4HttpServerTransport::void doStart()
serverBootstrap = new ServerBootstrap();
if (blockingServer) {
serverBootstrap.group(new OioEventLoopGroup(workerCount, daemonThreadFactory(settings,
HTTP_SERVER_WORKER_THREAD_NAME_PREFIX)));
serverBootstrap.channel(OioServerSocketChannel.class);
} else {
serverBootstrap.group(new NioEventLoopGroup(workerCount, daemonThreadFactory(settings,
HTTP_SERVER_WORKER_THREAD_NAME_PREFIX)));
serverBootstrap.channel(NioServerSocketChannel.class);
}
serverBootstrap.childHandler(configureServerChannelHandler());
serverBootstrap.channel 创建了channel,serverBootstrap.childHandler(configureServerChannelHandler()); 设置了channelhandler。
public ChannelHandler configureServerChannelHandler() {
return new HttpChannelHandler(this, detailedErrorsEnabled, threadPool.getThreadContext());
}
protected static class HttpChannelHandler extends ChannelInitializer<Channel> {
private final Netty4HttpServerTransport transport;
private final Netty4HttpRequestHandler requestHandler;
protected HttpChannelHandler(
final Netty4HttpServerTransport transport,
final boolean detailedErrorsEnabled,
final ThreadContext threadContext) {
this.transport = transport;
this.requestHandler = new Netty4HttpRequestHandler(transport, detailedErrorsEnabled, threadContext);
}
Netty4HttpRequestHandler将用于处理http请求。
protected void initChannel(Channel ch) throws Exception {
...
ch.pipeline().addLast("handler", requestHandler);
}
将http请求处理handler加入netty的pipeline中。http请求将会由Netty4HttpRequestHandler的channelRead0接收
protected void channelRead0(ChannelHandlerContext ctx, Object msg) throws Exception {
...
if (request.decoderResult().isSuccess()) {
serverTransport.dispatchRequest(httpRequest, channel);
} else {
assert request.decoderResult().isFailure();
serverTransport.dispatchBadRequest(httpRequest, channel, request.decoderResult().cause());
}
}
接收到的请求被Netty4HttpServerTransport::dispatchRequest分发
void dispatchRequest(final RestRequest request, final RestChannel channel) {
final ThreadContext threadContext = threadPool.getThreadContext();
try (ThreadContext.StoredContext ignore = threadContext.stashContext()) {
dispatcher.dispatchRequest(request, channel, threadContext);
}
}
接着被RestController::dispatchRequest 分发
public void dispatchRequest(RestRequest request, RestChannel channel, ThreadContext threadContext) {
...
dispatchRequest(request, responseChannel, client, threadContext, handler);
}
} catch (Exception e) {
... }
}
void dispatchRequest(final RestRequest request, final RestChannel channel, final NodeClient client, ThreadContext threadContext,
final RestHandler handler) throws Exception {
...
if (handler == null) {
...
} else {
final RestHandler wrappedHandler = Objects.requireNonNull(handlerWrapper.apply(handler));
wrappedHandler.handleRequest(request, channel, client);
}
}
}
BaseRestHandler::wrappedHandler.handleRequest 继续处理请求
public final void handleRequest(RestRequest request, RestChannel channel, NodeClient client) throws Exception {
// prepare the request for execution; has the side effect of touching the request parameters
final RestChannelConsumer action = prepareRequest(request, client);
// validate unconsumed params, but we must exclude params used to format the response
// use a sorted set so the unconsumed parameters appear in a reliable sorted order
final SortedSet<String> unconsumedParams =
request.unconsumedParams().stream().filter(p -> !responseParams().contains(p)).collect(Collectors.toCollection(TreeSet::new));
// validate the non-response params
if (!unconsumedParams.isEmpty()) {
final Set<String> candidateParams = new HashSet<>();
candidateParams.addAll(request.consumedParams());
candidateParams.addAll(responseParams());
throw new IllegalArgumentException(unrecognized(request, unconsumedParams, candidateParams, "parameter"));
}
// execute the action
action.accept(channel);
}
final RestChannelConsumer action = prepareRequest(request, client); 会根据具体的请求类型生产对应的action,例如一个bulk请求,它的实现代码如下:
@Override
public RestChannelConsumer prepareRequest(final RestRequest request, final NodeClient client) throws IOException {
BulkRequest bulkRequest = Requests.bulkRequest();
String defaultIndex = request.param("index");
String defaultType = request.param("type");
String defaultRouting = request.param("routing");
FetchSourceContext defaultFetchSourceContext = FetchSourceContext.parseFromRestRequest(request);
String fieldsParam = request.param("fields");
if (fieldsParam != null) {
DEPRECATION_LOGGER.deprecated("Deprecated field [fields] used, expected [_source] instead");
}
String[] defaultFields = fieldsParam != null ? Strings.commaDelimitedListToStringArray(fieldsParam) : null;
String defaultPipeline = request.param("pipeline");
String waitForActiveShards = request.param("wait_for_active_shards");
if (waitForActiveShards != null) {
bulkRequest.waitForActiveShards(ActiveShardCount.parseString(waitForActiveShards));
}
bulkRequest.timeout(request.paramAsTime("timeout", BulkShardRequest.DEFAULT_TIMEOUT));
bulkRequest.setRefreshPolicy(request.param("refresh"));
bulkRequest.add(request.requiredContent(), defaultIndex, defaultType, defaultRouting, defaultFields,
defaultFetchSourceContext, defaultPipeline, null, allowExplicitIndex, request.getXContentType());
return channel -> client.bulk(bulkRequest, new RestStatusToXContentListener<>(channel));
}
解析出请求中的index,type,routing等信息,重构出builRequest,然后调用AbstractClient::client.bulk
@Override
public void bulk(final BulkRequest request, final ActionListener<BulkResponse> listener) {
execute(BulkAction.INSTANCE, request, listener);
}
@Override
public final <Request extends ActionRequest, Response extends ActionResponse, RequestBuilder extends ActionRequestBuilder<Request, Response, RequestBuilder>> void execute(
Action<Request, Response, RequestBuilder> action, Request request, ActionListener<Response> listener) {
listener = threadedWrapper.wrap(listener);
doExecute(action, request, listener);
}
调用客户的节点NodeClient::doExcecute
@Override
public < Request extends ActionRequest,
Response extends ActionResponse,
RequestBuilder extends ActionRequestBuilder<Request, Response, RequestBuilder>
> void doExecute(Action<Request, Response, RequestBuilder> action, Request request, ActionListener<Response> listener) {
// Discard the task because the Client interface doesn't use it.
executeLocally(action, request, listener);
}
public < Request extends ActionRequest,
Response extends ActionResponse
> Task executeLocally(GenericAction<Request, Response> action, Request request, ActionListener<Response> listener) {
return transportAction(action).execute(request, listener);
}
调用TransportAction的execute
Task task = taskManager.register("transport", actionName, request);
if (task == null) {
execute(null, request, listener);
} else {
execute(task, request, new ActionListener<Response>() {
@Override
public void onResponse(Response response) {
taskManager.unregister(task);
listener.onResponse(response);
}
@Override
public void onFailure(Exception e) {
taskManager.unregister(task);
listener.onFailure(e);
}
});
}
return task;
}
public final void execute(Task task, Request request, ActionListener<Response> listener) {
ActionRequestValidationException validationException = request.validate();
if (validationException != null) {
listener.onFailure(validationException);
return;
}
if (task != null && request.getShouldStoreResult()) {
listener = new TaskResultStoringActionListener<>(taskManager, task, listener);
}
RequestFilterChain<Request, Response> requestFilterChain = new RequestFilterChain<>(this, logger);
requestFilterChain.proceed(task, actionName, request, listener);
}
@Override
public void proceed(Task task, String actionName, Request request, ActionListener<Response> listener) {
int i = index.getAndIncrement();
try {
if (i < this.action.filters.length) {
this.action.filters[i].apply(task, actionName, request, listener, this);
} else if (i == this.action.filters.length) {
this.action.doExecute(task, request, listener);
} else {
listener.onFailure(new IllegalStateException("proceed was called too many times"));
}
} catch(Exception e) {
logger.trace("Error during transport action execution.", e);
listener.onFailure(e);
}
}
this.action.doExecute(task, request, listener); 实际调用的是 TransportBulkAction::doExecute,那么怎么从action对应出TransportBulkAction的呢?
ActionModule::setupActions已经建立起对应关系
actions.register(BulkAction.INSTANCE, TransportBulkAction.class,
TransportShardBulkAction.class);