路由

路由是网关的核心功能,既然在spring的框架下,那就要按Spring的规矩来。

路由规则类:org.springframework.cloud.netflix.zuul.filters.Route 维护这以下信息:

private String id;

private String fullPath;

private String path;

private String location;

private String prefix;

private Boolean retryable;

private Set<String> sensitiveHeaders = new LinkedHashSet<>();

private boolean customSensitiveHeaders;

private boolean prefixStripped = true;

路由规则维护:RouteLocator

public interface RouteLocator {

   /**
* Ignored route paths (or patterns), if any.
*/
Collection<String> getIgnoredPaths(); /**
* A map of route path (pattern) to location (e.g. service id or URL).
*/
List<Route> getRoutes(); /**
* Maps a path to an actual route with full metadata.
*/
Route getMatchingRoute(String path); }

类结构如下:

自动配置代码:

@Bean
@ConditionalOnMissingBean(DiscoveryClientRouteLocator.class)
public DiscoveryClientRouteLocator discoveryRouteLocator() {
return new DiscoveryClientRouteLocator(this.server.getServletPrefix(),
this.discovery, this.zuulProperties, this.serviceRouteMapper, this.registration);
} @Bean
@Primary
public CompositeRouteLocator primaryRouteLocator(
Collection<RouteLocator> routeLocators) {
return new CompositeRouteLocator(routeLocators);
} @Bean
@ConditionalOnMissingBean(SimpleRouteLocator.class)
public SimpleRouteLocator simpleRouteLocator() {
return new SimpleRouteLocator(this.server.getServletPrefix(),
this.zuulProperties);
}

这里会使用DiscoveryClientRouteLocator,它做了一个事就是利用DiscoveryClient把注册中心的信息捞过来直接做映射成为路由规则列表。具体代码写的也有差点意思的,比如下图:

首先有ZuulController和ZuulHandlerMapping,请求进来先在ZuulHandlerMapping里的列表上找有没有,如果有就把请求丢给ZuulController处理。所以里面一定维护这个一个<path,ZuulController>这么个map。这个匹配规则哪里来呢,一般我们认为是配置,但这里用了spring cloud,它加了注册的微服务动态加入匹配规则的逻辑,也就是下面的DiscoveryClientRouteLocator。

ZuulHandlerMapping的lookupHandler方法:

protected Object lookupHandler(String urlPath, HttpServletRequest request) throws Exception {
if (this.errorController != null && urlPath.equals(this.errorController.getErrorPath())) {
return null;
}
if (isIgnoredPath(urlPath, this.routeLocator.getIgnoredPaths())) return null;
RequestContext ctx = RequestContext.getCurrentContext();
if (ctx.containsKey("forward.to")) {
return null;
}
if (this.dirty) {
synchronized (this) {
if (this.dirty) {
registerHandlers();
this.dirty = false;
}
}
}
return super.lookupHandler(urlPath, request);
} private void registerHandlers() {
Collection<Route> routes = this.routeLocator.getRoutes();
if (routes.isEmpty()) {
this.logger.warn("No routes found from RouteLocator");
}
else {
for (Route route : routes) {
registerHandler(route.getFullPath(), this.zuul);
}
}
}

这里看到this.dirty来控制是不是重新调用registerHandlers,看代码是执行一遍后,列表被存下来后面进来就用这个列表就行了。这里有一个点,就是每次心跳事件,就是应用和注册中心保持的心跳的时候会把这个重新改成true,从而再执行到locateRoutes方法,就可以重新在内存里拿注册中心可能同步过来的新的信息。

注意ZuulProxyAutoConfiguration中ZuulDiscoveryRefreshListener的onApplicationEvent方法:

