zoukankan      html  css  js  c++  java
  • 基于zuul实现自定义路由源码分析

    ZuulFilter定义

    通过继承ZuulFilter我们可以定义一个新的过滤器,如下

    public class IpAddressFilter extends ZuulFilter {
        @Autowired
        private IGatewayService iGatewayService;
    
        @Override
        public String filterType() {
            // pre类型的过滤器
            return PRE_TYPE;
        }
    
        @Override
        public int filterOrder() {
            // 排序
            return 1;
        }
    
        @Override
        public boolean shouldFilter() {
            return true;
        }
    
        @Override
        public Object run() {
            RequestContext ctx = RequestContext.getCurrentContext();
            String ip = ctx.getRequest().getRemoteAddr();
            Set<String> blackList = ConcurrentCache.getBlackSet();
            Set<String> whiteList = ConcurrentCache.getWhiteSet();
    
            blackList.removeAll(whiteList);
    
            // 在黑名单中禁用
            if (StringUtils.isNotBlank(ip)&& blackList.contains(ip)) {
                ctx.setSendZuulResponse(false);
    
                ctx.setResponseBody("Suspected flooding attack, IP blocked");
                ctx.setResponseStatusCode(HttpStatus.FORBIDDEN.value());
                ctx.addZuulResponseHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_UTF8_VALUE);
                return null;
            }
            return null;
        }
    }
    

    ZuulFilter中实现了compareTo()方法,根据它的值决定同类型的filter的执行顺序。compareTo()方法如下:

    public int compareTo(ZuulFilter filter) {
        return Integer.compare(this.filterOrder(), filter.filterOrder());
    }
    

    注册ZuulFilter到spring容器中

    ZuulFilter可以通过@Component,也可以通过@Bean实例化来纳入spring的生命周期中。

    @Configuration
    public class FilterConfig {
    
        @Bean
        public IpAddressFilter addIpAddressFilter() {
            return new IpAddressFilter();
        }
    }    
    

    ZuulServerAutoConfiguration中自动装配了filter,被spring实例化出来的所有的ZuulFilter都会被自动装配到Map中。

    @Configuration
    protected static class ZuulFilterConfiguration {
        // 根据类型,自动装配ZuulFilter到Map对象中
    	@Autowired
    	private Map<String, ZuulFilter> filters;
    
    	@Bean
    	public ZuulFilterInitializer zuulFilterInitializer(
    			CounterFactory counterFactory, TracerFactory tracerFactory) {
    		FilterLoader filterLoader = FilterLoader.getInstance();
    		// 单例模式
    		FilterRegistry filterRegistry = FilterRegistry.instance();
    		return new ZuulFilterInitializer(this.filters, counterFactory, tracerFactory, filterLoader, filterRegistry);
    	}
    
    }
    

    上面的代码会调用ZuulFilterInitializer的构造方法。

    ZuulFilterInitializer中的contextInitialized()开启了@PostConstruct注解,在构造方法完成时,容器会调用contextInitialized()方法(注意:ZuulFilterInitializer对象要由spring管理才会调用到@PostConstruct),将所有的filter保存到filterRegistry中,filterRegistry是一个单例对象。

    说明:PostConstruct 注释用于在依赖关系注入完成之后需要执行的方法上

    contextInitialized()方法如下:

    @PostConstruct
    public void contextInitialized() {
    	log.info("Starting filter initializer");
    
    	TracerFactory.initialize(tracerFactory);
    	CounterFactory.initialize(counterFactory);
    
    	for (Map.Entry<String, ZuulFilter> entry : this.filters.entrySet()) {
    	    // 保存filter
    		filterRegistry.put(entry.getKey(), entry.getValue());
    	}
    }
    

    自定义路由转发规则

    ZuulProxyAutoConfiguration类中注册了RouteLocatorbean@Bean会按照类型,自动注入RouteLocator的实现类。

    @Bean
    public PreDecorationFilter preDecorationFilter(RouteLocator routeLocator,
    		ProxyRequestHelper proxyRequestHelper) {
    	return new PreDecorationFilter(routeLocator, this.server.getServletPrefix(),
    			this.zuulProperties, proxyRequestHelper);
    }
    

    RouteLocator实例化

    @Configuration
    public class AppConfig{
        //.....省略....
        @Bean(value = "discoveryRouteLocator")
        public DiscoveryClientRouteLocator discoveryClientRouteLocator(ServerProperties server, DiscoveryClient discovery, ZuulProperties properties,ServiceInstance localInstance) {
            return new CustomRouteLocator(server.getServletPath(), discovery,properties,localInstance);
        }
    
    }
    

    CustomRouteLocator实现自定义路由的功能,类如下。

    
    public class CustomRouteLocator extends DiscoveryClientRouteLocator {
        // ....省略....
    
        @Override
        // 重写
        public Route getMatchingRoute(String path) {
            // ....省略....
            //可以从数据库中读取路由规则,并进行处理
        }
    
        // 重写
        @Override
        protected LinkedHashMap<String, ZuulRoute> locateRoutes() {
            // ....省略....
        }
    }
    

    Servlet初始化

    为什么通过访问网关可以自动跳转到zuul中,其实是通过servlet的实现的,该servlet对根路径/进行过滤。下面说明servlet的初始化内容。

    ZuulServerAutoConfiguration类中定义了ZuulController

    @Bean
    public ZuulController zuulController() {
    	return new ZuulController();
    }
    

    ZuulController继承了ServletWrappingController

    public class ZuulController extends ServletWrappingController {
    
    	public ZuulController() {
    	    // 设置类为ZuulServlet
    		setServletClass(ZuulServlet.class);
    		setServletName("zuul");
    		setSupportedMethods((String[]) null);
    	}
    
    	@Override
    	public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
    		try {
    			return super.handleRequestInternal(request, response);
    		}
    		finally {
    			RequestContext.getCurrentContext().unset();
    		}
    	}
    
    }
    

    ServletWrappingControllerZuulServlet进行实例化

    @Override
    public void afterPropertiesSet() throws Exception {
    	if (this.servletClass == null) {
    		throw new IllegalArgumentException("'servletClass' is required");
    	}
    	if (this.servletName == null) {
    		this.servletName = this.beanName;
    	}
    	// 实例化
    	this.servletInstance = this.servletClass.newInstance();
    	// 调用servlet的init方法
    	this.servletInstance.init(new DelegatingServletConfig());
    }    
    

    当访问一个url的时候,服务请求会跳转到ZuulController中,执行handleRequest()方法。

    @Override
    public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
    	try {
    	    // 调用父类的handleRequestInternal方法
    		return super.handleRequestInternal(request, response);
    	}
    	finally {
    		RequestContext.getCurrentContext().unset();
    	}
    }
    

    handleRequestInternal()方法如下:

    protected ModelAndView handleRequestInternal(HttpServletRequest request, HttpServletResponse response)
    		throws Exception {
    
    	this.servletInstance.service(request, response);
    	return null;
    }
    

    servletInstanceZuulServlet的实例,上面的方法最终调用ZuulServlet中的service()方法。

    service()方法如下:

    @Override
    public void service(javax.servlet.ServletRequest servletRequest, javax.servlet.ServletResponse servletResponse) throws ServletException, IOException {
        try {
            init((HttpServletRequest) servletRequest, (HttpServletResponse) servletResponse);
            RequestContext context = RequestContext.getCurrentContext();
            context.setZuulEngineRan();
    
            try {
                // pre过滤器
                preRoute();
            } catch (ZuulException e) {
                error(e);
                postRoute();
                return;
            }
            try {
                // route过滤器
                route();
            } catch (ZuulException e) {
                error(e);
                postRoute();
                return;
            }
            try {
                // post过滤器
                postRoute();
            } catch (ZuulException e) {
                error(e);
                return;
            }
    
        } catch (Throwable e) {
            error(new ZuulException(e, 500, "UNHANDLED_EXCEPTION_" + e.getClass().getName()));
        } finally {
            RequestContext.getCurrentContext().unset();
        }
    }
    

    根据上面的源码我们知道,一个请求到来的时候,就要经历preRoute、route、postRoute几个阶段,用官方的图来说明

    zuul请求的生命周期

    网关请求执行的过程

    根据第上面的内容,我们知道,当通过网关对服务进行请求的时候,要经历preRoute,route、postRoute阶段,这里以以preRoute()方法为例,对路由的处理过程进行说明。

    preRoute()方法如下:

    void preRoute() throws ZuulException {
        zuulRunner.preRoute();
    }
    

    ZuulRunner中的preRoute()方法如下:

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

    FilterProcessor是一个单例模式,FilterProcessor中的preRoute()方法如下:

    public void preRoute() throws ZuulException {
        try {
            // 运行pre过滤器
            runFilters("pre");
        } catch (ZuulException e) {
            throw e;
        } catch (Throwable e) {
            throw new ZuulException(e, 500, "UNCAUGHT_EXCEPTION_IN_PRE_FILTER_" + e.getClass().getName());
        }
    }
    

    执行过滤器,runFilters()方法如下:

     public Object runFilters(String sType) throws Throwable {
        if (RequestContext.getCurrentContext().debugRouting()) {
            Debug.addRoutingDebug("Invoking {" + sType + "} type filters");
        }
        boolean bResult = false;
        // 根据过滤器类型,获取过滤器列表。
        List<ZuulFilter> 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;
    }
    

    根据类型获取过滤器列表,getFiltersByType()方法如下:

    public List<ZuulFilter> getFiltersByType(String filterType) {
    
        List<ZuulFilter> list = hashFiltersByType.get(filterType);
        if (list != null) return list;
    
        list = new ArrayList<ZuulFilter>();
        // 获取所有的过滤器
        Collection<ZuulFilter> filters = filterRegistry.getAllFilters();
        for (Iterator<ZuulFilter> iterator = filters.iterator(); iterator.hasNext(); ) {
            ZuulFilter filter = iterator.next();
            // 取得filterType的类型列表
            if (filter.filterType().equals(filterType)) {
                list.add(filter);
            }
        }
        // 对filter进行排序
        Collections.sort(list); // sort by priority
        // 保存列表
        hashFiltersByType.putIfAbsent(filterType, list);
        return list;
    }
    

    FilterRegistry类是一个单例模式,getAllFilters()方法如下

    public class FilterRegistry {
    
        private static final FilterRegistry INSTANCE = new FilterRegistry();
    
        // ....省略....
    
        public Collection<ZuulFilter> getAllFilters() {
            return this.filters.values();
        }
    
    }
    

    过滤器的处理方法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();
          
            // ....省略....
            
            // 运行filter
            ZuulFilterResult result = filter.runFilter();
            ExecutionStatus s = result.getStatus();
            execTime = System.currentTimeMillis() - ltime;
            
            // .....省略....
     
            usageNotifier.notify(filter, s);
            return o;
    
        } catch (Throwable e) {
            // .....省略.....
        }
    }
    

    runFilter()方法如下,:

    public ZuulFilterResult runFilter() {
        ZuulFilterResult zr = new ZuulFilterResult();
        if (!isFilterDisabled()) {
            // 判断过滤器是否需要执行
            if (shouldFilter()) {
                Tracer t = TracerFactory.instance().startMicroTracer("ZUUL::" + this.getClass().getSimpleName());
                try {
                    // 调用filter的run方法。
                    Object res = run();
                    zr = new ZuulFilterResult(res, ExecutionStatus.SUCCESS);
                } catch (Throwable e) {
                   // ....省略....
                } finally {
                    t.stopAndLog();
                }
            } else {
                zr = new ZuulFilterResult(ExecutionStatus.SKIPPED);
            }
        }
        return zr;
    }
    

    最终调用到各ZuulFilter中的run()方法。

    路由查找

    pre类型的PreDecorationFilter过滤器,用来进行路由规则的匹配

    如下:
    根据请求路径寻找png

    执行后,上下文内容中的内容如下,加入了requestURI
    4.png-71.1kB

    访问服务

    根据下图可以知道,真正访问服务的是route阶段。如下:
    image_1cbu5ucct12mlcro4u215s5vkk1u.png-39.4kB

    对于正常的服务,比如:/xxx/service_name是通过RibbonRoutingFilter实现对服务的负载均衡访问,它的run()方法如下:

    public Object run() {
    	RequestContext context = RequestContext.getCurrentContext();
    	this.helper.addIgnoredHeaders();
    	try {
    		RibbonCommandContext commandContext = buildCommandContext(context);
    		ClientHttpResponse response = forward(commandContext);
    		setResponse(response);
    		return response;
    	}
    	catch (ZuulException ex) {
    		throw new ZuulRuntimeException(ex);
    	}
    	catch (Exception ex) {
    		throw new ZuulRuntimeException(ex);
    	}
    }
    

    如果是固定的url链接,如:http://www.abc.com/xxx/service_name这种,则是通过SendForwardFilter过滤器实现转发。它的run()方法如下:

    public Object run() {
    	try {
    		RequestContext ctx = RequestContext.getCurrentContext();
    		String path = (String) ctx.get(FORWARD_TO_KEY);
    		RequestDispatcher dispatcher = ctx.getRequest().getRequestDispatcher(path);
    		if (dispatcher != null) {
    			ctx.set(SEND_FORWARD_FILTER_RAN, true);
    			if (!ctx.getResponse().isCommitted()) {
    			    // url转发
    				dispatcher.forward(ctx.getRequest(), ctx.getResponse());
    				ctx.getResponse().flushBuffer();
    			}
    		}
    	}
    	catch (Exception ex) {
    		ReflectionUtils.rethrowRuntimeException(ex);
    	}
    	return null;
    }
    
  • 相关阅读:
    变长参数表
    以二进制方式输出数字在内存中的存储形式
    asp.net mvc controller 获取数据
    asp.net mvc 3.0 新特性之 Model
    一个实体对象不能由多个 IEntityChangeTracker 实例引用
    iis6.0运行asp请求资源正在使用中
    view的使用
    asp.net mvc RenderAction 和RenderPartial用法
    今天
    当下的力量
  • 原文地址:https://www.cnblogs.com/liangzs/p/8946740.html
Copyright © 2011-2022 走看看