Spring Cloud Zuul源码详解

目录

代码示例

实现原理

DispatcherServlet

ZuulHandlerMapping

RouteLocator

ZuulController

ZuulServlet

ZuulFilterConfiguration

查看路由规则

路由规则配置

忽略路由规则

自定义路由规则

查看Filter

自定义Filter


Java版本:1.8

Spring版本:5.1.8.RELEASE

Spring Boot版本:2.1.6.RELEASE

Spring Cloud版本:Greenwich.SR1

代码示例

添加spring-cloud-starter-netflix-zuul依赖

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>2.1.6.RELEASE</version>
		<relativePath/> <!-- lookup parent from repository -->
	</parent>
	<groupId>com.sean</groupId>
	<artifactId>test-zuul</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<name>test-zuul</name>
	<description>test-zuul</description>

	<properties>
		<java.version>1.8</java.version>
		<spring-cloud.version>Greenwich.SR1</spring-cloud.version>
	</properties>

	<dependencies>
		<dependency>
			<groupId>org.springframework.cloud</groupId>
			<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
		</dependency>

		<dependency>
			<groupId>org.springframework.cloud</groupId>
			<artifactId>spring-cloud-starter-netflix-zuul</artifactId>
		</dependency>

		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>

		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
		</dependency>

		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-web</artifactId>
		</dependency>
	</dependencies>

	<dependencyManagement>
		<dependencies>
			<dependency>
				<groupId>org.springframework.cloud</groupId>
				<artifactId>spring-cloud-dependencies</artifactId>
				<version>${spring-cloud.version}</version>
				<type>pom</type>
				<scope>import</scope>
			</dependency>
		</dependencies>
	</dependencyManagement>

	<build>
		<plugins>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
			</plugin>
		</plugins>
	</build>
</project>

启动类设置开启Zuul 

package com.sean;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.zuul.EnableZuulProxy;

@SpringBootApplication
@EnableZuulProxy
public class TestZuulApplication {

	public static void main(String[] args) {
		SpringApplication.run(TestZuulApplication.class, args);
	}
}
server.port=7000
spring.application.name=test-zuul

eureka.client.service-url.defaultZone=http://localhost:8000/eureka

# 是否注册至eureka,默认值为true,如果为true,需要配置eureka.client.service-url.defaultZone
# 否则会访问默认eureka地址:http://localhost:8761/eureka/
# 本服务仅供测试,不对外提供服务,因此不需要注册到eureka中
eureka.client.register-with-eureka=true

# 服务续租任务执行时间间隔
eureka.instance.lease-renewal-interval-in-seconds=10
# 服务过期时间
eureka.instance.lease-expiration-duration-in-seconds=20

management.endpoints.web.exposure.include=routes,filters

启动Eureka-Server后,启动Test-Service和Test-Service2,然后调用http://localhost:7000/test-service/name(调用http://localhost:7000/zuul/test-service/name也是可以的,具体原因参见下面的ZuulServlet),会间次获得sean和sean two

实现原理

我们从pom文件开始

spring-cloud-starter-netflix-zuul依赖spring-cloud-netflix-zuul

spring-cloud-netflix-zuul的代码结构如下

 spring.factories配置中指定了AutoConfiguration类

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.cloud.netflix.zuul.ZuulServerAutoConfiguration,\
org.springframework.cloud.netflix.zuul.ZuulProxyAutoConfiguration

我们先看一下ZuulServerAutoConfiguration类

@Configuration
@EnableConfigurationProperties({ ZuulProperties.class })
@ConditionalOnClass({ ZuulServlet.class, ZuulServletFilter.class })
@ConditionalOnBean(ZuulServerMarkerConfiguration.Marker.class)
public class ZuulServerAutoConfiguration {
    ......
}

ZuulServerMarkerConfiguration.Marker类的实例是在EnableZuulServer注解中创建的

如果我们使用EnableZuulServer注解,将只加载ZuulServerAutoConfiguration类中的配置信息

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(ZuulServerMarkerConfiguration.class)
public @interface EnableZuulServer {}
​@Configuration
public class ZuulServerMarkerConfiguration {