public void onApplicationEvent(ApplicationEvent event) {
if (event instanceof InstanceRegisteredEvent) {
reset();
}
else if (event instanceof ParentHeartbeatEvent) {
ParentHeartbeatEvent e = (ParentHeartbeatEvent) event;
resetIfNeeded(e.getValue());
}
else if (event instanceof HeartbeatEvent) {
HeartbeatEvent e = (HeartbeatEvent) event;
resetIfNeeded(e.getValue());
} } private void resetIfNeeded(Object value) {
if (this.monitor.update(value)) {
reset();
}
} private void reset() {
this.zuulHandlerMapping.setDirty(true);
}

locateRoutes方法就是从注册中心同步过来的所有注册的应用,转成一个个路由规则,如果自己在配置上配置了的路由规则,则按配置的来,没配置的就补上。这里注意了,也就是说不管你配置没配置,只要注册上来的,这个网关的路由规则上就有!问题是,很多微服务提供的接口并不想给网关用,甚至从分层的角度上来说某些应用属于基础服务应用,只想给上层的业务应用调用,并不想直接由网关暴露出去。那么是不是有问题呢,这个也不算是问题,人家提供的框架本来就是要你在这个基础上改造的。这个后续再讲。

locateRoutes方法:

protected LinkedHashMap<String, ZuulRoute> locateRoutes() {
LinkedHashMap<String, ZuulRoute> routesMap = new LinkedHashMap<String, ZuulRoute>();
routesMap.putAll(super.locateRoutes());
if (this.discovery != null) {
Map<String, ZuulRoute> staticServices = new LinkedHashMap<String, ZuulRoute>();
for (ZuulRoute route : routesMap.values()) {
String serviceId = route.getServiceId();
if (serviceId == null) {
serviceId = route.getId();
}
if (serviceId != null) {
staticServices.put(serviceId, route);
}
}
// Add routes for discovery services by default
List<String> services = this.discovery.getServices();
String[] ignored = this.properties.getIgnoredServices()
.toArray(new String[0]);
for (String serviceId : services) {
// Ignore specifically ignored services and those that were manually
// configured
String key = "/" + mapRouteToService(serviceId) + "/**";
if (staticServices.containsKey(serviceId)
&& staticServices.get(serviceId).getUrl() == null) {
// Explicitly configured with no URL, cannot be ignored
// all static routes are already in routesMap
// Update location using serviceId if location is null
ZuulRoute staticRoute = staticServices.get(serviceId);
if (!StringUtils.hasText(staticRoute.getLocation())) {
staticRoute.setLocation(serviceId);
}
}
if (!PatternMatchUtils.simpleMatch(ignored, serviceId)
&& !routesMap.containsKey(key)) {
// Not ignored
routesMap.put(key, new ZuulRoute(key, serviceId));
}
}
}
if (routesMap.get(DEFAULT_ROUTE) != null) {
ZuulRoute defaultRoute = routesMap.get(DEFAULT_ROUTE);
// Move the defaultServiceId to the end
routesMap.remove(DEFAULT_ROUTE);
routesMap.put(DEFAULT_ROUTE, defaultRoute);
}
LinkedHashMap<String, ZuulRoute> values = new LinkedHashMap<>();
for (Entry<String, ZuulRoute> entry : routesMap.entrySet()) {
String path = entry.getKey();
// Prepend with slash if not already present.
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;
}

zuul源码(2)的更多相关文章

  1. Spring Cloud Zuul源码

    一.Zuul源码分析(初始化流程.请求处理流程)

  2. zuul源码分析-探究原生zuul的工作原理

    前提 最近在项目中使用了SpringCloud,基于zuul搭建了一个提供加解密.鉴权等功能的网关服务.鉴于之前没怎么使用过Zuul,于是顺便仔细阅读了它的源码.实际上,zuul原来提供的功能是很单一 ...

  3. Zuul 源码的分析

    Zuul 就是个网关,过滤所有数据, 和Eureka的区别就是,前者或过滤数据,一般进行权限拦截,后者进行请求的转发,只是链接. Zuul包含了对请求的路由和过滤两个最主要的功能: 使用 注解@Ena ...

  4. Zuul源码分析

    先上一张流程图: 我们Zuul的使用如下: @SpringBootApplication @EnableZuulProxy public class ZuulApplication { public ...

  5. Spring Cloud源码分析(四)Zuul:核心过滤器

    通过之前发布的<Spring Cloud构建微服务架构(五)服务网关>一文,相信大家对于Spring Cloud Zuul已经有了一个基础的认识.通过前文的介绍,我们对于Zuul的第一印象 ...

  6. 服务网关zuul之二:过滤器--请求过滤执行过程(源码分析)

    Zuul的核心是一系列的过滤器,这些过滤器可以完成以下功能: 身份认证与安全:识别每个资源的验证要求,并拒绝那些与要求不符的请求. 审查与监控:在边缘位置追踪有意义的数据和统计结果,从而带来精确的生成 ...

  7. 基于zuul实现自定义路由源码分析

    ZuulFilter定义 通过继承ZuulFilter我们可以定义一个新的过滤器,如下 public class IpAddressFilter extends ZuulFilter { @Autow ...

  8. zuul网关源码解析

    zuul网关源码解析 zuul请求的生命周期 ZuulServlet ZuulServlet定义了对zuul整个过程的处理,如下: public void service(javax.servlet. ...

  9. SpringCloud微服务如何优雅停机及源码分析

    目录 方式一:kill -9 java进程id[不建议] 方式二:kill -15 java进程id 或 直接使用/shutdown 端点[不建议] kill 与/shutdown 的含义 Sprin ...

随机推荐

  1. LInux下几种定时器的比较和使用

    在数据通信过程中,会遇到对数据发送时间的格式要求.所以要在应用中根据实际要求选择不同的定时器,就要考虑到几种应用定时器的特点. 定时器文章参考 一般而言有, 1.sleep,usleep和nanosl ...

  2. laravel清缓存,flex简单使用

    关于laravel 删除视图缓存:php artisan view:clear 清除运行缓存:php artisan cache:clear 清除配置缓存:php artisan config:cle ...

  3. es6,es7,es8

    概述 ES全称ECMAScript,ECMAScript是ECMA制定的标准化脚本语言.目前JavaScript使用的ECMAScript版本为ECMAScript-262. ECMAScript 标 ...

  4. [CentOS 7] TexLive2017中kpsewhich Bug的修复

    使用TexLive有一段时间了,从2015到2017,一直运行如常. 最近,想把以前的文档重新编译,却发现不能正常运行,费了好大一番工夫,才终于修复了这个bug,记录如下. 问题描述:编译一个简单的 ...

  5. input radio单选框绑定change事件

    html页面: <input type="radio" name="sex" value="female">female < ...

  6. Kubernetes 入门之Kubernetes 的基本概念和术语

    Kubernetes是什么?    他是一个全新的基于容器技术分布式架构领先方案:    他也是一个开放的开发平台:    他也是一个完备的分布式系统支撑平台:   Kubernetes的基本慨念和术 ...

  7. linux jpg文件查找木马

    find ./ -type f -name "*.jpg" | xargs grep "eval"

  8. 【重磅】Spring Boot 2.0权威发布

    新版本特性 新版本值得关注的亮点有哪些: 基于 Java 8,支持 Java 9 也就是说Spring Boot2.0的最低版本要求为JDK8,据了解国内大部分的互联网公司系统都还跑在JDK1.6/7 ...

  9. [Oracle][DATAGUARD] 关于确认LOGICAL STANDBY的同期状况的方法

    Oracle的DATAGUARD环境,有PHYSICAL STANDBY和LOGICAL STANDBY两种.PHYSICAL STANDBY是传输REDO传到Standby端,然后由Standby端 ...

  10. P5016 龙虎斗 题解

    这题真是*到家了QAQ 我在考场上调了将近75min,总算过了大样例. 首先,我们可以简化这一题,这道题的本质就是让我们找出一个点p2,往那个点上面加上s2个单位的重量,使得以m为中的两边的权值和的差 ...