1. 程式人生 > >zuul 2.1.2 原始碼 (TBC)

zuul 2.1.2 原始碼 (TBC)

zuul 2.1.2 比zuul 1效能提高了4倍。

Handler

在 ZuulServerChannelInitializer 中初始化:

IdleStateHandler
CloseOnIdleStateHandler
... passport
PassportStateServerHandler
... tcp
SourceAddressChannelHandler
ServerChannelMetrics
PerEventLoopMetricsChannelHandler.Connections
ElbProxyProtocolChannelHandler(如果建構函式入參為false不執行此handler)
MaxInboundConnectionsHandler(屬於throttle,如果建構函式入參為0
,不執行此handler) ... http1 HttpServerCodec屬於codec, netty自帶 Http1ConnectionCloseHandler (ChannelDuplexHandler) Http1ConnectionExpiryHandler (outbound) ... http PassportStateHttpServerHandler HttpRequestReadTimeoutHandler(如果設定了httpRequestReadTimeout不為-1) HttpServerLifecycleChannelHandler HttpBodySizeRecordingChannelHandler HttpMetricsChannelHandler PerEventLoopMetricsChannelHandler.HttpRequests AccessLogChannelHandler ServerStatusHeaderHandler StripUntrustedProxyHeadersHandler ...
zuul handlers 有個rateLimitingChannelHandlerProvider的properties配置,不知道是哪個ChannelHandler? LoggingHandler ClientRequestReceiver PassportLoggingHandler ZuulFilterChainHandler 初始化一個Endpoint(endpoint指的是後面的服務),EndpointFilter初始化clientManager,是netty到後端服務的netty連線。 ClientResponseWriter

後端服務的連線 (endpoint)

每個Endpoint是一個Filter。
ProxyEndpoint 每個請求對應一個例項,但是重傳請求是相同的例項,因為有成員變數的狀態需要變更,比如重傳次數等。

客戶端

客戶端的管理在BasicNettyOrigin類中,它的建構函式初始化DefaultClientChannelManager,DefaultClientChannelManager建構函式初始化ConnectionPoolConfigImpl,它包含了Bootstrap的option的值,DefaultClientChannelManager還初始化ConcurrentHashMap<Server, PerServerConnectionPool> perServerPools物件,它的值PerServerConnectionPool含有到該Server的所有連線。此外DefaultClientChannelManager還有各種計數器和計時器。

BasicNettyOrigin 建構函式呼叫DefaultClientChannelManager的init方法,初始化netty的DefaultOriginChannelInitializer類。
handler:

PassportStateOriginHandler(duplex)
SslHandler (如果要ssl的話)
HttpClientCodec (duplex)
PassportStateHttpClientHandler (duplex)
LoggingHandler (duplex, netty)
HttpMetricsChannelHandler (inbound)
HttpClientLifecycleChannelHandler (duplex)
ConnectionPoolHandler (duplex)

當傳送請求時:

ZuulEndPointRunner(BaseZuulFilterRunner)執行filter方法:

final HttpResponseMessage zuulResp = filter(endpoint, zuulReq);
syncFilter.apply(inMesg); // syncFilter即ProxyEndpoint

ProxyEndpoint執行proxyRequestToOrigin方法

private void proxyRequestToOrigin() {
        Promise<PooledConnection> promise = null;
        try {
            attemptNum += 1;
            requestStat = createRequestStat();
            origin.preRequestChecks(zuulRequest);
            concurrentReqCount++;
            promise = origin.connectToOrigin(zuulRequest, channelCtx.channel().eventLoop(), attemptNum, passport, chosenServer);

            logOriginServerIpAddr();
            currentRequestAttempt = origin.newRequestAttempt(chosenServer.get(), context, attemptNum);
            requestAttempts.add(currentRequestAttempt);
            passport.add(PassportState.ORIGIN_CONN_ACQUIRE_START);

            if (promise.isDone()) {
                operationComplete(promise);
            } else {
                promise.addListener(this);
            }
        }
        catch (Exception ex) {
            LOG.error("Error while connecting to origin, UUID {} " + context.getUUID(), ex);
            logOriginServerIpAddr();
            if (promise != null && ! promise.isDone()) {
                promise.setFailure(ex);
            } else {
                errorFromOrigin(ex);
            }
        }
    }

上面的程式碼可總結成三步:
1)BasicNettyOrigin 的connectToOrigin方法獲取連線
2)ProxyEndpoint的operationComplete方法,呼叫自身的onOriginConnectSucceeded方法
3)onOriginConnectSucceeded呼叫自身的writeClientRequestToOrigin方法傳送資料:

private void writeClientRequestToOrigin(final PooledConnection conn) {
        final Channel ch = conn.getChannel(); // ch 客戶端channel
        passport.setOnChannel(ch);

        context.set("_origin_channel", ch);
        context.set(POOLED_ORIGIN_CONNECTION_KEY, conn);

        // empty body 
        preWriteToOrigin(chosenServer.get(), context); // 空的

        final ChannelPipeline pipeline = ch.pipeline();
        originResponseReceiver = getOriginResponseReceiver(); // 獲取client inbound handler,用new 的方式,並且OriginResponseReceiver建構函式傳入ProxyEndpoint,即netty server的ctx。用這個ctx將後端服務發來的報傳送到server。
        pipeline.addBefore("connectionPoolHandler", OriginResponseReceiver.CHANNEL_HANDLER_NAME, originResponseReceiver);

        // check if body needs to be repopulated for retry
        repopulateRetryBody();

        ch.write(zuulRequest);
        writeBufferedBodyContent(zuulRequest, ch);
        ch.flush();

        //Get ready to read origin's response
        ch.read();

        originConn = conn;
        channelCtx.read(); // 從伺服器的channel讀取當前請求餘下的資料
    }

至此完成將zuul的netty server的資料傳送到netty client。

接收後端服務的響應併發給前端

上面的函式中:

originResponseReceiver = getOriginResponseReceiver();
        pipeline.addBefore("connectionPoolHandler", OriginResponseReceiver.CHANNEL_HANDLER_NAME, originResponseReceiver);

將OriginResponseReceiver,一個DuplexHandler動態載入到client的pipeline中。它的建構函式需要ProxyEndpoint,用來寫入到server。

TO BO CONTINUE