1.Zuul简介
1.1 zuul的位置
zuul就像是整个微服务架构系统的门面,所有的外部客户端请求都需要经过它进行路由。
1.2 zuul执行流程
简单来说zuul将请求转发,并提供pre,post,error类型过滤器,这些过滤器执行的时间不同,对应不同的阶段。
2.使用
2.1.添加依赖
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-zuul</artifactId> </dependency>
2.2请求路由
传统路由方式
zuul.routes.api-server.path=/test/** zuul.routes.api-server.url=http://localhost:8011/面向服务的路由方式
zuul.routes.api-favorite.path=/Favorite/** zuul.routes.api-favorite.serviceId=mem-favorite
本地跳转
zuul.routes.api-b.path=/api-b/**
zuul.routes.api-b.url=forward:/api/b
zull默认会将通过服务名(从服务中心拉取的信息)作为ContextPath的方式来创建路由映射。
2.3请求过滤
class PreFilter extends ZuulFilter { @Override String filterType() { return "pre" } @Override int filterOrder() { return -4 } @Override boolean shouldFilter() { return true } @Override Object run() { HttpServletRequest context = RequestContext.getCurrentContext().getRequest() System.out.println("this is a pre filter") return null } }
通过继承ZuulFilter抽象类并覆盖下面4个方法来实现自定义过滤器。
filterType:过滤器类型,它决定了过滤器执行的时机。
filterOrder:过滤器的属性。相同类型过滤器有多个的时候,通过这个决定执行的顺序。
shouldFilter:判断该过滤器是否需要执行。
run:过滤器的具体逻辑。
2.4自定义路由映射规则
public class PatternServiceRouteMapper implements ServiceRouteMapper { /** * A RegExp Pattern that extract needed information from a service ID. Ex : * "(?<name>.*)-(?<version>v.*$)" */ private Pattern servicePattern; /** * A RegExp that refer to named groups define in servicePattern. Ex : * "${version}/${name}" */ private String routePattern; public PatternServiceRouteMapper(String servicePattern, String routePattern) { this.servicePattern = Pattern.compile(servicePattern); this.routePattern = routePattern; }
只要创建PatternServiceRouteMapper Bean就可以了。参数是两个正则表达式。
2.5路径匹配
? 匹配任意单个字符
* 匹配任意数量的字符
** 匹配任意的字符,支持多级目录
3.zuul原理
3.1 ZuulServlet整体执行流程
@Override public void service(javax.servlet.ServletRequest servletRequest, javax.servlet.ServletResponse servletResponse) throws ServletException, IOException { 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(); } }
继续往下看,看到FilterProcessor类几个方法
public void postRoute() throws ZuulException { try { runFilters("post"); } catch (ZuulException e) { throw e; } catch (Throwable e) { throw new ZuulException(e, 500, "UNCAUGHT_EXCEPTION_IN_POST_FILTER_" + e.getClass().getName()); } }
public void error() { try { runFilters("error"); } catch (Throwable e) { logger.error(e.getMessage(), e); } }
public void route() throws ZuulException { try { runFilters("route"); } catch (ZuulException e) { throw e; } catch (Throwable e) { throw new ZuulException(e, 500, "UNCAUGHT_EXCEPTION_IN_ROUTE_FILTER_" + e.getClass().getName()); } }
public void preRoute() throws ZuulException { try { runFilters("pre"); } catch (ZuulException e) { throw e; } catch (Throwable e) { throw new ZuulException(e, 500, "UNCAUGHT_EXCEPTION_IN_PRE_FILTER_" + e.getClass().getName()); } }继续看最终我们找到通过getFiltersByType获得相应的ZuulFilter
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(); if (filter.filterType().equals(filterType)) { list.add(filter); } } Collections.sort(list); // sort by priority hashFiltersByType.putIfAbsent(filterType, list); return list; }我们看filterRegistry,查看谁调用了put方法我们找到了 ZuulFilterInitializer
@Override public void contextInitialized(ServletContextEvent sce) { log.info("Starting filter initializer context listener"); TracerFactory.initialize(tracerFactory); CounterFactory.initialize(counterFactory); for (Map.Entry<String, ZuulFilter> entry : this.filters.entrySet()) { filterRegistry.put(entry.getKey(), entry.getValue()); } }
我们看到是通过filters获得的,继续查看是谁实例化了ZuulFilterInitializer对象,我们找到了ZuulConfiguration类。代码如下
@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); } }最终我们看到了是通过自动注入获得了所有的ZuulFilter
@Autowired private Map<String, ZuulFilter> filters;
3.2 Zuul默认提供的ZuulFilter
3.3动态路由
和配置中心结合加入如下代码即可
@Bean @RefreshScope @ConfigurationProperties("zuul") public ZuulProperties zullProperties() { return new ZuulProperties(); }