zuul 和 gateway 原理分析

Posted lisin-lee-cooper

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了zuul 和 gateway 原理分析相关的知识,希望对你有一定的参考价值。

一. zuul 原理分析

zuulServlet 源码

public class ZuulServlet extends HttpServlet 
    
 	private ZuulRunner zuulRunner;
    
    public void init(ServletConfig config) throws ServletException 
        ...
    
    public void service(javax.servlet.ServletRequest servletRequest, javax.servlet.ServletResponse servletResponse) throws ServletException, IOException 
        ...
    

service 方法

try 
    init((HttpServletRequest) servletRequest, (HttpServletResponse) servletResponse);
 
    // Marks this request as having passed through the "Zuul engine", as opposed to servlets
    // explicitly bound in web.xml, for which requests will not have the same data attached
    RequestContext context = RequestContext.getCurrentContext();
    context.setZuulEngineRan();
 
    try 
        preRoute();
     catch (ZuulException e) 
        error(e);
        postRoute();
        return;
    
    try 
        route();
     catch (ZuulException e) 
        error(e);
        postRoute();
        return;
    
    try 
        postRoute();
     catch (ZuulException e) 
        error(e);
        return;
    
 
 catch (Throwable e) 
    error(new ZuulException(e, 500, "UNHANDLED_EXCEPTION_" + e.getClass().getName()));
 finally 
    RequestContext.getCurrentContext().unset();

processZuulFilter 方法

public Object processZuulFilter(ZuulFilter filter) throws ZuulException 
 
    RequestContext ctx = RequestContext.getCurrentContext();
    boolean bDebug = ctx.debugRouting();
    final String metricPrefix = "zuul.filter-";
    long execTime = 0;
    String filterName = "";
    try 
        long ltime = System.currentTimeMillis();
        filterName = filter.getClass().getSimpleName();
        
        RequestContext copy = null;
        Object o = null;
        Throwable t = null;
 
        if (bDebug) 
            Debug.addRoutingDebug("Filter " + filter.filterType() + " " + filter.filterOrder() + " " + filterName);
            copy = ctx.copy();
        
        //执行ZuulFilter的runFilter逻辑
        ZuulFilterResult result = filter.runFilter();
        ExecutionStatus s = result.getStatus();
        //执行耗时统计(可以发现Zuul还没有完善这个功能,只是形成了框架)
        execTime = System.currentTimeMillis() - ltime;
		//处理执行结果,无论成功与否,都记录了debug日志
        switch (s) 
            case FAILED:
                t = result.getException();
                ctx.addFilterExecutionSummary(filterName, ExecutionStatus.FAILED.name(), execTime);
                break;
            case SUCCESS:
                o = result.getResult();
                ctx.addFilterExecutionSummary(filterName, ExecutionStatus.SUCCESS.name(), execTime);
                if (bDebug) 
                    Debug.addRoutingDebug("Filter " + filterName + " TYPE:" + filter.filterType() + " ORDER:" + filter.filterOrder() + " Execution time = " + execTime + "ms");
                    Debug.compareContextState(filterName, copy);
                
                break;
            default:
                break;
        
        
        if (t != null) throw t;
		//目前作为空壳存在,可见是为了方便扩展
        usageNotifier.notify(filter, s);
        return o;
 
     catch (Throwable e) 
        if (bDebug) 
            Debug.addRoutingDebug("Running Filter failed " + filterName + " type:" + filter.filterType() + " order:" + filter.filterOrder() + " " + e.getMessage());
        
        usageNotifier.notify(filter, ExecutionStatus.FAILED);
        if (e instanceof ZuulException) 
            throw (ZuulException) e;
         else 
            ZuulException ex = new ZuulException(e, "Filter threw Exception", 500, filter.filterType() + ":" + filterName);
            ctx.addFilterExecutionSummary(filterName, ExecutionStatus.FAILED.name(), execTime);
            throw ex;
        
    

Zuul提供了com.netflix.zuul.filters.StaticResponseFilter和com.netflix.zuul.filters.SurgicalDebugFilter两种抽象类,StaticResponseFilter会将请求直接处理并返回,即不会经过路由链路;SurgicalDebugFilter则会将请求路由到微服务中;

