IT教程 ·

Spring Cloud(七):服务网关zuul过滤器

非阻塞同步算法实战(四)- 计数器定时持久化

上文引见了Zuul的基础运用与路由功用,本文接着引见Zuul的中心观点 —— Zuul过滤器(filter)。

Zuul的功用基础经由过程Zuul过滤器来完成(类比于Struts的拦截器,只是Struts拦截器用到义务链形式,Zuul则是经由过程FilterProcessor来掌握实行),在差别的阶段,经由过程差别范例的过滤器来完成相应的功用。

Zuul过滤器

过滤器范例

zuul的过滤器依据对HTTP请求的差别处置惩罚阶段包括以下四种范例

  • pre :在请求转发到后端目的效劳之前实行,平常用于请求认证、肯定路由地点、日记纪录等
  • route :转发请求,运用Apache HttpClient 或 Ribbon来组织对目的效劳的请求
  • post :在目的效劳返回效果后对效果举行处置惩罚,比方增加相应头、网络统计机能数据等
  • error :在请求处置惩罚的全部流程中假如涌现毛病,则会触发error过滤器实行,对毛病举行处置惩罚

客户端请求经由zuul过滤器处置惩罚的流程以下图

Spring Cloud(七):服务网关zuul过滤器 IT教程 第1张

zuul运用RequestContext来在过滤器之间通报数据,数据存于每一个request的ThreadLocal,包括请求路由到哪里,毛病,HttpServletRequest,HttpServletResponse 等这些数据都存储于RequestContext中。RequestContext 扩大了ConcurrentHashMap,所以我们能够依据须要将信息存于context中举行通报。

@EnableZuulProxy vs @EnableZuulServer

zuul供应了两个注解 @EnableZuulProxy, @EnableZuulServer,来启用差别的过滤器鸠合。@EnableZuulProxy 启用的过滤器 是@EnableZuulServer 的超集, 它包括了@EnableZuulServer 的一切过滤器,proxy重要多了一些供应路由功用的过滤器(可见@EnableZuulServer 不供应路由功用,作为server形式而不是代办形式运转)

@EnableZuulServer 注解启用的过滤器包括

filter范例 完成类 filter次序值 功用申明
pre ServletDetectionFilter -3 检测请求是不是经由过程Spring Dispatcher,并在RequestContext 中增加一个key为isDispatcherServletRequest, 值为true(不经由过程则为false)的属性
pre FormBodyWrapperFilter -1 剖析Form data,为请求的下流举行从新编码
pre DebugFilter 1 假如请求参数设置了debug,则会将RequestContext.setDebugRouting() ,RequestContext.setDebugRequest() 设置为ture
route SendForwardFilter 500 运用RequestDispatch servlet来转发请求,转发地点存于RequestContext中key为FilterConstants.FORWARD_TO_KEY的属性中,关于转发到当前运用的接口比较有效
post SendResponseFilter 1000 将代办请求的相应内容写到当前的相应中
error SendErrorFilter 0 假如RequestContext.getThrowable() 不为空,则会转发到/error,能够经由过程error.path来转变默许的转发途径/error

@EnableZuulProxy 除了上面的过滤器,还包括以下过滤器

filter范例 完成类 filter次序值 功用申明
pre PreDecorationFilter 5 肯定路由到哪里,怎样路由,依靠供应的RouteLocator,同时也为下流请求设置多个与proxy相干的header
route RibbonRoutingFilter 10 运用ribbon,hystrix,以及内嵌的http client来发送请求,可在RequestContext中经由过程FilterConstants.SERVICE_ID_KEY 来找到路由Service的ID
route SimpleHostRoutingFilter 100 运用Apache httpClient来发送请求到一个预先肯定的url,可经由过程RequestContext.getRouteHost()来猎取urls

由上可见@EnableZuulServer 注解并不包括往后端效劳负载平衡地路由请求的代办功用,@EnableZuulProxy的PreDecorationFilter,RibbonRoutingFilter过滤器才担当此任。PreDecorationFilter经由过程供应的DiscoveryClientRouteLocator 从 DiscoveryClient(如Eureka)与属性文件中加载路由定义, 为每一个serviceId建立一个route,新效劳增加进来,路由也会动态革新。路由肯定了,在RibbonRoutingFilter 中经由过程ribbon与hystrix连系来向后端目的效劳提议请求,并举行负载平衡。过滤器的次序值示意在同范例过滤器中的实行次序,值越小越先实行。

自定义Zuul过滤器