	@Bean
	public Marker zuulServerMarkerBean() {
		return new Marker();
	}

	class Marker {}
}

 再看下ZuulProxyAutoConfiguration类,ZuulProxyAutoConfiguration类继承自ZuulServerAutoConfiguration

@Configuration
......
@ConditionalOnBean(ZuulProxyMarkerConfiguration.Marker.class)
public class ZuulProxyAutoConfiguration extends ZuulServerAutoConfiguration {
    ......
}

ZuulProxyMarkerConfiguration.Marker类的实例是在EnableZuulProxy注解中创建的

如果我们使用EnableZuulProxy注解,将会加载ZuulProxyAutoConfiguration类中的配置的信息,由于ZuulProxyAutoConfiguration继承ZuulServerAutoConfiguration类,ZuulServerAutoConfiguration类中的配置信息也会被加载(为什么@ConditionalOnBean(ZuulServerMarkerConfiguration.Marker.class)判定未生效?

@EnableCircuitBreaker
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Import(ZuulProxyMarkerConfiguration.class)
public @interface EnableZuulProxy {}
@Configuration
public class ZuulProxyMarkerConfiguration {

    @Bean
    public Marker zuulProxyMarkerBean() {
        return new Marker();
    }

    class Marker {}
}

因此比起EnableZuulServer注解,EnableZuulProxy注解将额外提供三个在ZuulProxyAutoConfiguration类中定义的Filter:

PreDecorationFilter,RibbonRoutingFilter,SimpleHostRoutingFilter

DispatcherServlet

根据Spring MVC的实现原理,我们知道所有的Http请求将被发送至DispatcherServlet,DispatcherServlet内部维护了所有的handlerMapping,如果请求path注册在handlerMapping中,则从handlerMapping获取path对应的handler进行处理

public class DispatcherServlet extends FrameworkServlet {

    @Nullable
    private List<HandlerMapping> handlerMappings;
    ......

    @Override
    protected void doService(HttpServletRequest request, HttpServletResponse response) 
            throws Exception {
        ......
        doDispatch(request, response);
        ......
    }
	
    protected void doDispatch(HttpServletRequest request, HttpServletResponse response) 
            throws Exception {
        ......
        HandlerExecutionChain mappedHandler = getHandler(processedRequest);
        ......
        ModelAndView mv = ha.handle(processedRequest, response,
                mappedHandler.getHandler());
        ......
    }
	
    @Nullable
    protected HandlerExecutionChain getHandler(HttpServletRequest request) 
            throws Exception {
        if (this.handlerMappings != null) {
            for (HandlerMapping mapping : this.handlerMappings) {
                HandlerExecutionChain handler = mapping.getHandler(request);
                if (handler != null) {
                    return handler;
                }
            }
        }
        return null;
    }
    ......
}

ZuulHandlerMapping

ZuulHandlerMapping用来根据请求path,查找对应的handler

当然ZuulHandlerMapping的创建依赖于RouteLocator和ZuulController 

@Bean
public ZuulHandlerMapping zuulHandlerMapping(RouteLocator routes) {
    ZuulHandlerMapping mapping = new ZuulHandlerMapping(routes, zuulController());
    mapping.setErrorController(this.errorController);
    mapping.setCorsConfigurations(getCorsConfigurations());
    return mapping;
}

所有从RouteLocator中获取的路径(this.routeLocator.getRoutes()),均将与ZuulController绑定

public class ZuulHandlerMapping extends AbstractUrlHandlerMapping {

    private final RouteLocator routeLocator;
    private final ZuulController zuul;
    ......

    public ZuulHandlerMapping(RouteLocator routeLocator, ZuulController zuul) {
        this.routeLocator = routeLocator;
        this.zuul = zuul;
        setOrder(-200);
    }

    private void registerHandlers() {
        Collection<Route> routes = this.routeLocator.getRoutes();
        ......
        for (Route route : routes) {
            //调用父类AbstractUrlHandlerMapping中的registerHandler方法
            registerHandler(route.getFullPath(), this.zuul);
        }
    }

    @Override
    protected Object lookupHandler(String urlPath, HttpServletRequest request)
            throws Exception {
        ......
        //如果是系统启动后第一次接收到请求,或者Eureka中的服务发生变化时
        //在查找handler之前,会重新调用registerHandlers方法
        //注册最新的URL Path与Handler对应关系
        registerHandlers();
        return super.lookupHandler(urlPath, request);
    }
}

ZuulHandlerMapping类继承AbstractUrlHandlerMapping类

public abstract class AbstractUrlHandlerMapping extends AbstractHandlerMapping implements MatchableHandlerMapping {

    // 用来保存所有URL Path和Handler的关联关系
    private final Map<String, Object> handlerMap = new LinkedHashMap<>();
    ......
	
    //Register the specified handler for the given URL path.
    protected void registerHandler(String urlPath, Object handler) throws BeansException, IllegalStateException {
        Object resolvedHandler = handler;
        ......
        Object mappedHandler = this.handlerMap.get(urlPath);
        if (mappedHandler != null) {
            if (mappedHandler != resolvedHandler) {
                throw new IllegalStateException("无法匹配URL和handler,该URL已经和其它Handler关联");
            }
        }
        else {
            // The handler to be registered for the root path ("/")
            if (urlPath.equals("/")) {
                setRootHandler(resolvedHandler);
            }
            // This handler will be returned if no specific mapping was found
            else if (urlPath.equals("/*")) {
                setDefaultHandler(resolvedHandler);
            }
            else {
                this.handlerMap.put(urlPath, resolvedHandler);
            }
        }
    }
	
    @Nullable
    protected Object lookupHandler(String urlPath, HttpServletRequest request) throws Exception {
        // 直接匹配
        Object handler = this.handlerMap.get(urlPath);
        if (handler != null) {
            ......
            return buildPathExposingHandler(handler, urlPath, urlPath, null);
        }
        // 模糊匹配
        ......
    }
    ......
}

RouteLocator

CompositeRouteLocator的作用是为了整合可能存在的多个RouteLocator 

@Bean
@Primary
public CompositeRouteLocator primaryRouteLocator(
        Collection<RouteLocator> routeLocators) {
    // RouteLocator that composes multiple RouteLocators.
    return new CompositeRouteLocator(routeLocators);
}
public class CompositeRouteLocator implements RefreshableRouteLocator {

    private final Collection<? extends RouteLocator> routeLocators;
    private ArrayList<RouteLocator> rl;

    public CompositeRouteLocator(Collection<? extends RouteLocator> routeLocators) {
        Assert.notNull(routeLocators, "'routeLocators' must not be null");
        rl = new ArrayList<>(routeLocators);
        AnnotationAwareOrderComparator.sort(rl);
        this.routeLocators = rl;
    }

    @Override
    public List<Route> getRoutes() {
        List<Route> route = new ArrayList<>();
        for (RouteLocator locator : routeLocators) {
            route.addAll(locator.getRoutes());
        }
        return route;
    }
    ......
}

如果使用EnableZuulServer注解,则创建SimpleRouteLocator类的实例,并用其构建CompositeRouteLocator

@Bean
@ConditionalOnMissingBean(SimpleRouteLocator.class)
public SimpleRouteLocator simpleRouteLocator() {
    return new SimpleRouteLocator(this.server.getServlet().getContextPath(),
            this.zuulProperties);
}

SimpleRouteLocator仅能加载ZuulProperties中配置的路径

public class SimpleRouteLocator implements RouteLocator, Ordered {
    private ZuulProperties properties;
    private AtomicReference<Map<String, ZuulRoute>> routes = 
        new AtomicReference<>();
    ......

    public SimpleRouteLocator(String servletPath, ZuulProperties properties) {
        this.properties = properties;
        if (StringUtils.hasText(servletPath)) {
            this.dispatcherServletPath = servletPath;
        }
        this.zuulServletPath = properties.getServletPath();
    }
	
    @Override
    public List<Route> getRoutes() {
        List<Route> values = new ArrayList<>();
        for (Entry<String, ZuulRoute> entry : getRoutesMap().entrySet()) {
            ZuulRoute route = entry.getValue();
            String path = route.getPath();
            values.add(getRoute(route, path));
            ......
        }
        return values;
    }
	
    protected Map<String, ZuulRoute> getRoutesMap() {
        if (this.routes.get() == null) {
            this.routes.set(locateRoutes());
        }
        return this.routes.get();
    }
	
    // Compute a map of path pattern to route. The default is just a static map 
    // from the ZuulProperties.
    protected Map<String, ZuulRoute> locateRoutes() {
        LinkedHashMap<String, ZuulRoute> routesMap = new LinkedHashMap<>();
        for (ZuulRoute route : this.properties.getRoutes().values()) {
            routesMap.put(route.getPath(), route);
        }
        return routesMap;
    }
    ......
}

如果使用EnableZuulProxy注解,则创建DiscoveryClientRouteLocator类的实例,并用其构建CompositeRouteLocator

@Bean
@ConditionalOnMissingBean(DiscoveryClientRouteLocator.class)
public DiscoveryClientRouteLocator discoveryRouteLocator() {
    return new DiscoveryClientRouteLocator(this.server.getServlet().getContextPath(),
            this.discovery, this.zuulProperties, this.serviceRouteMapper,
            this.registration);
}

由于DiscoveryClientRouteLocator继承SimpleRouteLocator,所以DiscoveryClientRouteLocator不但可以加载ZuulProperties中配置的路径,还能通过DiscoveryClient从Eureka中获取服务地址

public class DiscoveryClientRouteLocator extends SimpleRouteLocator
        implements RefreshableRouteLocator {
	
    public DiscoveryClientRouteLocator(String servletPath, DiscoveryClient discovery,
            ZuulProperties properties, ServiceInstance localServiceInstance) {
        // SimpleRouteLocator
        super(servletPath, properties);
        ......
    }
	
    // 重写了SimpleRouteLocator中的locateRoutes方法
    @Override
    protected LinkedHashMap<String, ZuulRoute> locateRoutes() {
        LinkedHashMap<String, ZuulRoute> routesMap = new LinkedHashMap<>();
        // SimpleRouteLocator中的locateRoutes
        // 也就是所有ZuulProperties中配置的路由规则 
        routesMap.putAll(super.locateRoutes());
        if (this.discovery != null) {
            ......
            // 通过DiscoveryClient从Eureka获取的所有服务地址
            List<String> services = this.discovery.getServices();
            for (String serviceId : services) {
                String key = "/" + mapRouteToService(serviceId) + "/**";
                ......
                routesMap.put(key, new ZuulRoute(key, serviceId));
            }
        }
        ......
        LinkedHashMap<String, ZuulRoute> values = new LinkedHashMap<>();
        for (Entry<String, ZuulRoute> entry : routesMap.entrySet()) {
            String path = entry.getKey();
            if (!path.startsWith("/")) {
                path = "/" + path;
            }
            if (StringUtils.hasText(this.properties.getPrefix())) {
                path = this.properties.getPrefix() + path;
                if (!path.startsWith("/")) {
                    path = "/" + path;
                }
            }
            values.put(path, entry.getValue());
        }
        return values;
    }
    ......
}

ZuulController

ZuulController的创建需要ZuulServlet

public class ZuulController extends ServletWrappingController {

    public ZuulController() {
        setServletClass(ZuulServlet.class);
        setServletName("zuul");
        setSupportedMethods((String[]) null); // Allow all
    }

    @Override
    public ModelAndView handleRequest(HttpServletRequest request,
            HttpServletResponse response) throws Exception {
        return super.handleRequestInternal(request, response);
    }
}

ZuulController继承自ServletWrappingController,从ServletWrappingController类的注释我们可以很清楚的明白其作用是:一个Spring的Controller包裹了一个内部的Servlet,使我们可以通过Spring的调度方式调用Servlet

/**
 * Spring Controller implementation that wraps a servlet instance which it manages
 * internally. Such a wrapped servlet is not known outside of this controller;
 * its entire lifecycle is covered here.
 *
 * Useful to invoke an existing servlet via Spring's dispatching infrastructure...
 */
public class ServletWrappingController extends AbstractController
        implements BeanNameAware, InitializingBean, DisposableBean {
		
    @Nullable
    private Class<? extends Servlet> servletClass;
    @Nullable
    private String servletName;
    @Nullable
    private Servlet servletInstance;
    ......
	
    public void setServletClass(Class<? extends Servlet> servletClass) {
        this.servletClass = servletClass;
    }
	
    public void setServletName(String servletName) {
        this.servletName = servletName;
    }
	
    @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 = ReflectionUtils.accessibleConstructor(this.servletClass).newInstance();
        this.servletInstance.init(new DelegatingServletConfig());
    }
	
    @Override
    protected ModelAndView handleRequestInternal(HttpServletRequest request, HttpServletResponse response)
            throws Exception {
        Assert.state(this.servletInstance != null, "No Servlet instance");
        this.servletInstance.service(request, response);
        return null;
    }
    ......
}

