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等等. 而一个很尴尬的境地是,如果直接将提 ...
随机推荐
- MySQL的简单使用方法备忘
这只是一篇我的个人备忘录,写的是我常用的命令.具体可以参考"菜鸟教程" https://www.runoob.com/mysql/mysql-tutorial.html 登录(用户 ...
- ToolBar 用法
xml中的设置: <android.support.v7.widget.Toolbar android:id="@+id/toolbar" android:layout_wi ...
- net里面using的使用
起初using就明白一个作用 那就是引用命名空间.当面试官听到我回答这个问题的时候,马上就还问我,还有什么作用?我就只能摇头了,今天在网上看了下using的作用. 1.using指令.using + ...
- 2.go语言入门----变量类型、声明变量、数组、切片
基本变量类型 介绍几种基本的变量类型:字符串.int.float.bool package main import ( "fmt" ) // 列举几种非常基本的数据类型 func ...
- JS中indexOf的用法
String.IndexOf(Char, [startIndex], [count]):返回指定字符在原字符串中的第一个匹配项的索引.可指定字符开始检索位置和指定长度的字符,若没有找到该字符,则返回 ...
- RocketMQ基础概念剖析,并分析一下Producer的底层源码
由于篇幅原因,本次的源码分析只限于Producer侧的发送消息的核心逻辑,我会通过流程图.代码注释.文字讲解的方式来对源码进行解释,后续应该会专门开几篇文章来做源码分析. 这篇博客聊聊关于Rocket ...
- EFCodeFirst关系映射约定
EFCodeFirst关系映射约定 EFCodeFirst 关系映射约定 默认多重关系的一些约定规则: 1.一对多关系 两个类中分别包含一个引用和一个集合属性. 两个类中一个类包含另一个类的引用属性. ...
- 185. 部门工资前三高的所有员工 + 多表联合 + join + dense_rank()
185. 部门工资前三高的所有员工 LeetCode_MySql_185 题目描述 方法一:使用join on # Write your MySQL query statement below sel ...
- Docker搭建HAproxy+tomcat 实现高可用
构建业务镜像1创建tomcat-app1和tomcat-app2两个目录,代表不同的两个基于tomcat的业务.准备tomcat的配置文件[root@localhost ~]#mkdir -p /da ...
- Vmware虚拟机CentOS7、Ubuntu20系统设置静态IP,且主机和虚拟机系统能相互ping通。
目录 前言 一.VMware虚拟系统centos7设置静态IP 1.1 打开VMware虚拟网络配置窗口 1.2 方法1:通过DHCP服务给主机动态分配IP,同时设置centos静态IP 1.2.1 ...