路由

路由是网关的核心功能,既然在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. VMware下Debian开发环境部署之常见问题记录

    本文讲介绍windows作为宿主机,linux虚拟机作为编译环境的开发环境搭建中最常用到的三个问题,详细描述了解决过程. 目录: 1.网路配置: 2.分辨率设置: 3.共享网盘设置: 1.网络设置,V ...

  2. ElasticSearch踩坑记录

    一.分词错误 [2018-02-06 14:28:30:098] --- [INFO] --- [CjhArticleSimilarityTask.java:66] --- [[SimilarityC ...

  3. Ubuntu连网的问题

    Ubuntu一直提示网络offline,disconnection 首先,进入了无线网络,进入属性,允许其他网络用户通过此计算机的Internet连接: 但是虚拟机仍显示网络未连接:(不知道此步骤是不 ...

  4. day25_python_1124

    1.内容回顾 2.作业讲解 3.今日作业 4.粘包问题 5.tcp和udp 6.udp-socket 7.udp-socket 多人聊天 8.socketserver-tcp-socket并发 9.p ...

  5. JS-4-if

    流程控制结构1 顺序结构 alert(10); alert(20);2 分支结构(选择结构) * IF 2.1  if(条件) { 条件成立时执行的语句 } else { 条件不成立时执行的语句 } ...

  6. JS-1

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...

  7. Saiku更改導出文件的文件名(十九)

    Saiku更改導出文件的文件名 Saiku查询完数据之后,可以以excel,pdf,csv等格式将数据导出,这里我们来讲一下怎么更改导出的文件名. 找到对应的导出方法所在的js文件: saiku-se ...

  8. Shadow Properties之美(一)【Microsoft Entity Framework Core随笔】

    最近在做公司的项目的时候,开始把部分程序迁移到EF Core,然后有了一些感触,趁着还没忘却,还是先记录下来. EF Core还在成长中,我写这个的时候,版本是2.2.如果对着已有的EF 5/6来说, ...

  9. String.split()方法

    如果用“(”作为分隔的话,必须是如下写法,String.split("\\("),这样才能正确的分隔开,不能用String.split("("); 如果用“)” ...

  10. 初识html与css

    ---恢复内容开始--- PS应用量尺寸w.h- 操作方式: 窗口,信息,右上角面板选项,标尺单位,像素切图- 操作方式: 切片工具, 文件 ,另存为web所有格式,jpeg,存储,切片:选中的切片, ...