我們知道elasticsearch主要有兩種通信方式: Http和Transport。第二篇主要分析了通過Transport發送請求的處理方式。本篇分析elasticsearch如何處理http請求的。同樣以Netty3HttpServerTranport爲例。
Netty3HttpServerTransport類
public class Netty3HttpTranport{
protected void doStart() {
boolean success = false;
try {
this.serverOpenChannels = new Netty3OpenChannelsHandler(logger);
if (blockingServer) {
serverBootstrap = new ServerBootstrap(new OioServerSocketChannelFactory(
Executors.newCachedThreadPool(daemonThreadFactory(settings, HTTP_SERVER_BOSS_THREAD_NAME_PREFIX)),
Executors.newCachedThreadPool(daemonThreadFactory(settings, HTTP_SERVER_WORKER_THREAD_NAME_PREFIX))
));
} else {
serverBootstrap = new ServerBootstrap(new NioServerSocketChannelFactory(
Executors.newCachedThreadPool(daemonThreadFactory(settings, HTTP_SERVER_BOSS_THREAD_NAME_PREFIX)),
Executors.newCachedThreadPool(daemonThreadFactory(settings, HTTP_SERVER_WORKER_THREAD_NAME_PREFIX)),
workerCount));
}
serverBootstrap.setPipelineFactory(configureServerChannelPipelineFactory());
serverBootstrap.setOption("child.tcpNoDelay", tcpNoDelay);
serverBootstrap.setOption("child.keepAlive", tcpKeepAlive);
if (tcpSendBufferSize.getBytes() > 0) {
serverBootstrap.setOption("child.sendBufferSize", tcpSendBufferSize.getBytes());
}
if (tcpReceiveBufferSize.getBytes() > 0) {
serverBootstrap.setOption("child.receiveBufferSize", tcpReceiveBufferSize.getBytes());
}
serverBootstrap.setOption("receiveBufferSizePredictorFactory", receiveBufferSizePredictorFactory);
serverBootstrap.setOption("child.receiveBufferSizePredictorFactory", receiveBufferSizePredictorFactory);
serverBootstrap.setOption("reuseAddress", reuseAddress);
serverBootstrap.setOption("child.reuseAddress", reuseAddress);
this.boundAddress = createBoundHttpAddress();
success = true;
} finally {
if (success == false) {
doStop(); // otherwise we leak threads since we never moved to started
}
}
}
protected void dispatchRequest(RestRequest request, RestChannel channel) {
httpServerAdapter.dispatchRequest(request, channel, threadPool.getThreadContext());
}
public ChannelPipelineFactory configureServerChannelPipelineFactory() {
return new HttpChannelPipelineFactory(this, detailedErrorsEnabled, threadPool.getThreadContext());
}
}
doStart爲Netty3HttpServerTransport對象的程序運行入口。我們很明顯的可以看到用了netty的ServerBootstrap,重點的一行代碼是serverBootstrap.setPipelineFactory(configureServerChannelPipelineFactory), 這裏是設置netty的pipline參數,configureServerChannelPipelineFactory方法很簡單,就是在內部初始化了一個HttpChannelPipelineFactory對象,我們向下探究HttpChannelPipelineFactory類。
HttpChannelPiplineFactory類
protected static class HttpChannelPipelineFactory implements ChannelPipelineFactory { protected final Netty3HttpServerTransport transport; protected final Netty3HttpRequestHandler requestHandler; public HttpChannelPipelineFactory(Netty3HttpServerTransport transport, boolean detailedErrorsEnabled, ThreadContext threadContext) { this.transport = transport; this.requestHandler = new Netty3HttpRequestHandler(transport, detailedErrorsEnabled, threadContext); } @Override public ChannelPipeline getPipeline() throws Exception { ChannelPipeline pipeline = Channels.pipeline(); pipeline.addLast("openChannels", transport.serverOpenChannels); HttpRequestDecoder requestDecoder = new HttpRequestDecoder( (int) transport.maxInitialLineLength.getBytes(), (int) transport.maxHeaderSize.getBytes(), (int) transport.maxChunkSize.getBytes() ); if (transport.maxCumulationBufferCapacity.getBytes() >= 0) { if (transport.maxCumulationBufferCapacity.getBytes() > Integer.MAX_VALUE) { requestDecoder.setMaxCumulationBufferCapacity(Integer.MAX_VALUE); } else { requestDecoder.setMaxCumulationBufferCapacity((int) transport.maxCumulationBufferCapacity.getBytes()); } } if (transport.maxCompositeBufferComponents != -1) { requestDecoder.setMaxCumulationBufferComponents(transport.maxCompositeBufferComponents); } pipeline.addLast("decoder", requestDecoder); pipeline.addLast("decoder_compress", new HttpContentDecompressor()); HttpChunkAggregator httpChunkAggregator = new HttpChunkAggregator((int) transport.maxContentLength.getBytes()); if (transport.maxCompositeBufferComponents != -1) { httpChunkAggregator.setMaxCumulationBufferComponents(transport.maxCompositeBufferComponents); } pipeline.addLast("aggregator", httpChunkAggregator); pipeline.addLast("encoder", new ESNetty3HttpResponseEncoder()); if (transport.compression) { pipeline.addLast("encoder_compress", new HttpContentCompressor(transport.compressionLevel)); } if (SETTING_CORS_ENABLED.get(transport.settings())) { pipeline.addLast("cors", new Netty3CorsHandler(transport.getCorsConfig())); } if (transport.pipelining) { pipeline.addLast("pipelining", new HttpPipeliningHandler(transport.pipeliningMaxEvents)); } pipeline.addLast("handler", requestHandler); return pipeline; } }
在HttpChannelPipelineFactory類裏,我們觀察getPipeline()方法,該方法裏爲所有的request配置了一系列的處理方法,我們重點關注倒數第二行代碼 - pipline.addLast("handler", requestHandler), 其中requestHandler爲請求的最終處理對象。它屬於Netty3HttpRequestHandler類。
public class Netty3HttpRequestHandler extends SimpleChannelUpstreamHandler{
private final Netty3HttpServerTransport serverTransport;
private final boolean httpPipeliningEnabled;
private final boolean detailedErrorsEnabled;
private final ThreadContext threadContext;
@Override
public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) throws Exception {
HttpRequest request;
OrderedUpstreamMessageEvent oue = null;
if (this.httpPipeliningEnabled && e instanceof OrderedUpstreamMessageEvent) {
oue = (OrderedUpstreamMessageEvent) e;
request = (HttpRequest) oue.getMessage();
} else {
request = (HttpRequest) e.getMessage();
}
// the netty HTTP handling always copy over the buffer to its own buffer, either in NioWorker internally
// when reading, or using a cumulation buffer
Netty3HttpRequest httpRequest = new Netty3HttpRequest(serverTransport.xContentRegistry, request, e.getChannel());
Netty3HttpChannel channel = new Netty3HttpChannel(serverTransport, httpRequest, oue, detailedErrorsEnabled, threadContext);
serverTransport.dispatchRequest(httpRequest, channel);
super.messageReceived(ctx, e);
}
...
}
我們可以看到該類繼承了SimpleChannelUpstreamHandler, 請求最終是由messageReceived方法來進行處理。同樣在倒數第二行代碼,通過Netty3HttpServerTranport“派發”請求。回到Netty3HttpServerTransport類,可以看到dispatchRequest方法,該方法內部調用HttpServerAdapter類進行派發,它是一個接口,只有一個實現類即爲HttpServer.
HttpServer類
public class HttpServer extends AbstractLifecycleComponent implements HttpServerAdapter {
public void dispatchRequest(RestRequest request, RestChannel channel, ThreadContext threadContext) {
if (request.rawPath().equals("/favicon.ico")) {
handleFavicon(request, channel);
return;
}
RestChannel responseChannel = channel;
try {
int contentLength = request.content().length();
if (restController.canTripCircuitBreaker(request)) {
inFlightRequestsBreaker(circuitBreakerService).addEstimateBytesAndMaybeBreak(contentLength, "<http_request>");
} else {
inFlightRequestsBreaker(circuitBreakerService).addWithoutBreaking(contentLength);
}
// iff we could reserve bytes for the request we need to send the response also over this channel
responseChannel = new ResourceHandlingHttpChannel(channel, circuitBreakerService, contentLength);
restController.dispatchRequest(request, responseChannel, client, threadContext);
} catch (Exception e) {
try {
responseChannel.sendResponse(new BytesRestResponse(channel, e));
} catch (Exception inner) {
inner.addSuppressed(e);
logger.error((Supplier<?>) () ->
new ParameterizedMessage("failed to send failure response for uri [{}]", request.uri()), inner);
}
}
}
...
}
觀察該方法,在try代碼塊裏可以看到最終是由RestController來派發請求。我們繼續往下探究。
RestController類
public class RestController extends AbstractComponent {
public void dispatchRequest(final RestRequest request, final RestChannel channel, final NodeClient client, ThreadContext threadContext) throws Exception {
if (!checkRequestParameters(request, channel)) {
return;
}
try (ThreadContext.StoredContext ignored = threadContext.stashContext()) {
for (String key : headersToCopy) {
String httpHeader = request.header(key);
if (httpHeader != null) {
threadContext.putHeader(key, httpHeader);
}
}
final RestHandler handler = getHandler(request);
if (handler == null) {
if (request.method() == RestRequest.Method.OPTIONS) {
// when we have OPTIONS request, simply send OK by default (with the Access Control Origin header which gets automatically added)
channel.sendResponse(new BytesRestResponse(OK, BytesRestResponse.TEXT_CONTENT_TYPE, BytesArray.EMPTY));
} else {
final String msg = "No handler found for uri [" + request.uri() + "] and method [" + request.method() + "]";
channel.sendResponse(new BytesRestResponse(BAD_REQUEST, msg));
}
} else {
final RestHandler wrappedHandler = Objects.requireNonNull(handlerWrapper.apply(handler));
wrappedHandler.handleRequest(request, channel, client);
}
}
}
...
}
進入RestController類的dispatchRequest,可以發現對每個request獲取相關的RestHandler來進行處理,如果RestHandler不爲空的話,那麼調用handlerRequest方法,該方法即爲最終request的請求處理方法。我們進入RestHandler, 可以發現其爲一個藉口,有兩個具體的實現子類。DeprecationRestHandler爲RestHandler提供代理,方便打印log。真正的子類實現是在BaseHandler裏。
BaseRestHandler類
/**
* Base handler for REST requests.
* <p>
* This handler makes sure that the headers & context of the handled {@link RestRequest requests} are copied over to
* the transport requests executed by the associated client. While the context is fully copied over, not all the headers
* are copied, but a selected few. It is possible to control what headers are copied over by returning them in
* {@link ActionPlugin#getRestHeaders()}.
*/
public abstract class BaseRestHandler extends AbstractComponent implements RestHandler {
@Override
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);
}
...
}
BaseHandler.dispatchRequest()方法的最後一行代碼,可以發現最終執行相關操作,action是一個函數式接口,我們看看它有哪些具體實現。
至此,我們可以大致瞭解,elasticsearch爲每類通過rest發起的操作都會配對一個對應的處理方式。這裏我們同樣跟蹤Index操作,選擇RestIndexAction類。我們點擊上面圖片的裏RestIndexAction, 最終定位到prepareRequest的return 語句,也就是說最終返回的就是RestChannelConsumer。觀察(channel->client.index),我們可以發現最終執行client.index方法,這裏的client是傳入的參數值NodeClient對象。
public class RestIndexAction extends BaseRestHandler {
@Inject
public RestIndexAction(Settings settings, RestController controller) {
super(settings);
controller.registerHandler(POST, "/{index}/{type}", this); // auto id creation
controller.registerHandler(PUT, "/{index}/{type}/{id}", this);
controller.registerHandler(POST, "/{index}/{type}/{id}", this);
CreateHandler createHandler = new CreateHandler(settings);
controller.registerHandler(PUT, "/{index}/{type}/{id}/_create", createHandler);
controller.registerHandler(POST, "/{index}/{type}/{id}/_create", createHandler);
}
final class CreateHandler extends BaseRestHandler {
protected CreateHandler(Settings settings) {
super(settings);
}
@Override
public RestChannelConsumer prepareRequest(RestRequest request, final NodeClient client) throws IOException {
request.params().put("op_type", "create");
return RestIndexAction.this.prepareRequest(request, client);
}
}
@Override
public RestChannelConsumer prepareRequest(final RestRequest request, final NodeClient client) throws IOException {
IndexRequest indexRequest = new IndexRequest(request.param("index"), request.param("type"), request.param("id"));
indexRequest.routing(request.param("routing"));
indexRequest.parent(request.param("parent")); // order is important, set it after routing, so it will set the routing
if (request.hasParam("timestamp")) {
deprecationLogger.deprecated("The [timestamp] parameter of index requests is deprecated");
}
indexRequest.timestamp(request.param("timestamp"));
if (request.hasParam("ttl")) {
deprecationLogger.deprecated("The [ttl] parameter of index requests is deprecated");
indexRequest.ttl(request.param("ttl"));
}
indexRequest.setPipeline(request.param("pipeline"));
indexRequest.source(request.content());
indexRequest.timeout(request.paramAsTime("timeout", IndexRequest.DEFAULT_TIMEOUT));
indexRequest.setRefreshPolicy(request.param("refresh"));
indexRequest.version(RestActions.parseVersion(request));
indexRequest.versionType(VersionType.fromString(request.param("version_type"), indexRequest.versionType()));
String sOpType = request.param("op_type");
String waitForActiveShards = request.param("wait_for_active_shards");
if (waitForActiveShards != null) {
indexRequest.waitForActiveShards(ActiveShardCount.parseString(waitForActiveShards));
}
if (sOpType != null) {
indexRequest.opType(IndexRequest.OpType.fromString(sOpType));
}
return channel ->
client.index(indexRequest, new RestStatusToXContentListener<>(channel, r -> {
try {
return r.getLocation(indexRequest.routing());
} catch (URISyntaxException ex) {
logger.warn("Location string is not a valid URI.", ex);
return null;
}
}));
}
}
繼續往下走,我們可以發現一個方法調用鏈 index->execute -> doExecute, doExecute方法是一個抽象方法,由子類實現,我們在上面已經知道client類型爲NodeClient。一次我們進入NodeClient類裏。
AbstractClient類
public abstract class AbstractClient extends AbstractComponent implements Client {
...
@Override
public void index(final IndexRequest request, final ActionListener<IndexResponse> listener) {
execute(IndexAction.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);
}
protected abstract <Request extends ActionRequest, Response extends ActionResponse, RequestBuilder extends ActionRequestBuilder<Request, Response, RequestBuilder>> void doExecute(final Action<Request, Response, RequestBuilder> action, final Request request, ActionListener<Response> listener);
...
}
NodeClient類
/**
* Client that executes actions on the local node.
*/
public class NodeClient extends AbstractClient {
private Map<GenericAction, TransportAction> actions;
public NodeClient(Settings settings, ThreadPool threadPool) {
super(settings, threadPool);
}
public void initialize(Map<GenericAction, TransportAction> actions) {
this.actions = actions;
}
@Override
public void close() {
// nothing really to do
}
@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);
}
/**
* Execute an {@link Action} locally, returning that {@link Task} used to track it, and linking an {@link ActionListener}. Prefer this
* method if you don't need access to the task when listening for the response. This is the method used to implement the {@link Client}
* interface.
*/
public <Request extends ActionRequest,
Response extends ActionResponse
> Task executeLocally(GenericAction<Request, Response> action, Request request, ActionListener<Response> listener) {
return transportAction(action).execute(request, listener);
}
/**
* Execute an {@link Action} locally, returning that {@link Task} used to track it, and linking an {@link TaskListener}. Prefer this
* method if you need access to the task when listening for the response.
*/
public <Request extends ActionRequest,
Response extends ActionResponse
> Task executeLocally(GenericAction<Request, Response> action, Request request, TaskListener<Response> listener) {
return transportAction(action).execute(request, listener);
}
/**
* Get the {@link TransportAction} for an {@link Action}, throwing exceptions if the action isn't available.
*/
@SuppressWarnings("unchecked")
private <Request extends ActionRequest,
Response extends ActionResponse
> TransportAction<Request, Response> transportAction(GenericAction<Request, Response> action) {
if (actions == null) {
throw new IllegalStateException("NodeClient has not been initialized");
}
TransportAction<Request, Response> transportAction = actions.get(action);
if (transportAction == null) {
throw new IllegalStateException("failed to find action [" + action + "] to execute");
}
return transportAction;
}
}
在NodeClient類的doExecute的方法調用鏈 doExecute->executeLocally->transportAction。我們仔細觀察transportAction,裏面有行代碼actiotns.get,這和在上篇裏的TransportProxyClient類初始化時,爲每個action生成對應的TransportActionNodeProxy有點相似,只不過這裏生成的TransportAction。我們繼續,進入TransportAction類。
TransportAction類
public abstract class TransportAction<Request extends ActionRequest, Response extends ActionResponse> extends AbstractComponent {
public final Task execute(Request request, ActionListener<Response> listener) {
/*
* While this version of execute could delegate to the TaskListener
* version of execute that'd add yet another layer of wrapping on the
* listener and prevent us from using the listener bare if there isn't a
* task. That just seems like too many objects. Thus the two versions of
* this method.
*/
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;
}
...
}
看到該TransportAction的execute方法,我們可以發現這個上篇裏Transport方式殊途同歸了,也就是不論是通過Http方式還是Transport方式,所有的請求只是在真正處理前的預處理方式不同,但是最終真正的請求處理方法都是一樣。
總結
不論Http方式和Transport方式,請求處理方式是一樣的,但是從請求發起到請求真正開始被處理這一段邏輯是不同,暫且稱之爲“預處理”,兩種請求方法的預處理有自己對應的邏輯處理方式。