spring-cloud-gateway 服务网关
Spring Cloud Gateway是Spring Cloud官方推出的第二代网关框架,取代Zuul网关。网关作为流量的,在微服务系统中有着非常作用,网关常见的功能有路由转发、权限校验、限流控制等作用。
Spring Cloud Gateway是Spring官方最新推出的一款基于Spring Framework 5,Project Reactor和Spring Boot 2之上开发的网关。与zuul1.0不同的是,gateway是异步非阻塞的(netty+webflux实现),zuul1.0是同步阻塞请求的。gateway的数据是封装在ServerWebExchange中,zuul是存放在RequestContext里的(这里是重点,圈起来!)Gateway相对于Zuul来说,在路由的配置上更加多样化,配置更加简便。
官方文档 : https://docs.spring.io/spring-cloud-gateway/docs/2.2.5.RELEASE/reference/html/#gateway-starter
Gateway 核心概念:
Route 路由,它是网关的基础元素,包含ID、目标URI、断言、过滤器组成,当前请求到达网关时,会通过Gateway Handler Mapping,基于断言进行路由匹配,当断言为true时,匹配到路由进行转发
Predicate,断言,学过java8的同学应该知道这个函数,它可以允许开发人员去匹配HTTP请求中的元素,一旦匹配为true,则表示匹配到合适的路由进行转发
Filter,过滤器,可以在请求发出的前后进行一些业务上的处理,比如授权、埋点、限流等。
Gateway 工作模型:
其中,predicate就是我们的匹配条件;而filter,就可以理解为一个无所不能的拦截器。有了这两个元素,再加上目标uri,就可以实现一个具体的路由了。客户端向 Spring Cloud Gateway 发出请求,如果请求与网关程序定义的路由匹配,则该请求就会被发送到网关 Web 处理程序,此时处理程序运行特定的请求过滤器链。过滤器之间用虚线分开的原因是过滤器可能会在发送代理请求的前后执行逻辑。所有 pre 过滤器逻辑先执行,然后执行代理请求;代理请求完成后,执行 post 过滤器逻辑。
Predicate 路由断言:
Spring Cloud Gateway将路由匹配为Spring WebFlux HandlerMapping基础设施的一部分。Spring Cloud Gateway包括许多内置的路由谓词工厂。所有这些谓词都匹配HTTP请求的不同属性。您可以使用逻辑和语句组合多个路由谓词工厂。
Gateway 的路由断言机制以 AbstractRoutePredicateFactory 为基础,实现了如下多种方式。再官方文档内提供了配置说明
Gateway 网关的实现:
要在项目中引入Spring Cloud Gateway,引入相关依赖,然后只需要一些简单的配置即可构建好一个 Gateway 网关服务。
1.引入依赖(基于 spring-cloud-dependencies Hoxton.SR4 版本)
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
2. 以 PathRoutePredicateFactory 路由断言为例子演示,做以下配置:
server:
port: 9544 spring:
application:
name: gateway-service
cloud:
gateway:
enabled: true
discovery:
locator:
enabled: false #gateway开启服务注册和发现的功能
lowerCaseServiceId: true #请求路径上的服务名配置为小写
routes:
- id: ribbon-server
uri: lb://RIBBON-SERVER #uri以lb://开头(lb代表从注册中心获取服务),后面接的就是你需要转发到的服务名称
predicates:
- Path=/demo/**
filters:
- StripPrefix=1 # 代表 Path 的值中将第一段舍弃,本例子就是转发的时候为 /** 将/demo 去除。 eureka:
client:
service-url:
defaultZone: http://localhost:7001/eureka/
instance:
instance-id: gateway-service
3.启动服务访问相关的路径即可看到效果。
自定义Predicate 路由断言的实现:
创建一个类,继承 AbstractRoutePredicateFactory 类,实现对应方法:
@Component
public class AuthRoutePredicateFactory extends AbstractRoutePredicateFactory<AuthRoutePredicateFactory.Config> { public AuthRoutePredicateFactory() {
super(Config.class);
} private static final String NAME_KEY = "name";
private static final String VALUE_KEY = "value"; @Override
public List<String> shortcutFieldOrder() {
//属性进行匹配对应
return Arrays.asList(NAME_KEY, VALUE_KEY);
} @Override
public Predicate<ServerWebExchange> apply(Config config) {
//Header中携带了某个值,进行header的判断
return (exchange -> {
HttpHeaders headers = exchange.getRequest().getHeaders();
List<String> headerList = headers.get(config.getName());
return headerList.size() > 0;
});
} public static class Config {
private String name;
private String value; public String getName() {
return name;
} public void setName(String name) {
this.name = name;
} public String getValue() {
return value;
} public void setValue(String value) {
this.value = value;
}
}
}
yml 配置如下:
spring:
application:
name: gateway-service
cloud:
gateway:
enabled: true
discovery:
locator:
enabled: false #gateway开启服务注册和发现的功能
lowerCaseServiceId: true #请求路径上的服务名配置为小写
routes:
- id: cookie_route
predicates:
- Auth=Authorization,token
filters:
- StripPrefix=1
uri: lb://RIBBON-SERVER
其中 Authorization 为 name,token 为 value,打个断点看看就行了。
Filter 请求过滤器:
Filter分为全局过滤器和路由过滤器,当请求与路由匹配时,过滤Web处理程序会将的所有实例GlobalFilter
和所有特定GatewayFilter
于路由的实例添加到过滤器链中。该组合的过滤器链按org.springframework.core.Ordered
接口排序,您可以通过实现该getOrder()
方法进行设置。
需要获取到更详细的信息可以惨开官网 :https://docs.spring.io/spring-cloud-gateway/docs/2.2.5.RELEASE/reference/html/#gatewayfilter-factories
这里演示一下自定义的过滤器(全局、路由)
1.全局过滤器只需要实现接口 org.springframework.cloud.gateway.filter.GlobalFilter,而后无需任何配置,即可生效。
@Component
public class CustomGlobalFilter implements GlobalFilter, Ordered { Logger logger= LoggerFactory.getLogger(CustomGlobalFilter.class); @Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
logger.info("custom global filter");
return chain.filter(exchange);
} @Override
public int getOrder() {
return -1;
}
}
2.路由过滤器 需要继承 org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory
每个过滤器工厂都对应一个实现类,并且这些类的名称必须以 GatewayFilterFactory 结尾,这是Spring Cloud Gateway的一个约定,例如 AddRequestHeader 对应的实现类为 AddRequestHeaderGatewayFilterFactory
@Component
public class WuzzDefineGatewayFilterFactory extends AbstractGatewayFilterFactory<WuzzDefineGatewayFilterFactory.WuzzConfig>{ private static final String NAME_KEY="name"; Logger logger= LoggerFactory.getLogger(WuzzDefineGatewayFilterFactory.class); public WuzzDefineGatewayFilterFactory() {
super(WuzzConfig.class);
} @Override
public List<String> shortcutFieldOrder() {
return Arrays.asList(NAME_KEY);
} @Override
public GatewayFilter apply(WuzzConfig config) {
//Filter pre post
return ((exchange,chain)->{
logger.info("[pre] Filter Request, name:"+config.getName());
//TODO
return chain.filter(exchange).then(Mono.fromRunnable(()->{
//TODO
logger.info("[post]: Response Filter");
}));
});
} public static class WuzzConfig{
private String name; public String getName() {
return name;
} public void setName(String name) {
this.name = name;
}
}
}
3. 配置路由规则
spring:
application:
name: gateway-service
cloud:
gateway:
enabled: true
discovery:
locator:
enabled: false #gateway\u5F00\u542F\u670D\u52A1\u6CE8\u518C\u548C\u53D1\u73B0\u7684\u529F\u80FD
lowerCaseServiceId: true #\u8BF7\u6C42\u8DEF\u5F84\u4E0A\u7684\u670D\u52A1\u540D\u914D\u7F6E\u4E3A\u5C0F\u5199
routes:
- id: config_route
predicates:
- Path=/demo/**
filters:
- StripPrefix=1
- WuzzDefine=Hello Wuzz
uri: lb://RIBBON-SERVER
4. 启动测试,发送一个请求,可以在控制台看到如下信息,说明过滤器均生效:
使用自带的限流过滤器 :
spring:
application:
name: gateway-service
cloud:
gateway:
enabled: true
discovery:
locator:
enabled: false #gateway\u5F00\u542F\u670D\u52A1\u6CE8\u518C\u548C\u53D1\u73B0\u7684\u529F\u80FD
lowerCaseServiceId: true #\u8BF7\u6C42\u8DEF\u5F84\u4E0A\u7684\u670D\u52A1\u540D\u914D\u7F6E\u4E3A\u5C0F\u5199
routes:
- id: config_route
predicates:
- Path=/demo/**
filters:
- StripPrefix=1
- WuzzDefine=Hello Wuzz
uri: lb://RIBBON-SERVER
- id: ratelimiter_route
predicates:
- Path=/ratelimiter/**
filters:
- StripPrefix=1
- name: RequestRateLimiter
args:
deny-empty-key: true
keyResolver: '#{@ipAddressKeyResolver}'
redis-rate-limiter.replenishRate: 1
redis-rate-limiter.burstCapacity: 2
uri: lb://RIBBON-SERVER
以上配置了限流过滤器,
- replenishRate:令牌桶中令牌的填充速度,代表允许每秒执行的请求数。
- burstCapacity:令牌桶的容量,也就是令牌桶最多能够容纳的令牌数。表示每秒用户最大能够执行的请求数量。
其中还需要配置一个 keyResolver:
@Component
public class IpAddressKeyResolver implements KeyResolver{ @Override
public Mono<String> resolve(ServerWebExchange exchange) {
return Mono.just(exchange.getRequest().getRemoteAddress().getAddress().getHostAddress());
}
}
然后启动测试,迅速刷新页面访问接口,会限流:
动态路由:
Spring Cloud Gateway 提供了 Endpoint 端点,暴露路由信息,有获取所有路由、刷新路由、查看单个路由、删除路由等方法,源码在 org.springframework.cloud.gateway.actuate.GatewayControllerEndpoint 中,想访问端点中的方法需要添加 spring-boot-starter-actuator 依赖,并在配置文件中暴露所有端点
management:
endpoints:
web:
exposure:
include: "*"
列举几个常用的操作API 。详细信息及配置查看官方文档:https://docs.spring.io/spring-cloud-gateway/docs/2.2.5.RELEASE/reference/html/#actuator-api
- /actuator/gateway/routes GET 获取路由列表
- /actuator/gateway/globalfilters GET 全局过滤器列表
- /actuator/gateway/routefilters GET 路由过滤器列表
- /actuator/gateway/refresh POST 刷新新增的路由
- /gateway/routes/{id_route_to_create} POST/DELETS 新增或者删除
- ......
新增路由示例 :
然后调用一下 刷新的接口,即可生效。但是默认情况下,我们新增的路由只是保存在内存中,万一服务重启,则配置信息丢失,这个时候就需要将路由信息持久化。
路由持久化--基于redis
1. 引入 redis 依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
</dependency>
2. 实现 org.springframework.cloud.gateway.route.RouteDefinitionRepository 接口,重写路由的相关操作方法
@Component
public class RedisRouteDefinitionRepository implements RouteDefinitionRepository { private final static String GATEWAY_ROUTE_KEY="gateway_dynamic_route"; @Autowired
RedisTemplate<String,String> redisTemplate; @Override
public Flux<RouteDefinition> getRouteDefinitions() {
List<RouteDefinition> routeDefinitionList=new ArrayList<>();
redisTemplate.opsForHash().values(GATEWAY_ROUTE_KEY).stream().forEach(route->{
routeDefinitionList.add(JSON.parseObject(route.toString(),RouteDefinition.class));
});
return Flux.fromIterable(routeDefinitionList);
} @Override
public Mono<Void> save(Mono<RouteDefinition> route) {
return route.flatMap(routeDefinition -> {
redisTemplate.opsForHash().put(GATEWAY_ROUTE_KEY,routeDefinition.getId(),JSON.toJSONString(routeDefinition));
return Mono.empty();
});
} @Override
public Mono<Void> delete(Mono<String> routeId) {
return routeId.flatMap(id->{
if(redisTemplate.opsForHash().hasKey(GATEWAY_ROUTE_KEY,id)){
redisTemplate.opsForHash().delete(GATEWAY_ROUTE_KEY,id);
return Mono.empty();
}
return Mono.defer(()->Mono.error(new Exception("routeDefinition not found:"+routeId)));
});
}
}
3.配置路由
spring:
application:
name: gateway-service
cloud:
gateway:
enabled: true
discovery:
locator:
enabled: false #gateway\u5F00\u542F\u670D\u52A1\u6CE8\u518C\u548C\u53D1\u73B0\u7684\u529F\u80FD
lowerCaseServiceId: true #\u8BF7\u6C42\u8DEF\u5F84\u4E0A\u7684\u670D\u52A1\u540D\u914D\u7F6E\u4E3A\u5C0F\u5199
routes:
- id: config_route
predicates:
- Path=/demo/**
filters:
- StripPrefix=1
- WuzzDefine=Hello Wuzz
uri: lb://RIBBON-SERVER
4. 测试,启动后按照之前一样创建一个路由,这个时候发现 redis里面已经保存了这个路由配置信息
也可以通过配置中心,比如 config 、Nacos 实现动态的配置。
spring-cloud-gateway 服务网关的更多相关文章
- Spring Cloud Gateway 服务网关快速上手
Spring Cloud Gateway 服务网关 API 主流网关有NGINX.ZUUL.Spring Cloud Gateway.Linkerd等:Spring Cloud Gateway构建于 ...
- Spring Cloud Gateway服务网关
原文:https://www.cnblogs.com/ityouknow/p/10141740.html Spring 官方最终还是按捺不住推出了自己的网关组件:Spring Cloud Gatewa ...
- spring cloud:服务网关 Spring Cloud GateWay 入门
Spring 官方最终还是按捺不住推出了自己的网关组件:Spring Cloud Gateway ,相比之前我们使用的 Zuul(1.x) 它有哪些优势呢?Zuul(1.x) 基于 Servlet,使 ...
- Spring Cloud (14) 服务网关-过滤器
Spring Cloud Zuul作为网关所具备的最基本的功能:路由,还具备另外一个核心的功能:过滤器. 过滤器 通过Spring Cloud Zuul实现的路由功能,我们的微服务可以通过统一的API ...
- Spring Cloud (12) 服务网关-基础
通过前几篇介绍,已经可以构建一个简单的微服务架构了,如下图: 通过eureka实现服务注册中心以及服务注册发现,通过ribbon或feign实现服务的消费以及负载均衡,通过spring cloud c ...
- Spring Cloud 之 服务网关
在微服务架构体系中,使用API 服务网关后的系统架构图如下: API服务网关的主要作用如下: 服务访问的统一入口 服务访问的负载均衡功能 服务访问的路由功能 在SpringCloud中,基于Netfl ...
- Spring Cloud (13) 服务网关-路由配置
传统路由配置 所谓传统路由配置方式就是在不依赖于服务发现机制情况下,通过在配置文件中具体制定每个路由表达式与服务实例的映射关系来实现API网关对外部请求的路由.没有Eureka服务治理框架帮助的时候, ...
- spring Cloud网关之Spring Cloud Gateway
Spring Cloud Gateway是什么?(官网地址:https://cloud.spring.io/spring-cloud-gateway/reference/html/) Spring C ...
- 熬夜肝了这篇Spring Cloud Gateway的功能及综合使用
前言 SpringCloud 是微服务中的翘楚,最佳的落地方案. Spring Cloud Gateway 是 Spring Cloud 新推出的网关框架,之前是 Netflix Zuul.网关通常在 ...
- Spring Cloud微服务中网关服务是如何实现的?(Zuul篇)
导读 我们知道在基于Spring Cloud的微服务体系中,各个微服务除了在内部提供服务外,有些服务接口还需要直接提供给客户端,如Andirod.IOS.H5等等. 而一个很尴尬的境地是,如果直接将提 ...
随机推荐
- 关于各种Formatting context
Formatting context 我们把网页看作是由很多个盒子组成的,而这些盒子的展示方式,就是由display这个属性来决定的. 这里出现了一个概念,叫做Formatting context(格 ...
- 解决margin-top无效问题
当两个空的块级元素嵌套时,如果内部的块设置有margin-top属性,而且父元素没有下边解决方法所述的特征,那么内部块的margin-top属性会绑架父元素(即将margin-top传递凌驾给了父元素 ...
- mybites框架遇到的坑之Mapper.xml文件不要随意加注释和ORA-00911
原文链接:https://blog.csdn.net/streetlight8023/article/details/69388495/ 先说解决方法: org.mybatis.spring.MyBa ...
- Expression #4 of SELECT list is not in GROUP BY clause and contains nonaggregated column
Error: javax.servlet.ServletException: java.sql.SQLSyntaxErrorException: Expression #4 of SELECT lis ...
- SecureCRT无法登陆ubuntu问题解决的方法(亲测有效)
最近在虚拟机安装了几个ubuntu系统玩耍,然后想着用SecureCRT在Windows本地连接但是怎么也连接不上!!!如下,这只是示意图,ip地址是瞎编的,但是情况完全相同,期间尝试过让linux和 ...
- 适配三星Galaxy S8及S8+ 屏幕比例为 18.5:9
开发者只需在App的AndroidManifest.xml文件<application> </application>中添加如下代码: <meta-data androi ...
- RabbitMQ-RPC版主机管理程序
一.作业需求 1.可以对指定机器异步的执行多个命令 例子: 请输入操作指令>>>:run ipconfig --host 127.0.0.0 in the call tack ...
- Adapper 入门
Adapper 入门 特点 单实体实现自动装配.连表查询需要自己处理装配,查看查询. 原生sql语句. 连接接口: IDbConnection connection = new SqlConnecti ...
- Jenkins+Python自动化测试持续集成详细教程
Jenkins是一个开源的软件项目,是基于java开发的一种持续集成工具,用于监控持续重复的工作,旨在提供一个开放易用的软件平台,使软件的持续集成变成可能.由于是基于java开发因此它也依赖jav ...
- JPEG解码——(5)反量化和逆ZigZag变换
本篇是该系列的第五篇,承接上篇huffman解码,介绍接下来的两个步骤--反量化和逆zigzag变换,即IDCT前的两个步骤. 需要说明的是,这两个步骤可以颠倒,本人的实现是,先反量化,再逆ZigZa ...