自定义的zuul过滤器与框架自带过滤器相似,包括四部份

  1. 过滤器范例,包括pre, route, post
  2. 过滤器次序,定义在同范例过滤器中的实行次序,数值越小越先实行
  3. 是不是实行过滤,经由过程一些前提推断来肯定是不是实行该过滤器
  4. 过滤器实行体,定义详细实行的操纵

比方我们须要在Http请求头中设置一个值,供请求链路的下流环节接见,则能够自定义一个过滤器以下,

@Component
public class ReqIdPreFilter extends ZuulFilter {

    @Override
    public String filterType() {
        return FilterConstants.PRE_TYPE;
    }

    @Override
    public int filterOrder() {
        return FilterConstants.PRE_DECORATION_FILTER_ORDER - 1; //在PreDecorationFilter过滤器之前实行
    }

    @Override
    public boolean shouldFilter() {
        return true;
    }

    @Override
    public Object run() throws ZuulException {
        RequestContext ctx = RequestContext.getCurrentContext();
        ctx.addZuulRequestHeader("reqId", UUID.randomUUID().toString());
        return null;
    }
}

在请求的后续环节,比方后端效劳的filter或接口中,则可直接从HttpServletRequest 猎取该header值,如

@GetMapping("hello/reqId")
public String getReqId(HttpServletRequest request) {
    return "hello-service返回:" + request.getHeader("reqId");
}

Zuul的毛病处置惩罚

在zuul过滤器的生命周期中,假如任何一个环节抛出非常,则error过滤器会被实行,SendErrorFilter只有当RequestContext.getThrowable()不为null时才会运转,会设置javax.servlet.error.* 属性到request中,然后将请求转发到spring boot的error page, 默许为BasicErrorController完成的/error接口。 有时候我们须要将返回相应花样举行一致,而默许的/error接口完成大概不满足请求,则能够自定义/error接口。须要完成ErrorController 接口以使默许的BasicErrorController 失效。

@RestController
public class ZuulErrorController implements ErrorController {

    @RequestMapping("/error")
    public Map<String, String> error(HttpServletRequest request){
        Map<String, String> result = Maps.newHashMap();
        result.put("code", request.getAttribute("javax.servlet.error.status_code").toString());
        result.put("message", request.getAttribute("javax.servlet.error.message").toString());
        result.put("exception", request.getAttribute("javax.servlet.error.exception").toString());
        return result;
    }

    @Override
    public String getErrorPath() {
        return "/error";
    }
}

Zuul的效劳降级

当挪用效劳涌现超时或非常时,在zuul侧可供应回调举行效劳降级,返回默许相应效果,如

@Component
public class MyFallbackProvider implements FallbackProvider {

    @Override
    public String getRoute() {
        return null; //指定这个回调针对的route Id,假如对一切route,则返回* 或null
    }

    @Override
    public ClientHttpResponse fallbackResponse(String route, Throwable cause) {
        if (cause instanceof HystrixTimeoutException) {
            return response(HttpStatus.GATEWAY_TIMEOUT);
        } else {
            return response(HttpStatus.INTERNAL_SERVER_ERROR);
        }
    }

    private ClientHttpResponse response(final HttpStatus status) {
        return new ClientHttpResponse() {
            @Override
            public HttpStatus getStatusCode() throws IOException {
                return status;
            }
            @Override
            public int getRawStatusCode() throws IOException {
                return status.value();
            }
            @Override
            public String getStatusText() throws IOException {
                return status.getReasonPhrase();
            }
            @Override
            public void close() {
            }
            @Override
            public InputStream getBody() throws IOException {
                Map<String, String> result = Maps.newLinkedHashMap();
                result.put("code", "" + status.value());
                String msg = HttpStatus.GATEWAY_TIMEOUT == getStatusCode() ? "请求效劳超时" : "效劳器内部毛病";
                result.put("message", msg);
                return new ByteArrayInputStream(new ObjectMapper().writeValueAsString(result).getBytes());
            }
            @Override
            public HttpHeaders getHeaders() {
                HttpHeaders headers = new HttpHeaders();
                headers.setContentType(MediaType.APPLICATION_JSON);
                return headers;
            }
        };
    }
}

则当效劳请求失利时,一致返回以下花样的相应

{
    "code": "500",
    "message": "效劳器内部毛病"
}

总结

本文重要对Zuul过滤器相干内容及自定义运用举行了引见,同时对过滤器运转过程当中非常的处置惩罚及效劳挪用失利的降级回调举行了简朴申明。出于篇幅,开发过程当中更详细的细节我们后续再继承讨论。
仔细生活,快活分享

如何高效开展测试用例评审?附用例评审检查清单及用例评审报告模板

参与评论