ZuulServlet

通过ServletRegistrationBean向Servlet容器中注册了一个ZuulServlet,ZuulServlet注册的ServletPattern是/zuul/*,因此直接访问http://localhost:7000/zuul/test-service/name也是可以成功的,只是这种访问方式属于直接访问Servlet,跳过了Spring MVC的dispatch机制

@Bean
@ConditionalOnMissingBean(name = "zuulServlet")
@ConditionalOnProperty(name = "zuul.use-filter", havingValue = "false", matchIfMissing = true)
public ServletRegistrationBean zuulServlet() {
    // ServletRegistrationBean is a servletContextInitializer 
    // to register Servlets in a Servlet 3.0+ container.
    ServletRegistrationBean<ZuulServlet> servlet = new ServletRegistrationBean<>(
            new ZuulServlet(), 
            this.zuulProperties.getServletPattern()); // /zuul/*
    servlet.addInitParameter("buffer-requests", "false");
    return servlet;
}

从ZuulServlet的service方法可以很清晰的了解Zuul中各类Filter的执行时机

// Core Zuul servlet which intializes and orchestrates zuulFilter execution
public class ZuulServlet extends HttpServlet {

    private ZuulRunner zuulRunner;

    @Override
    public void init(ServletConfig config) throws ServletException {
        ......
        zuulRunner = new ZuulRunner(bufferReqs);
    }

    @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 {
                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();
        }
    }
	
    //executes "route" filters
    void route() throws ZuulException {
        zuulRunner.route();
    }
    ......
}

ZuulRunner没什么特别需要介绍的地方

public class ZuulRunner {

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

FilterProcessor的代码如下,功能同类名

public class FilterProcessor {
    static FilterProcessor INSTANCE = new FilterProcessor();

    public FilterProcessor() {
        usageNotifier = new BasicFilterUsageNotifier();
    }

    public static FilterProcessor getInstance() {
        return INSTANCE;
    }
	
    public void route() throws ZuulException {
        runFilters("route");
        ......
    }
	
    public Object runFilters(String sType) throws Throwable {
        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);
                ......
            }
        }
        ......
    }
    ......
}

FilterLoader

public class FilterLoader {
    final static FilterLoader INSTANCE = new FilterLoader();
	
    private FilterRegistry filterRegistry = FilterRegistry.instance();
	
    public List<ZuulFilter> getFiltersByType(String filterType) {
        ......
        list = new ArrayList<ZuulFilter>();

        Collection<ZuulFilter> filters = filterRegistry.getAllFilters();
        for (Iterator<ZuulFilter> iterator = filters.iterator(); iterator.hasNext(); ) {
            ZuulFilter filter = iterator.next();
            if (filter.filterType().equals(filterType)) {
                list.add(filter);
            }
        }
        Collections.sort(list); // sort by priority
        ......
        return list;
    }
    ......
}

FilterRegistry的结构特别简单,单纯的维护了一个ConcurrentHashMap用来保存所有的ZuulFilter

public class FilterRegistry {
    private static final FilterRegistry INSTANCE = new FilterRegistry();

    public static final FilterRegistry instance() {
        return INSTANCE;
    }

    private final ConcurrentHashMap<String, ZuulFilter> filters = new ConcurrentHashMap<String, ZuulFilter>();
    ......
}

接下来我们需要查看一下Filter的加载过程

ZuulFilterConfiguration

@Configuration
protected static class ZuulFilterConfiguration {

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

 可以看见共计10个Filter会被加载

FilterRegistry中的Filter是在ZuulFilterInitializer类初始化过程中放置进去的

public class ZuulFilterInitializer {

    private final FilterRegistry filterRegistry;

    public ZuulFilterInitializer(Map<String, ZuulFilter> filters,
            CounterFactory counterFactory, TracerFactory tracerFactory,
            FilterLoader filterLoader, FilterRegistry filterRegistry) {
        this.filters = filters;
        this.counterFactory = counterFactory;
        this.tracerFactory = tracerFactory;
        this.filterLoader = filterLoader;
        this.filterRegistry = filterRegistry;
    }
	
    @PostConstruct
    public void contextInitialized() {
        for (Map.Entry<String, ZuulFilter> entry : this.filters.entrySet()) {
            filterRegistry.put(entry.getKey(), entry.getValue());
        }
    }
    ......
}

查看路由规则

spring-cloud-starter-netflix-zuul依赖spring-boot-starter-actuator,所以我们不需要添加额外的actuator依赖

修改Spring Cloud配置信息,添加如下配置项

#actuator默认不开启routes结点,我们打开所有节点
management.endpoints.web.exposure.include=*
#该设置让我们可以使用format参数看见更详细的路由信息
management.endpoint.routes.enabled=true

http://localhost:7000/actuator/routes

通过添加format参数,我们可以得到更详细的路由信息

http://localhost:7000/actuator/routes/details

路由规则配置

配置的方式比较多,这里简单介绍两种

忽略路由规则

修改Spring Cloud配置信息,添加如下配置项

#忽略特定的微服务
zuul.ignored-services=test-service

配置完成后效果如下,之前test-service相关的转发规则已经不见了 

请求http://localhost:7000/test-service/name也变成了404

自定义路由规则

修改Spring Cloud配置信息,添加如下配置项

#/abc/**路径的请求将被转发至test-service
#例如/abc/name请求将被转发至test-service服务的/name
zuul.routes.customer-defination.serviceId=test-service
zuul.routes.customer-defination.path=/abc/**

转发效果如下

查看Filter

前面查看路由信息时已经添加了相关配置,无需再次修改

#actuator默认不开启routes结点,我们打开所有节点
management.endpoints.web.exposure.include=*

访问http://localhost:7000/actuator/filters可以查看所有加载的Filter

自定义Filter

实现自定义的Filter仅需继承ZuulFilter类

package com.sean;

import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import com.netflix.zuul.exception.ZuulException;
import org.springframework.cloud.netflix.zuul.filters.support.FilterConstants;

import javax.servlet.http.HttpServletRequest;

public class TestFilter extends ZuulFilter {
    @Override
    public String filterType() {//过滤器类型
        return FilterConstants.PRE_TYPE;
    }

    @Override
    public int filterOrder() {//该过滤器在同类型过滤器中的执行顺序
        return 99;
    }

    @Override
    public boolean shouldFilter() {//过滤器是否执行
        return true;
    }

    @Override
    public Object run() throws ZuulException {//过滤器具体逻辑
        RequestContext rc = RequestContext.getCurrentContext();
        HttpServletRequest req = rc.getRequest();
        System.out.println("get request : " + req.getRequestURI());
        return null;
    }
}

然后修改项目启动类,以便在项目启动时创建TestFilter类的实例

package com.sean;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.zuul.EnableZuulProxy;
import org.springframework.context.annotation.Bean;

@SpringBootApplication
@EnableZuulProxy
public class TestZuulApplication {

    @Bean
    public TestFilter testFilter(){
        return new TestFilter();
    }

    public static void main(String[] args) {
        SpringApplication.run(TestZuulApplication.class, args);
    }
}

启动工程来看看效果

可见加载的Filter由之前的10个变为了11个,自定义的TestFilter位列其中

访问http://localhost:7000/abc/name可在后台看见如下日志

get request : /abc/name

 


版权声明:本文为a19881029原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。