国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 開發(fā) > Java > 正文

淺談Spring Cloud zuul http請求轉(zhuǎn)發(fā)原理

2024-07-14 08:42:06
字體:
供稿:網(wǎng)友

spring cloud 網(wǎng)關(guān),依賴于netflix 下的zuul 組件

zuul 的流程是,自定義 了ZuulServletFilter和zuulServlet兩種方式,讓開發(fā)者可以去實現(xiàn),并調(diào)用

先來看下ZuulServletFilter的實現(xiàn)片段

 @Override  public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {    try {      init((HttpServletRequest) servletRequest, (HttpServletResponse) servletResponse);      try {        preRouting();      } catch (ZuulException e) {        error(e);        postRouting();        return;      }            // Only forward onto to the chain if a zuul response is not being sent      if (!RequestContext.getCurrentContext().sendZuulResponse()) {        filterChain.doFilter(servletRequest, servletResponse);        return;      }            try {        routing();      } catch (ZuulException e) {        error(e);        postRouting();        return;      }      try {        postRouting();      } catch (ZuulException e) {        error(e);        return;      }    } catch (Throwable e) {      error(new ZuulException(e, 500, "UNCAUGHT_EXCEPTION_FROM_FILTER_" + e.getClass().getName()));    } finally {      RequestContext.getCurrentContext().unset();    }  }

從上面的代碼可以看到,比較關(guān)心的是preRouting、routing,postRouting三個方法 ,這三個方法會調(diào)用 注冊為ZuulFilter的子類,首先來看下這三個方法

preRouting: 是路由前會做一些內(nèi)容

routing():開始路由事項

postRouting:路由結(jié)束,不管是否有錯誤都會經(jīng)過該方法

那這三個方法是怎么和ZuulFilter聯(lián)系在一起的呢?

先來分析下 preRouting:

 void postRouting() throws ZuulException {    zuulRunner.postRoute();  }

同時 ZuulRunner再來調(diào)用

  public void postRoute() throws ZuulException {    FilterProcessor.getInstance().postRoute();  }

最終調(diào)用 FilterProcessor 的 runFilters

  public void preRoute() throws ZuulException {    try {      runFilters("pre");    } catch (ZuulException e) {      throw e;    } catch (Throwable e) {      throw new ZuulException(e, 500, "UNCAUGHT_EXCEPTION_IN_PRE_FILTER_" + e.getClass().getName());    }  }

看到了runFilters 是通過 filterType(pre ,route ,post )來過濾出已經(jīng)注冊的 ZuulFilter:

 public Object runFilters(String sType) throws Throwable {    if (RequestContext.getCurrentContext().debugRouting()) {      Debug.addRoutingDebug("Invoking {" + sType + "} type filters");    }    boolean bResult = false;    //通過sType獲取 zuulFilter的列表    List<ZuulFilter> list = FilterLoader.getInstance().getFiltersByType(sType);    if (list != null) {      for (int i = 0; i < list.size(); i++) {        ZuulFilter zuulFilter = list.get(i);        Object result = processZuulFilter(zuulFilter);        if (result != null && result instanceof Boolean) {          bResult |= ((Boolean) result);        }      }    }    return bResult;  }

再來看下 ZuulFilter的定義

public abstract class ZuulFilter implements IZuulFilter, Comparable<ZuulFilter> {  private final DynamicBooleanProperty filterDisabled =      DynamicPropertyFactory.getInstance().getBooleanProperty(disablePropertyName(), false);  /**   * 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   */  abstract public String filterType();  /**   * 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   */  abstract public int filterOrder();  /**   * By default ZuulFilters are static; they don't carry state. This may be overridden by overriding the isStaticFilter() property to false   *   * @return true by default   */  public boolean isStaticFilter() {    return true;  }

只列出了一部分字段,但可以看到filterType和filterOrder兩個字段,這兩個分別是指定filter是什么類型,排序

這兩個決定了實現(xiàn)的ZuulFilter會在什么階段被執(zhí)行,按什么順序執(zhí)行

當(dāng)選擇好已經(jīng)注冊的ZuulFilter后,會調(diào)用ZuulFilter的runFilter

 public ZuulFilterResult runFilter() {    ZuulFilterResult zr = new ZuulFilterResult();    if (!isFilterDisabled()) {      if (shouldFilter()) {        Tracer t = TracerFactory.instance().startMicroTracer("ZUUL::" + this.getClass().getSimpleName());        try {          Object res = run();          zr = new ZuulFilterResult(res, ExecutionStatus.SUCCESS);        } catch (Throwable e) {          t.setName("ZUUL::" + this.getClass().getSimpleName() + " failed");          zr = new ZuulFilterResult(ExecutionStatus.FAILED);          zr.setException(e);        } finally {          t.stopAndLog();        }      } else {        zr = new ZuulFilterResult(ExecutionStatus.SKIPPED);      }    }    return zr;  }

其中run 是一個ZuulFilter的一個抽象方法

public interface IZuulFilter {  /**   * 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   */  boolean shouldFilter();  /**   * 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.   */  Object run();}  

所以,實現(xiàn)ZuulFilter的子類要重寫 run方法,我們來看下 其中一個階段的實現(xiàn) PreDecorationFilter 這個類是Spring Cloud封裝的在使用Zuul 作為轉(zhuǎn)發(fā)的代碼服務(wù)器時進(jìn)行封裝的對象,目的是為了決定當(dāng)前的要轉(zhuǎn)發(fā)的請求是按ServiceId,Http請求,還是forward來作轉(zhuǎn)發(fā)

@Override  public Object run() {    RequestContext ctx = RequestContext.getCurrentContext();    final String requestURI = this.urlPathHelper.getPathWithinApplication(ctx.getRequest());    Route route = this.routeLocator.getMatchingRoute(requestURI);    if (route != null) {      String location = route.getLocation();      if (location != null) {        ctx.put("requestURI", route.getPath());        ctx.put("proxy", route.getId());        if (!route.isCustomSensitiveHeaders()) {          this.proxyRequestHelper              .addIgnoredHeaders(this.properties.getSensitiveHeaders().toArray(new String[0]));        }        else {          this.proxyRequestHelper.addIgnoredHeaders(route.getSensitiveHeaders().toArray(new String[0]));        }        if (route.getRetryable() != null) {          ctx.put("retryable", route.getRetryable());        }        // 如果配置的轉(zhuǎn)發(fā)地址是http開頭,會設(shè)置 RouteHost        if (location.startsWith("http:") || location.startsWith("https:")) {          ctx.setRouteHost(getUrl(location));          ctx.addOriginResponseHeader("X-Zuul-Service", location);        }         // 如果配置的轉(zhuǎn)發(fā)地址forward,則會設(shè)置forward.to        else if (location.startsWith("forward:")) {          ctx.set("forward.to",              StringUtils.cleanPath(location.substring("forward:".length()) + route.getPath()));          ctx.setRouteHost(null);          return null;        }        else {           // 否則以serviceId進(jìn)行轉(zhuǎn)發(fā)          // set serviceId for use in filters.route.RibbonRequest          ctx.set("serviceId", location);          ctx.setRouteHost(null);          ctx.addOriginResponseHeader("X-Zuul-ServiceId", location);        }        if (this.properties.isAddProxyHeaders()) {          addProxyHeaders(ctx, route);          String xforwardedfor = ctx.getRequest().getHeader("X-Forwarded-For");          String remoteAddr = ctx.getRequest().getRemoteAddr();          if (xforwardedfor == null) {            xforwardedfor = remoteAddr;          }          else if (!xforwardedfor.contains(remoteAddr)) { // Prevent duplicates            xforwardedfor += ", " + remoteAddr;          }          ctx.addZuulRequestHeader("X-Forwarded-For", xforwardedfor);        }        if (this.properties.isAddHostHeader()) {          ctx.addZuulRequestHeader("Host", toHostHeader(ctx.getRequest()));        }      }    }    else {      log.warn("No route found for uri: " + requestURI);      String fallBackUri = requestURI;      String fallbackPrefix = this.dispatcherServletPath; // default fallback                                // servlet is                                // DispatcherServlet      if (RequestUtils.isZuulServletRequest()) {        // remove the Zuul servletPath from the requestUri        log.debug("zuulServletPath=" + this.properties.getServletPath());        fallBackUri = fallBackUri.replaceFirst(this.properties.getServletPath(), "");        log.debug("Replaced Zuul servlet path:" + fallBackUri);      }      else {        // remove the DispatcherServlet servletPath from the requestUri        log.debug("dispatcherServletPath=" + this.dispatcherServletPath);        fallBackUri = fallBackUri.replaceFirst(this.dispatcherServletPath, "");        log.debug("Replaced DispatcherServlet servlet path:" + fallBackUri);      }      if (!fallBackUri.startsWith("/")) {        fallBackUri = "/" + fallBackUri;      }      String forwardURI = fallbackPrefix + fallBackUri;      forwardURI = forwardURI.replaceAll("//", "/");      ctx.set("forward.to", forwardURI);    }    return null;  }

這個前置處理,是為了后面決定以哪種ZuulFilter來處理當(dāng)前的請求 ,如 SimpleHostRoutingFilter,這個的filterType是post ,當(dāng) ``PreDecorationFilter設(shè)置了requestContext中的 RouteHost,如 SimpleHostRoutingFilter中的判斷

  @Override  public boolean shouldFilter() {    return RequestContext.getCurrentContext().getRouteHost() != null        && RequestContext.getCurrentContext().sendZuulResponse();  }

在 SimpleHostRoutingFilter中的run中,真正實現(xiàn)地址轉(zhuǎn)發(fā)的內(nèi)容,其實質(zhì)是調(diào)用 httpClient進(jìn)行請求

@Override  public Object run() {    RequestContext context = RequestContext.getCurrentContext();    HttpServletRequest request = context.getRequest();    MultiValueMap<String, String> headers = this.helper        .buildZuulRequestHeaders(request);    MultiValueMap<String, String> params = this.helper        .buildZuulRequestQueryParams(request);    String verb = getVerb(request);    InputStream requestEntity = getRequestBody(request);    if (request.getContentLength() < 0) {      context.setChunkedRequestBody();    }    String uri = this.helper.buildZuulRequestURI(request);    this.helper.addIgnoredHeaders();    try {      HttpResponse response = forward(this.httpClient, verb, uri, request, headers,          params, requestEntity);      setResponse(response);    }    catch (Exception ex) {      context.set(ERROR_STATUS_CODE, HttpServletResponse.SC_INTERNAL_SERVER_ERROR);      context.set("error.exception", ex);    }    return null;  }

最后如果是成功能,會調(diào)用 注冊 為post的ZuulFilter ,目前有兩個 SendErrorFilter 和 SendResponseFilter 這兩個了,一個是處理錯誤,一個是處理成功的結(jié)果

以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持VeVb武林網(wǎng)。


注:相關(guān)教程知識閱讀請移步到JAVA教程頻道。
發(fā)表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發(fā)表
主站蜘蛛池模板: 德江县| 大渡口区| 塔城市| 璧山县| 武定县| 定安县| 交城县| 德格县| 青浦区| 永丰县| 张家界市| 孟村| 陵川县| 商南县| 宣威市| 始兴县| 长春市| 广汉市| 错那县| 勃利县| 榆林市| 潢川县| 洛浦县| 沈丘县| 绥江县| 延安市| 黑山县| 开远市| 都江堰市| 灯塔市| 凉城县| 龙岩市| 介休市| 沙湾县| 宿迁市| 龙南县| 重庆市| 朔州市| 兰坪| 南京市| 静海县|