NL Gateway Implementation Analysis

NL Gateway Implementation Analysis

ZuulFilter Definition

By inheritance ZuulFilterwe can define a new filter as follows

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

ZuulFilterA method is implemented in compareTo(), which determines the execution order of filters of the same type according to its value. compareTo()Methods as below:

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

Register ZuulFilter to the spring container

ZuulFilterIt can be incorporated into spring's life cycle @Componentthrough @Beaninstantiation.

@Configuration
public class FilterConfig {

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

ZuulServerAutoConfigurationIt is autowired in filter, and everything instantiated by spring ZuulFilterwill be autowired into the 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);
    }

}

The above code will call ZuulFilterInitializerthe constructor.

ZuulFilterInitializerThe annotation is contextInitialized()enabled in @PostConstruct. After initialization, the container will call contextInitialized()the method to save all filters to filterRegistry, which filterRegistryis a singleton object.

contextInitialized()Methods as below:

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

Custom route forwarding rules

ZuulProxyAutoConfigurationThe class registered in the RouteLocatorclass will be automatically injected into the implementation class of RouteLocator according to the type.bean@Bean

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

RouteLocatorinstantiate

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

}

CustomRouteLocatorImplement the function of custom routing, the class is as follows.


public class CustomRouteLocator extends DiscoveryClientRouteLocator {
    // ....省略....

    @Override
    // 重写
    public Route getMatchingRoute(String path) {
        // ....省略....
        //可以从数据库中读取路由规则,并进行处理
    }

    // 重写
    @Override
    protected LinkedHashMap<String, ZuulRoute> locateRoutes() {
        // ....省略....
    }
}

Servlet initialization

Why you can automatically jump to zuul through the access gateway is actually implemented through the servlet, which /filters the root path. The following describes the initialization content of the servlet.

ZuulServerAutoConfigurationdefined in the classZuulController

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

ZuulControllerinherited ServletWrappingControllerclass

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();
        }
    }

}

ServletWrappingControllerZuulServletinstantiate _

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

When accessing a url, the service request will jump to ZuulControllerand execute the handleRequest()method.

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

handleRequestInternal()Methods as below:

protected ModelAndView handleRequestInternal(HttpServletRequest request, HttpServletResponse response)
        throws Exception {

    this.servletInstance.service(request, response);
    return null;
}

servletInstancei.e. ZuulServletthe instance where the above method ends up calling ZuulServletthe service()method in.

service()Methods as below:

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

According to the above source code, we know that when a request arrives, it will go through several stages of preRoute, route, and postRoute, which are illustrated by the official diagram.

The life cycle of zuul requests

The process that the gateway requests to execute

According to the above content, we know that when a service is requested through the gateway, it must go through the stages of preRoute, route, and postRoute. Here we take the preRoute()method as an example to describe the routing process.

preRoute()Methods as below:

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

ZuulRunnerThe preRoute()method in is as follows:

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

FilterProcessoris a singleton pattern with FilterProcessorthe preRoute()following methods:

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());
    }
}

Execute the filter runFilters()as follows:

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

Get a list of filters by type getFiltersByType()as follows:

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

FilterRegistryThe class is a singleton pattern, the getAllFilters()method is as follows

public class FilterRegistry {

    private static final FilterRegistry INSTANCE = new FilterRegistry();

    // ....省略....

    public Collection<ZuulFilter> getAllFilters() {
        return this.filters.values();
    }

}

The filter is handled processZuulFilter()as follows:

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()Methods as below,:

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

ZuulFilterThe method that is eventually called into each run().

route lookup

preThe type of PreDecorationFilterfilter used to match routing rules

as follows:
Find png according to the request path

After execution, the content in the context content is as follows, addingrequestURI
4.png-71.1kB

access service

According to the figure below, it can be known that the real access to the service is the rote phase. as follows:
image_1cbu5ucct12mlcro4u215s5vkk1u.png-39.4kB

For normal services, for example, /xxx/service_nameby RibbonRoutingFilterimplementing load balancing access to services, its run()methods are as follows:

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

If it is a fixed url link, such as: http://www.abc.com/xxx/service_namethis, it is SendForwardFilterforwarded through a filter. The run()method is as follows:

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

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=324933016&siteId=291194637