zuulServlet 由于底层是servlet, 易于扩展, 可以构建相应的filter 来按照顺序做登陆校验和权限校验。

二.gateway原理分析

gateway 自动装配类

@Configuration
@ConditionalOnProperty(name = "spring.cloud.gateway.enabled", matchIfMissing = true)
@EnableConfigurationProperties
@AutoConfigureBefore(HttpHandlerAutoConfiguration.class)
@AutoConfigureAfter(GatewayLoadBalancerClientAutoConfiguration.class, GatewayClassPathWarningAutoConfiguration.class)
@ConditionalOnClass(DispatcherHandler.class)
public class GatewayAutoConfiguration 
 
	...
	
	@Bean
	@ConditionalOnBean(DispatcherHandler.class)
	public ForwardRoutingFilter forwardRoutingFilter(DispatcherHandler dispatcherHandler) 
		return new ForwardRoutingFilter(dispatcherHandler);
	
 
	...
 

protected Mono<?> getHandlerInternal(ServerWebExcha	nge exchange) 
   exchange.getAttributes().put(GATEWAY_HANDLER_MAPPER_ATTR, getClass().getSimpleName());
 
   return lookupRoute(exchange)
         //根据exchange找匹配的route
         // .log("route-predicate-handler-mapping", Level.FINER) //name this
         .flatMap((Function<Route, Mono<?>>) r -> //替换请求attributes值
            exchange.getAttributes().remove(GATEWAY_PREDICATE_ROUTE_ATTR);
            if (logger.isDebugEnabled()) 
               logger.debug("Mapping [" + getExchangeDesc(exchange) + "] to " + r);
            
 
            exchange.getAttributes().put(GATEWAY_ROUTE_ATTR, r);
            return Mono.just(webHandler);//执行filter的handle方法
         ).switchIfEmpty(Mono.empty().then(Mono.fromRunnable(() -> 
            exchange.getAttributes().remove(GATEWAY_PREDICATE_ROUTE_ATTR);
            if (logger.isTraceEnabled()) 
               logger.trace("No RouteDefinition found for [" + getExchangeDesc(exchange) + "]");
            
         ))); 

protected Mono<Route> lookupRoute(ServerWebExchange exchange) 
   return this.routeLocator.getRoutes()
         .filterWhen(route ->  //找到匹配的route,一个route包含一个filter链
            // add the current route we are testing
            exchange.getAttributes().put(GATEWAY_PREDICATE_ROUTE_ATTR, route.getId());
            return route.getPredicate().apply(exchange);
         )
         // .defaultIfEmpty() put a static Route not found
         // or .switchIfEmpty()
         // .switchIfEmpty(Mono.<Route>empty().log("noroute"))
         .next()
         .map(route -> 
            if (logger.isDebugEnabled()) 
               logger.debug("Route matched: " + route.getId());
            
            validateRoute(route, exchange);
            return route;
         );

@Override
public Flux<Route> getRoutes() 
   return this.routeDefinitionLocator.getRouteDefinitions()
         .map(this::convertToRoute)
         //TODO: error handling
         .map(route -> 
            if (logger.isDebugEnabled()) 
               logger.debug("RouteDefinition matched: " + route.getId());
            
            return route;
         );

servlet,但使用了webflux,多嵌套了一层框架 ,大量使用的流式编程;
提供了非常丰富的filter实现和灵活的RoutePredicateFactory(route匹配规则),提供了限流接口RedisRateLimiter的实现,功能更加全面。

以上是关于zuul 和 gateway 原理分析的主要内容,如果未能解决你的问题,请参考以下文章

Spring GateWay 路由源码分析

最全面的改造Zuul网关为Spring Cloud Gateway(包含Zuul核心实现和Spring Cloud Gateway核心实现)

0601-Zuul构建API Gateway-API gateway简介

微服务网关Gateway实践总结

SpringCloud - Spring Cloud 之 Zuul和Gateway网关(十四)

Spring Cloud Gateway初探