简介
本文主要是用来学习Spring Cloud gateway网关使用的;API网关作为后端服务的统一入口,可提供请求路由、协议转换、安全认证、服务鉴权、流量控制、日志监控等服务。
概览
gateway网关处理流程图如下:
网关
- 请求发送到网关,DispatcherHandler是Http请求的中央分发器,将请求匹配到相应的HandlerMapping;
- 请求和处理器之间有一个映射关系,网关将会对请求进行路由,handler会匹配RoutePredicateHandlerMapping,以匹配到对应的Route;
- 经过RoutePredicateHandlerMapping处理后,请求会发送到Web处理器,该WebHandler代理了一系列网关过滤器和全局过滤,此时会对请求或者响应头进行处理;
- 最后转发到具体的代理服务;
DispatcherHandler--->RoutePredicateHandlerMapping
RoutePredicateHandlerMapping---->FilteringWebHandler
FilteringWebHandler----->DefaultGatewayFilterChain
Route路由
什么是路由?一个路由就代表一个真实的下游请求路径,路由在gateway里面有两个重要的辅助概念-路由定义和定位器;
- 路由定义RouteDefinition:路由定义是Gateway Properties中的属性,可以定义获取路由的方式;
- 定位器Locator:定位器分为路由定位器RouteLocatorRouteDefinitionLocator,
RouteLocator用来获取routes,而RouteDefinitionLocator则用来获RouteDefinitions;
RouteDefinition路由定义
路由定义有五个基本属性:
- id: 路由id,默认为uuid;
- predicates: 路由断言定义列表;
- fliters:该路由需要经过的过滤器;
- URI:该路由请求的路径;
- order:优先级 ;
定位器
RouteLocator
用来获取路由的定位器,其源码如下:
public interface RouteLocator {
Flux<Route> getRoutes();
}
其内置了3个路由定位器实现类:
- CachingRouteLocator:具备缓存功能的路由定位器,其内具有监听RefreshRoutesEvent事件,并能及时刷新缓存;
@EventListener(RefreshRoutesEvent.class)
/* for testing */ void handleRefresh() {
refresh();
}
- CompositeRouteLocator:组合路由定位器,其内有代理了一系列的定位器,来实现路由查询;
- RouteDefinitionRouteLocator:基于路由定义的路由定位器,根据路由定义定位器获取路由定义并将其转换成对应的路由;代码如下:
public Flux<Route> getRoutes() {
return this.routeDefinitionLocator.getRouteDefinitions()
.map(this::convertToRoute)
//TODO: error handling
.map(route -> {
if (logger.isDebugEnabled()) {
logger.debug("RouteDefinition matched: " + route.getId());
}
return route;
});
}
也可以自定义一个路由定位器,如下代码:
@SpringBootApplication
public class DemogatewayApplication {
@Bean
public RouteLocator customRouteLocator(RouteLocatorBuilder builder) {
return builder.routes()
.route("path_route", r -> r.path("/get")
.uri("http://httpbin.org"))//将所有/get开头的请求路径转发到httpbin.org
.route("host_route", r -> r.host("*.myhost.org")
.uri("http://httpbin.org"))//将域名以myhost.org结尾的请求转发
.route("rewrite_route", r -> r.host("*.rewrite.org")
.filters(f -> f.rewritePath("/foo/(?<segment>.*)", "/${segment}"))
.uri("http://httpbin.org"))//将域名以myhost.org结尾的请求转发并进行路径重写,保留/foo/后面路径;
.route("hystrix_route", r -> r.host("*.hystrix.org")
.filters(f -> f.hystrix(c -> c.setName("slowcmd")))
.uri("http://httpbin.org"))
.route("hystrix_fallback_route", r -> r.host("*.hystrixfallback.org")
.filters(f -> f.hystrix(c -> c.setName("slowcmd").setFallbackUri("forward:/hystrixfallback")))
.uri("http://httpbin.org"))
.route("limit_route", r -> r
.host("*.limited.org").and().path("/anything/**")
.filters(f -> f.requestRateLimiter(c -> c.setRateLimiter(redisRateLimiter())))
.uri("http://httpbin.org"))
.build();
}
}
路由定义定位器
路由定义定位器用来获取路由定义的,路由定义可以在配置文件中进行设置,gateway内置了三个路由定义定位器:
- CompositeRouteDefinitionLocator:其内代理了一系列RouteDefinitionLocators,通过其来获取路由定义集合;
- RouteDefinitionRepository & InMemoryRouteDefinitionRepository:该定位器实现了路由定义的增删改查等操作,gateway内置端点接口会用到这些操作,同时也可以通过该路由定义定位器可以实现路由仓库,来动态更新路由定义信息;
- CachingRouteDefinitionLocator:带有缓存功能路由定义定位器;
- DiscoveryClientRouteDefinitionLocator:服务发现路由定义定位器,通过服务发现机制以及Spel表达式来动态更新路由定义信息;获取路由源码如下:
@Override
public Flux<RouteDefinition> getRouteDefinitions() {
//获取Spel表达式
SpelExpressionParser parser = new SpelExpressionParser();
Expression includeExpr = parser.parseExpression(properties.getIncludeExpression());
Expression urlExpr = parser.parseExpression(properties.getUrlExpression());
Predicate<ServiceInstance> includePredicate;
if (properties.getIncludeExpression() == null || "true".equalsIgnoreCase(properties.getIncludeExpression())) {
includePredicate = instance -> true;
} else {
includePredicate = instance -> {
Boolean include = includeExpr.getValue(evalCtxt, instance, Boolean.class);
if (include == null) {
return false;
}
return include;
};
}
//获取service clients以及其instances,通过serviceId替换的方式获取路由定义
return Flux.fromIterable(discoveryClient.getServices())
.map(discoveryClient::getInstances)
.filter(instances -> !instances.isEmpty())
.map(instances -> instances.get(0))
.filter(includePredicate)
.map(instance -> {
String serviceId = instance.getServiceId();
RouteDefinition routeDefinition = new RouteDefinition();
routeDefinition.setId(this.routeIdPrefix + serviceId);
String uri = urlExpr.getValue(evalCtxt, instance, String.class);
routeDefinition.setUri(URI.create(uri));
final ServiceInstance instanceForEval = new DelegatingServiceInstance(instance, properties);
for (PredicateDefinition original : this.properties.getPredicates()) {
PredicateDefinition predicate = new PredicateDefinition();
predicate.setName(original.getName());
for (Map.Entry<String, String> entry : original.getArgs().entrySet()) {
String value = getValueFromExpr(evalCtxt, parser, instanceForEval, entry);
predicate.addArg(entry.getKey(), value);
}
routeDefinition.getPredicates().add(predicate);
}
for (FilterDefinition original : this.properties.getFilters()) {
FilterDefinition filter = new FilterDefinition();
filter.setName(original.getName());
for (Map.Entry<String, String> entry : original.getArgs().entrySet()) {
String value = getValueFromExpr(evalCtxt, parser, instanceForEval, entry);
filter.addArg(entry.getKey(), value);
}
routeDefinition.getFilters().add(filter);
}
return routeDefinition;
});
}
- PropertiesRouteDefinitionLocator: 通过配置文件来获取路由定义集合,其源码如下:
@Override
public Flux<RouteDefinition> getRouteDefinitions() {
return Flux.fromIterable(this.properties.getRoutes());
}
Predicate断言
Predicate通过Predicate函数式接口来判断当前请求是否满足选择条件,Predicate分为以下条件划分为不同的路由断言工厂类
Datetime类型
根据日期判断是否满足路由选择条件,其有三个工厂类:
- AfteRoutePredicateFactory: 接收一个日期参数,判断请求日期是否晚于指定日志;
- BeforeRoutePredicateFactory:接收一个日期参数,判断请求日期是否早于指定日期;
- BetweenRoutePredicateFactory:接收两个日期参数,判断请求日期是否位于之间;
其配置如下:
spring:
cloud:
gateway:
routes:
- id: after_route_id
uri: http://www.baidu.com
predicates:
- After= 2018-12-30T23:59:59.789+08:00[Asia/Shanghai]
Cookie类型
根据请求的Cookie正则匹配是否通过选择条件;
唯一工厂类:CookieRoutePredicateFactory;
其配置实例如下:
spring:
cloud:
gateway:
routes:
- id: cookie_route
uri: https://example.org
predicates:
- Cookie=chocolate, ch.p
Header头断言类型
请求头属性正则匹配是否通过选择,唯一工厂类:HeaderRoutePredicateFactory,其配置如下:
spring:
cloud:
gateway:
routes:
- id: header_route
uri: https://example.org
predicates:
- Header=X-Request-Id, \d+
Host域名断言类型
Host域名正则匹配,是否通过选择,唯一工厂类:HostRoutePredicateFactory,
其配置如下:
spring:
cloud:
gateway:
routes:
- id: host_route
uri: https://example.org
predicates:
- Host=**.somehost.org,**.anotherhost.org
Method断言类型
基于方法类型来进行相应匹配选择,唯一工厂类MethodRoutePredicateFactory;
其配置如下:
spring:
cloud:
gateway:
routes:
- id: method_route
uri: https://example.org
predicates:
- Method=GET
Path断言类型
Path断言类型会通过接受的两个参数来判断是否匹配选择,唯一工厂类:PathRoutePredicateFactory,其配置如下:
spring:
cloud:
gateway:
routes:
- id: host_route
uri: https://example.org
predicates:
- Path=/foo/{segment},/bar/{segment}
Query查询类型
Query类型通过接受两个参数:一个要求的参数和一个可选的regexp表达式,来匹配是否选择,其工厂类为QueryRoutePredicateFactory,配置示例如下:
spring:
cloud:
gateway:
routes:
- id: query_route
uri: https://example.org
predicates:
- Query=baz,ba.
路由必须满足有一个baz的查询参数,且该参数值必须满足ba.表达式,方可通过请求;
RemoteAddr类型
该类型通过设置一系列的IPv4或者IPv6地址以及子网掩码形式的字符串,来进行匹配,其唯一工厂类为:RemoteAddrRoutePredicateFactory;
其配置如下:
spring:
cloud:
gateway:
routes:
- id: remoteaddr_route
uri: https://example.org
predicates:
- RemoteAddr=192.168.1.1/24
路由请求必须满足192.168.1.1/24指定的ip域内方可通过选择;
Weight类型
Weight类型通过设置权重,来进行路由选择,其配置如下:
spring:
cloud:
gateway:
routes:
- id: weight_high
uri: https://weighthigh.org
predicates:
- Weight=group1, 8
- id: weight_low
uri: https://weightlow.org
predicates:
- Weight=group1, 2
上例说明路由百分之80将发向https://weighthigh.org,百分20发向https://weightlow.org
Filter过滤器
配置文件
fliter需要在配置文件bootstrap.yml中进行相应设置,或者手动定义一个实现了Filter;
配置文件设置filter属性如下:
gateway:
discovery:
locator:
enabled: true
predicates:
- Path='/api/**/'+serviceId+'/**'
filters:
- name: RewritePath
args:
regexp: "'/api/.*/' + serviceId + '/(?<remaining>.*)'"
replacement: "'/${remaining}'"
通过设置name便可调用相应的过滤器工厂类创建一个过滤器,上例中,前缀RewritePath便会通过RewritePathGatewayFilterFactory创建一个GatewayFilter对象,来进行相应过滤操作;
GlobalFilter
gateway网关Filter分为以下Global和Define,其中Gobal有以下几种(按ordered从小到大):
-
AdaptCachedBodyGlobalFilter(Integer.Min_VALU):通过适配缓存requestbody参数来实现request的重新构建;
-
NettyWriteResponseFilter(-1):将当前请求的响应进行输出到服务调用方;
-
ForwardPathFilter(0):解析路径并将路径转发
-
GatewayMetricsFilter(0):网关性能测量器,可以用来收集网关请求参数,方便创建一个Grafana Dashbord;
-
RouteToRequestUrlFilter(10000):转换路由中的URI
-
LoadBalancerClientFilter(10100):通过负载均衡客户端根据路由的URL解析转换成真实的请求URL
-
WebsocketRoutingFilter(Integer.MAX_VALUE-1):负责处理Websocket类型请求响应信息;
-
ForwardRoutingFilter(Integer.MAX_VALUE):其根据 forward:// 前缀( Scheme )过滤处理,将请求转发到当前网关实例本地接口。
-
NettyRoutingFilter(Integer.MAX_VALUE)):通过HttpClient客户端转发真实的URL请求到服务提供方,并将响应写入到当前的请求响应中;
网友评论