Spring Cloud (seven): Service Gateway zuul filter

The basic use of the above routing functions Zuul paper then introduces the core concept Zuul - Zuul filter (filter).

Zuul basic functions implemented (analogous to Struts interceptor, except Struts interceptors use chain of responsibility pattern, Zuul is controlled performed by FilterProcessor) by Zuul filter, at different stages, realization by different types of filters the corresponding function.

Zuul filter

Filter Type

zuul filter comprising four types depending on the HTTP request processing stages

  • pre: Before forwarding the request to the backend service execution target, typically for requesting authentication, determining the routing address, logging, and so
  • route: forwarding the request, using Apache HttpClient to construct or Ribbon request for target service
  • post: After the target service returns the result to the processing result, such as adding the response header for collecting statistical performance data
  • error: the entire process of processing the request if an error occurs, it will trigger execution error filter for the error processing

Zuul client request through the filter process flow below

Zuul filter

zuul used RequestContextto pass data between the filter, the data stored in each request ThreadLocal, including where to route the request, error, HttpServletRequest, HttpServletResponse other such data are stored in the RequestContext. RequestContext expanded ConcurrentHashMap, so we may need to keep the information passed to the 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;
    }
}

In the subsequent part of the request, such as an interface or a backend service filter, the values ​​can be acquired from the header HttpServletRequest, such as

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

Zuul error handling

In the life cycle zuul filter, if any one link throws an exception error filter will be executed, SendErrorFilter only when RequestContext.getThrowable () will not run is null, sets javax.servlet.error. * attribute to the request, and then forwards the request to the spring boot error page, the default is / error BasicErrorController interfaces implemented. Sometimes we need to return to a unified response format, and the default / error interface may not meet the requirements, you can customize / error interface. ErrorController need to implement the interface so that the default BasicErrorController failure.

@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 service degradation

When a timeout occurs or an abnormal call service, can be provided at the side zuul callbacks service degradation, the result returns default responses, such as

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

When the service request fails, it returns a response following format unified

{
    "code": "500",
    "message": "服务器内部错误"
}

to sum up

This paper focuses on Zuul filter content and customize the use of introduced while handling a service call fails and abnormal operation of the process filter callback downgrade a simple explanation. For space, the development process more specific details we continue to explore further follow-up.
Serious life, happy to share
welcome attention to micro-channel public number: technology space empty mountains Xinyu of
obtaining Spring Boot, Spring Cloud, Docker and other technical articles
Public two-dimensional code number

Guess you like

Origin www.cnblogs.com/spec-dog/p/12340902.html