1. 程式人生 > >Apache、Zuul進行轉發後報錯問題的解決

Apache、Zuul進行轉發後報錯問題的解決

今天用Zuul寫了一個filter進行地址轉換,邏輯比較簡單,前端請求通過Zuul的filter,把前端的token反解為員工號,加入一些特定的引數後,遞交到第三方服務端。大致花了一個小時寫了一個filter,本機測試良好,但是放到測試環境測試,服務端返回500,未返回具體的錯誤message。由於服務端不是我們維護開發的,檢視具體500報錯資訊很難,所以只能進行本地除錯。

系統架構:

整個架構很簡單,外網請求通過Apache反向代理到內網的Nginx,再請求Zuul,Zuul通過一系列轉換後請求第三方系統。本地除錯一切正常,但是在模擬環境部署後,發現問題:通過瀏覽器直接訪問Zuul,返回正常;訪問Nginx,返回正常;但是訪問Apache,返回500。

第一個反應就是找所有請求的差異。從架構和列印的除錯資訊分析,通過apache訪問,請求已經正確到達Zuul應用。所以首先要找出通過nginx正常訪問和通過apache不正常訪問,在zuul伺服器上的差異。

一次GET請求,就包含了請求URL、請求引數和請求頭,首先我們在zuul上加個filter,把這些資訊打出來。

filter的Order放在原來寫的Filter之後。

具體程式碼:

package com.xxx.portalZuul;

import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import org.springframework.cloud.netflix.zuul.filters.ProxyRequestHelper;
import org.springframework.stereotype.Component;
import org.springframework.util.MultiValueMap;

import javax.servlet.http.HttpServletRequest;

/**
 * @author : xxx
 * @version :
 * @date : Created in 18:26 2018/8/2
 * @description:
 * @modified By:
 */
@Component
public class testRequestFilter extends ZuulFilter {
    /**
     * to classify a filter by type. Standard types in Zuul are "pre" for pre-routing filtering,
     * "route" for routing to an origin, "post" for post-routing filters, "error" for error handling.
     * We also support a "static" type for static responses see  StaticResponseFilter.
     * Any filterType made be created or added and run by calling FilterProcessor.runFilters(type)
     *
     * @return A String representing that type
     */

    private ProxyRequestHelper helper;

    @Override
    public String filterType() {
        return "pre";
    }

    /**
     * filterOrder() must also be defined for a filter. Filters may have the same  filterOrder if precedence is not
     * important for a filter. filterOrders do not need to be sequential.
     *
     * @return the int order of a filter
     */
    @Override
    public int filterOrder() {
        return 1;
    }

    /**
     * a "true" return from this method means that the run() method should be invoked
     *
     * @return true if the run() method should be invoked. false will not invoke the run() method
     */
    @Override
    public boolean shouldFilter() {
        return true;
    }

    /**
     * if shouldFilter() is true, this method will be invoked. this method is the core method of a ZuulFilter
     *
     * @return Some arbitrary artifact may be returned. Current implementation ignores it.
     */
    @Override
    public Object run() {
        RequestContext context = RequestContext.getCurrentContext();
        HttpServletRequest request = context.getRequest();
        this.helper = new ProxyRequestHelper();
        MultiValueMap<String, String> headers = this.helper
                .buildZuulRequestHeaders(request);
        MultiValueMap<String, String> params = this.helper
                .buildZuulRequestQueryParams(request);
        String uri = this.helper.buildZuulRequestURI(request);
        
        System.out.println("=========request=========");
        System.out.println("Headers:"+headers);
        System.out.println("Params:"+params);
        System.out.println("URI:"+uri);
        
        return null;
    }
}

這樣打出資訊:

報200正確的訪問(敏感資訊已經用XXX代替):

=========request=========
Headers:{content-type=[application/x-www-form-urlencoded], cache-control=[no-cache], postman-token=[d6860958-b147-469c-a402-d679e3e8fa13], user-agent=[PostmanRuntime/7.2.0], accept=[*/*], apikey=[XXXXXXXXXXXXXXXX], Accept-Encoding=[gzip]}
Params:{token=[XXXXXXXXXXXXXXXXXXXXXX], UserNumber=[XXXXXX]}
URI:/XXXXX/XXX/XXXXX/XXXXXXXXXXXXXXXXXX

報500的訪問:在上述資訊的Headers裡,多出了以下資訊:

x-forwarded-for=[172.XX.XX.XX], x-forwarded-host=[172.YY.YY.YY], x-forwarded-server=[172.YY.YY.YY]

但是將這些X-Forwarded資訊直接寫到Http頭裡,然後向第三方伺服器請求,是正常返回,而同樣的資訊,通過Zuul轉換再請求,則報500。

查詢了Zuul的文件所知,Zuul會通過PreDecorationFilter這個過濾器,在頭資訊加入x-forwarded相關資訊。而這個過濾器的過濾順序為5

再次修改上述testRequestFilter ,將Order值改為6,放在PreDecorationFilter之後執行,然後輸出除錯資訊,發現每個請求裡均加入了x-forwarded相關資訊。而通過apache訪問的原本就帶有x-forwarded資訊的請求則變成了以下資訊:

x-forwarded-for=[172.XX.XX.XX, 172.ZZ.ZZ.ZZ], x-forwarded-host=[172.YY.YY.YY,172.ZZ.ZZ.ZZ:8769], x-forwarded-server=[172.ZZ.ZZ.ZZ]

猜測第三方介面伺服器在處理以上資訊的時候,不符合其處理格式,所以報錯。通知第三方伺服器相關部門,其修改處理邏輯後,返回正常。

最後總結排查時候遇到的兩個坑:

  1. apache做反向代理時候會加上x-forwarded資訊,而nginx做反向代理時候預設不會加上x-forwarded資訊。
  2. 一開始用RequestContext.getZuulRequestHeaders().toString() 這個方法,只會輸出非x-forwarded開頭的頭資訊,不會輸出帶有x-forwarded開頭的頭資訊,導致一開始除錯好久,還以為一模一樣的頭,照樣報錯,百思不得其解。