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的实现,功能更加全面。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值