动态路由组件
组件编码
hzero-starter-dynamic-route
一、简介
1.1 概述
动态路由组件用于服务治理中 feign
调用及 zuul
网关层动态路由和URL客制化,将根据发布节点实例时配置的节点组规则以及当前请求的租户或用户选择配置的节点,从而做到动态路由。
1.2 组件坐标
<dependency>
<groupId>org.hzero.starter</groupId>
<artifactId>hzero-starter-dynamic-route</artifactId>
<version>${hzero.starter.veresion}</version>
</dependency>
1.3 特性
- 在服务内,拦截 feign 调用,根据调用服务和URL以及当前租户,为URL加上租户编码前缀,做到URL客制化。
- 在 Zuul 网关层,根据获取的服务路由及当前租户,为URL加上租户编码前缀,做到URL客制化。
- 通过修改 Ribbon 选择服务节点的方法,根据当前请求的服务、租户、用户获取配置的节点组,从而将请求路由到特定的节点上。
- 可用于灰度发布,支持租户级、用户级灰度。
二、节点组规则
首先需要明白这个组件是为了解决什么问题:
- 灰度发布时,想让一部分租户或用户使用新版本的服务功能,使用没问题才会正式发布服务。
- 对于某些 API 要做客制化开发,访问网关或 feign 调用时,服务名、地址都不变,但是要求能正确路由到客制化的API上。
- 无论是灰度发布还是客制化URL都要求对代码无侵入,不能修改代码中的服务名、URL。
解决这些问题的手段主要是通过配置规则,在发布服务时为节点实例打上节点组标签(nodeGroupId
),从而根据配置找到相应的节点组。
2.1 规则配置
-
在 [服务治理>节点组规则] 配置节点组规则。先选择租户,之后可以选择租户下的用户,代表只对这些用户生效,否则对整个租户生效;
-
或选择URL进行客制化,当该租户访问这个URL时,组件为URL会加上租户编码前缀,如
/v1/{organizationId}/notices
->/tenant_code/v1/{organizationId}/notices
。这就要求客制化开发的服务中,客制化URL需要加上租户编码前缀。
2.2 节点组标签配置
-
首先需要在代码中添加节点组标签,最终通过配置找节点组时就是与这个标签的值进行对比。
-
在
values.yaml
中添加节点组ID配置,之后发布服务时,会自动用节点组ID替换这两个值,部署服务时,则替换上面的节点组ID标签的值。 -
在
deployment.yaml
文件中添加节点组标签,这个标签最终会打到K8S的标签上。 -
最后,在需要灰度的项目中引入该组件,然后构建项目。
<dependency> <groupId>org.hzero.starter</groupId> <artifactId>hzero-starter-dynamic-route</artifactId> </dependency>
2.3 创建节点组
-
准备工作做完之后,在 [服务治理>节点组维护] 创建节点组,如果选择了
通用
,则代表该节点为通用节点,节点组标签(nodeGroupId=0
),该服务的请求若没有找到规则,则路由到该节点。 -
这里如果选择了灰度,则不会立即发布节点,需要在[灰度发布管理]处创建灰度发布来发布节点。如果没有选择灰度,则会立即发布服务。
-
非通用节点,必须添加节点组规则。
-
发布服务时,会自动将该节点组ID设置到配置文件中,并且在服务发布时替换
2.4 缓存规则
服务发布成功后,会将节点组对应的规则缓存起来,请求时,则从缓存中查找规则,如果有匹配的规则,就会寻找相应的节点组,否则走通用节点。
一共有三种类型的缓存,结构都为 HASH:
-
如果该规则只选择了租户,则缓存
租户-节点
关系,key 包含服务名称nodegroup:{serviceName}:tenant-node - {tenantId}:{nodeGroupId}
-
如果租户下选择了用户,则缓存
用户-节点
关系,key 包含服务名和租户IDnodegroup:{serviceName}:{tenantId}:user-node - {userId}:{nodeGroupId}
-
如果租户下选择了客制化URL,则缓存
URL-节点
关系,key 包含服务名和租户IDnodegroup:{serviceName}:{tenantId}:url-node - {url#method}:{urlPrefix#nodeGroupId}
三、组件核心思想
3.1 网关层URL客制化:
- 通过
ZuulPathFilter
拦截所有请求,获取当前租户ID和用户ID,并放入线程变量中。ZuulPathFilter
继承自ZuulFilter
。 MemoryRouteLocator
重写路由定位器获取匹配路由的方法,获取到匹配的路由Route
后,根据服务名、租户ID、URL获取客制化的URL编码前缀,并改写Route
的path
。MemoryRouteLocator
继承自SimpleRouteLocator
。
3.2 服务间 feign 调用URL客制化:
- 通过
TenantHeaderInterceptor
拦截所有服务请求,获取当前租户ID和用户ID,并放入线程变量中。TenantHeaderInterceptor
继承自HandlerInterceptorAdapter
。 - 使用AOP
FeignClientAspect
拦截所有infra.feign
包下的被@FeignClient
注解的接口,缓存 feign 调用的服务名称。 - 通过
FeignRouteInterceptor
拦截器修改RequestTemplate
,根据服务名、租户ID、URL获取客制化的URL编码前缀,向RequestTemplate
插入URL编码前缀。FeignRouteInterceptor
继承自RequestInterceptor
。
3.3 节点定位:
- 核心思想是通过修改
Ribbon
选择节点的方法,因为不论是网关层、还是 feign 调用、还是使用LoadBalanced
标注的RestTemplate
,最终都会通过 Ribbon 来从注册中心选择一个节点进行服务请求。 - 自定义
CustomMetadataRule
,继承自ZoneAvoidanceRule
,覆盖choose
方法。在choose
方法中首先拉取所有服务节点,然后根据当前规则所匹配的节点组ID来获取匹配的节点组。
四、版本更新日志
0.3.0-SNAPSHOT [2018-10-12]
- 初版完成
- 支持租户级、用户级灰度,支持URL客制化