Spring Cloud zuul http请求转发原理

您所在的位置:网站首页 转发是转发到哪里的 Spring Cloud zuul http请求转发原理

Spring Cloud zuul http请求转发原理

2024-06-28 10:06:46| 来源: 网络整理| 查看: 265

spring cloud 网关,依赖于netflix 下的zuul 组件

zuul 的流程是,自定义 了ZuulServletFilter和zuulServlet两种方式,让开发者可以去实现,并调用

先来看下ZuulServletFilter的实现片段

@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(); } }

从上面的代码可以看到,比较关心的是preRouting、routing,postRouting三个方法 ,这三个方法会调用 注册为ZuulFilter的子类,首先来看下这三个方法

preRouting: 是路由前会做一些内容

routing():开始路由事项

postRouting:路由结束,不管是否有错误都会经过该方法

那这三个方法是怎么和ZuulFilter联系在一起的呢?

先来分析下 preRouting:

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

同时 ZuulRunner再来调用

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

最终调用 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 )来过滤出已经注册的 ZuulFilter:

public Object runFilters(String sType) throws Throwable { if (RequestContext.getCurrentContext().debugRouting()) { Debug.addRoutingDebug("Invoking {" + sType + "} type filters"); } boolean bResult = false; //通过sType获取 zuulFilter的列表 List 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 { 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是什么类型,排序

这两个决定了实现的ZuulFilter会在什么阶段被执行,按什么顺序执行

当选择好已经注册的ZuulFilter后,会调用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(); }

所以,实现ZuulFilter的子类要重写 run方法,我们来看下 其中一个阶段的实现 PreDecorationFilter 这个类是Spring Cloud封装的在使用Zuul 作为转发的代码服务器时进行封装的对象,目的是为了决定当前的要转发的请求是按ServiceId,Http请求,还是forward来作转发

@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()); } // 如果配置的转发地址是http开头,会设置 RouteHost if (location.startsWith("http:") || location.startsWith("https:")) { ctx.setRouteHost(getUrl(location)); ctx.addOriginResponseHeader("X-Zuul-Service", location); } // 如果配置的转发地址forward,则会设置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进行转发 // 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来处理当前的请求 ,如 SimpleHostRoutingFilter,这个的filterType是post ,当 ``PreDecorationFilter设置了requestContext中的 RouteHost,如 SimpleHostRoutingFilter中的判断

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

在 SimpleHostRoutingFilter中的run中,真正实现地址转发的内容,其实质是调用 httpClient进行请求

@Override public Object run() { RequestContext context = RequestContext.getCurrentContext(); HttpServletRequest request = context.getRequest(); MultiValueMap headers = this.helper .buildZuulRequestHeaders(request); MultiValueMap 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; }

最后如果是成功能,会调用 注册 为post的ZuulFilter ,目前有两个 SendErrorFilter 和 SendResponseFilter 这两个了,一个是处理错误,一个是处理成功的结果。



【本文地址】

公司简介

联系我们

今日新闻


点击排行

实验室常用的仪器、试剂和
说到实验室常用到的东西,主要就分为仪器、试剂和耗
不用再找了,全球10大实验
01、赛默飞世尔科技(热电)Thermo Fisher Scientif
三代水柜的量产巅峰T-72坦
作者:寞寒最近,西边闹腾挺大,本来小寞以为忙完这
通风柜跟实验室通风系统有
说到通风柜跟实验室通风,不少人都纠结二者到底是不
集消毒杀菌、烘干收纳为一
厨房是家里细菌较多的地方,潮湿的环境、没有完全密
实验室设备之全钢实验台如
全钢实验台是实验室家具中较为重要的家具之一,很多

推荐新闻


图片新闻

实验室药品柜的特性有哪些
实验室药品柜是实验室家具的重要组成部分之一,主要
小学科学实验中有哪些教学
计算机 计算器 一般 打孔器 打气筒 仪器车 显微镜
实验室各种仪器原理动图讲
1.紫外分光光谱UV分析原理:吸收紫外光能量,引起分
高中化学常见仪器及实验装
1、可加热仪器:2、计量仪器:(1)仪器A的名称:量
微生物操作主要设备和器具
今天盘点一下微生物操作主要设备和器具,别嫌我啰嗦
浅谈通风柜使用基本常识
 众所周知,通风柜功能中最主要的就是排气功能。在

专题文章

    CopyRight 2018-2019 实验室设备网 版权所有 win10的实时保护怎么永久关闭