SpringCloud-快速通关(二)
SpringCloud-快速通关(一)
SpringCloud-快速通关(二)
SpringCloud-快速通关(三)
SpringCloud-快速通关(二)
- 四、OpenFeign - 远程调用
- 4.1、基础入门
- 4.2、进阶配置
- 4.2.1、开启日志
- 4.2.2、超时控制
- 4.2.3、重试机制
- 4.2.4、fallback - 兜底返回
- 五、Sentinel - 流量保护
- 5.1、介绍
- 5.2、架构
- 5.3、资源&规则
- 5.4、环境搭建
- 5.5、异常处理
- 5.5.1、Web接口出现异常(自定义 BlockExceptionHandler)
- 5.5.2、@SentinelResource出现异常(blockHandler)
- 5.5.3、OpenFeign调用出现异常
- 5.5.4、SphU 硬编码 出现异常
- 5.6、规则 - 流量控制
- 5.6.1、阈值类型
- 5.6.2、集群
- 5.6.2、流控模式
- 5.6.3、流控效果
- 5.7、规则 - 熔断降级
- 5.7.1、断路器
- 5.7.2、工作原理
- 5.7.3、熔断与兜底
- 5.7.4、熔断策略
- 5.8、规则 - 热点参数
- 六、Gateway - 网关
- 6.1、基础入门
- 6.1.1、功能
- 6.1.2、原理
- 6.1.3、环境配置
- 6.2、Predicate - 断言
- 6.3、Filter - 过滤器
- 6.4、CORS - 跨域处理
- 6.5、GlobalFilter
四、OpenFeign - 远程调用
4.1、基础入门
- 简介
官网:https://docs.spring.io/spring-cloud-openfeign/reference/spring-cloud-openfeign.html#spring-cloud-feign
OpenFeign 是一个声明式远程调用客户端;区别于:restTemplate是编程式远程调用;
- 基础使用
引入依赖
由于大型项目中,每个项目都可能需要使用远程调用。所以我们可以在父项目中统一引入
<dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
开启功能
@SpringBootApplication
@EnableFeignClients
public class Application {public static void main(String[] args) {SpringApplication.run(Application.class, args);}}
远程调用
@FeignClient("stores")
public interface StoreClient {@RequestMapping(method = RequestMethod.GET, value = "/stores")List<Store> getStores();@GetMapping("/stores")Page<Store> getStores(Pageable pageable);@PostMapping(value = "/stores/{storeId}", consumes = "application/json",params = "mode=upsert")Store update(@PathVariable("storeId") Long storeId, Store store);@DeleteMapping("/stores/{storeId:\\d+}")void delete(@PathVariable Long storeId);
}
注意用法:
@EnableFeignClients
@EnableFeignClients(basePackages = “com.example.clients”)
- 详细用法
services中导依赖
<!--openfeign-->
<dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
启动类加注解@EnableFeignClients(value = "com.atguigu.order.feign")
编写FeignClient接口 远程调用方法直接将调用地方的controller方法粘贴过来即可
@FeignClient(value = "service-product")
public interface ProductFeignClient {@GetMapping(value = "/productId/{id}")public Product getProductById(@PathVariable("id") Long productId);
}
改造service-order OrderServiceImpl
@Autowired
ProductFeignClient productFeignClient;Product product = productFeignClient.getProductById(productId);
远程调用外部API
feign接口
@FeignClient(value = "weather-client", url = "http://aliv18.data.moji.com")
public interface WeatherFeignClient {@PostMapping("/whapi/json/alicityweather/condition")String getWeather(@RequestHeader("Authorization") String auth,@RequestParam("token") String token,@RequestParam("cityId") String cityId);
}
测试方法
@SpringBootTest
public class WeatherTest {@AutowiredWeatherFeignClient weatherFeignClient;@Testpublic void test() {String weather = weatherFeignClient.getWeather("自己的AppCode","50b53ff8dd7d9fa320d3d3ca32cf8ed1","2182");System.out.println("weather = " + weather);}
}
测试效果
-
客户端负载均衡:客户端在选择服务地址进行调用,例如service-order掉多个service-product
-
服务端负载均衡:服务端进行负载均衡,客户端只要发请求即可,例如调用墨迹天气API
4.2、进阶配置
4.2.1、开启日志
logging:level:com.atguigu.order.feign: debug
@Bean
Logger.Level feignLoggerLevel() {return Logger.Level.FULL;
}
application.yml
中开启日志
说明: 这行配置是 Spring Boot 的日志配置,用于设置 com.atguigu.order.feign 这个包下的 日志级别 为 DEBUG。
logging:level:com.atguigu.order.feign: debug
在OrderConfig
中设置日志信息
说明:这是 Feign 提供的日志级别配置,它控制 Feign 请求和响应的详细日志,Logger.Level.FULL 代表 打印所有请求和响应的详细信息
- 请求方法(GET、POST等)
- 请求URL
- 请求头
- 请求体
- 响应状态码
- 响应头
- 响应体
- 请求的执行时间
@Bean
Logger.Level feignLoggerLevel() {return Logger.Level.FULL;
}
4.2.2、超时控制
spring:cloud:openfeign:client:config:default:logger-level: fullconnect-timeout: 1000read-timeout: 2000service-product:logger-level: fullconnect-timeout: 3000read-timeout: 5000
在application.yml
引入application-feign.yml
添加application-feign.yml
spring:cloud:openfeign:client:config:default:logger-level: fullconnect-timeout: 1000read-timeout: 2000service-product:logger-level: fullconnect-timeout: 3000read-timeout: 5000
源码
在service-product
中模拟超时,后续记得去掉
try {TimeUnit.SECONDS.sleep(100);
} catch (InterruptedException e) {e.printStackTrace();
}
效果
4.2.3、重试机制
@Bean
Retryer retryer(){return new Retryer.Default();
}
在service-order
的OrderConfig
中加入重试机制
@Bean
Retryer retryer() {return new Retryer.Default();
}
源码:默认重试5次,初始间隔100毫秒,后续每次乘1.5,最多间隔1秒
拦截器
全局拦截器——拦截所有远程调用请求
响应拦截器用的不多,请求拦截器多
在service-order
的包下 com.atguigu.order.interceptor
@Component
public class XTokenRequestIntercepter implements RequestInterceptor {@Overridepublic void apply(RequestTemplate requestTemplate) {requestTemplate.header("X-Token", UUID.randomUUID().toString());}
}
改造service-product
的ProductController
@RestController
public class ProductController {@AutowiredProductService productService;@GetMapping(value = "/productId/{id}")public Product getProductById(@PathVariable("id") Long productId, HttpServletRequest httpServletRequest) {String XToken = httpServletRequest.getHeader("X-Token");System.out.println("XToken = " + XToken);System.out.println("正在远程调用service-product...");// int i = 10 / 0;Product product = productService.getProductById(productId);return product;}
}
效果
局部拦截器
测试
实验一:拦截器不放入容器+配置定制拦截器
效果:有效
实验二:拦截器不放入容器+不定制拦截器
效果:失败
4.2.4、fallback - 兜底返回
- 引入
sentinel
<dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-sentinel</artifactId></dependency>
- 开启熔断
在application-feign.yml
加配置
feign:sentinel:enabled: true
- 编写
fallback
函数
ProductFeignClient
加上回调实现
@FeignClient(value = "service-product",fallback = ProductFeignClientFallback.class) // feign客户端
public interface ProductFeignClient {//mvc注解的两套使用逻辑//1、标注在Controller上,是接收这样的请求//2、标注在FeignClient上,是发送这样的请求@GetMapping("/product/{id}")Product getProductById(@PathVariable("id") Long id);
}
ProductFeignClientFallback
@Component
public class ProductFeignClientFallback implements ProductFeignClient {@Overridepublic Product getProductById(Long id) {System.out.println("兜底回调....");Product product = new Product();product.setId(id);product.setPrice(new BigDecimal("0"));product.setProductName("未知商品");product.setNum(0);return product;}
}
测试前先将重试机制关了,不然会一直重试,无法快速看到兜底结果。
测试:模拟远程调用失败
在调用service-product
的controller
中加入一个错误。或者将service-order
服务给停掉
小节
五、Sentinel - 流量保护
5.1、介绍
- 官网:https://sentinelguard.io/zh-cn/index.html
- wiki:https://github.com/alibaba/Sentinel/wiki
- 下载控制台:sentinel-dashboard-1.8.8.jar
随着微服务的流行,服务和服务之间的稳定性变得越来越重要。Sentinel 以流量为切入点,从流量控制、流量路由、熔断降级、系统自适应过载保护、热点流量防护等多个维度保护服务的稳定性。
Sentinel 具有以下特征:
- 丰富的应用场景:Sentinel 承接了阿里巴巴近 10 年的双十一大促流量的核心场景,例如秒杀(即突发流量控制在系统容量可以承受的范围)、消息削峰填谷、集群流量控制、实时熔断下游不可用应用等。
- 完备的实时监控:Sentinel 同时提供实时的监控功能。您可以在控制台中看到接入应用的单台机器秒级数据,甚至 500 台以下规模的集群的汇总运行情况。
- 广泛的开源生态:Sentinel 提供开箱即用的与其它开源框架/库的整合模块,例如与 Spring Cloud、Apache Dubbo、gRPC、Quarkus 的整合。您只需要引入相应的依赖并进行简单的配置即可快速地接入 Sentinel。同时 Sentinel 提供 Java/Go/C++ 等多语言的原生实现。
- 完善的 SPI 扩展机制:Sentinel 提供简单易用、完善的 SPI 扩展接口。您可以通过实现扩展接口来快速地定制逻辑。例如定制规则管理、适配动态数据源等。
5.2、架构
5.3、资源&规则
定义资源:
- 主流框架自动适配(Web Servlet、Dubbo、Spring Cloud、gRPC、Spring WebFlux、Reactor);所有Web接口均为资源
- 编程式:SphU API
- 声明式:@SentinelResource
定义规则:
- 流量控制规则
- 熔断降级规则
- 系统保护规则
- 来源访问控制规则
- 热点参数规则
5.4、环境搭建
-
依赖
<dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-sentinel</artifactId> </dependency>
-
下载sentinel dashboard:sentinel-dashboard-1.8.8.jar
-
启动:cmd输入:
java -jar sentinel.jar
;账号密码都是sentinel
-
配置连接+热加载(服务启动就加载):
spring.cloud.sentinel.transport.dashboard=localhost:8080 spring.cloud.sentinel.eager=true
说明:簇点链路中的链路来自于几种资源:
- 主流框架自动适配(例如:web请求)
- 声明式 Sphu API(不常用)
- 声明式:
@SentinelResource
5.5、异常处理
5.5.1、Web接口出现异常(自定义 BlockExceptionHandler)
model模块写异常类—模拟异常处理
@Data
public class R {private Integer code;private String msg;private Object data;public static R ok() {R r = new R();r.setCode(200);return r;}public static R ok(String msg,Object data) {R r = new R();r.setCode(200);r.setMsg(msg);r.setData(data);return r;}public static R error() {R r = new R();r.setCode(500);return r;}public static R error(Integer code,String msg) {R r = new R();r.setCode(code);r.setMsg(msg);return r;}
}
实现BlockExceptionHandler接口
在service-order
的 com.atguigu.order.exception
包中写一个BlockExceptionHandler
的实现类
@Component
public class MyBlockExceptionHandler implements BlockExceptionHandler {private ObjectMapper objectMapper = new ObjectMapper();@Overridepublic void handle(HttpServletRequest request, HttpServletResponse response,String resourceName, BlockException e) throws Exception {response.setStatus(429); //too many requestsresponse.setContentType("application/json;charset=utf-8");PrintWriter writer = response.getWriter();R error = R.error(500, resourceName + " 被Sentinel限制了,原因:" + e.getClass());String json = objectMapper.writeValueAsString(error);writer.write(json);writer.flush();writer.close();}
}
配置流控规则
QPS设置每秒只允许1个请求
效果
违反流控规则就走MyBlockExceptionHandler
异常
5.5.2、@SentinelResource出现异常(blockHandler)
在目标方法加@SentinelResource
注解
编写blockHandler
方法或者fallback
方法处理异常
blockHandler="异常处理方法"
,这个方法中加参数: BlockException exception
fallback="异常处理方法"
,这个方法中加参数:Throwable exception
二者作用一样
@Service
@Slf4j
public class OrderServiceImpl implements OrderService {@AutowiredDiscoveryClient discoveryClient;@AutowiredRestTemplate restTemplate;@AutowiredLoadBalancerClient loadBalancerClient;@AutowiredProductFeignClient productFeignClient;@SentinelResource(value = "createOrder",blockHandler = "createOrderFallback")@Overridepublic Order createOrder(Long userId, Long productId) {// Product product = this.getProductFromRemote(productId);// Product product = this.getProductFromRemoteWithLoadBalance(productId);// Product product = this.getProductFromRemoteWithLoadBalanceAnnotation(productId);Product product = productFeignClient.getProductById(productId);Order order = new Order();order.setId(1L);// 总金额=价格*数量BigDecimal price = product.getPrice();//价格int num = product.getNum();//数量order.setTotalAmount(price.multiply(new BigDecimal(num)));//总价order.setUserId(userId);order.setNickName("张三");order.setAddress("火星");// 远程查询商品列表order.setProductList(Arrays.asList(product));return order;}public Order createOrderFallback(Long userId, Long productId, BlockException e) {Order order = new Order();order.setId(0L);order.setTotalAmount(new BigDecimal("0"));order.setUserId(userId);order.setNickName("未知用户");order.setAddress("异常信息" + e.getClass());return order;}//阶段三:于注解的负载均衡public Product getProductFromRemoteWithLoadBalanceAnnotation(Long productId) {//给远程发送请求;;service-product会被动态替换String url = "http://service-product/productId/" + productId;Product product = restTemplate.getForObject(url, Product.class);return product;}}
测试
5.5.3、OpenFeign调用出现异常
走openfeign的fallback异常
5.5.4、SphU 硬编码 出现异常
暂时略…
5.6、规则 - 流量控制
在application-feign.yml
中加配置:
web-context-unify: false #在sentinel中不共用同一个上下文
5.6.1、阈值类型
- QPS:统计每秒请求数
- 并发线程数:统计并发线程数
5.6.2、集群
集群和流控模式是相背离的,二选一
5.6.2、流控模式
流控模式——直接策略:只对一个资源进行控制
流控模式——链路策略:
@GetMapping("/seckill")
public Order seckill(@RequestParam("userId") Long userId,@RequestParam("productId") Long productId) {Order order = orderService.createOrder(userId, productId);order.setId(Long.MAX_VALUE);return order;
}
特点:两个请求访问一个资源(随便选一个creatOrder
资源,即加了@SentinelResource
的资源),但只限制seckill
这个请求
流控模式——关联策略:在OrderController
加方法,方便测试
@GetMapping("/writeDb")
public String writeDb() {return "writeDb success...";
}
@GetMapping("/readDb")
public String readDb() {log.info("readDb success...");return "readDb success...";
}
情况有两种
- 单独访问
/readDb
或者/writeDb
,多少请求都能成功 - 当
/writeDb
请求量非常大的时候,突然访问/readDb
,则会崩溃,走自定义BlockExceptionHandler
的实现类
使用场景:当系统里出现资源竞争的时候,使用关联策略进行限制。例如这里只有写量特别大的时候才会限制读,其他不限制。
注意:这里可能需要手速快点!
5.6.3、流控效果
流控效果——快速失败
作用:处理不了的请求直接丢弃。交给Web接口异常处理
MyBlockExceptionHandler
。
流控效果——Warm Up
作用:慢慢将每秒的请求达到峰值,有个预热机制
例如我这里QPS是10,需要经过3秒才能达到10,前三秒是慢慢将能接收请求数量提上来。
流控效果——排队等待
注意!
5.7、规则 - 熔断降级
熔断降级是保护自身的手段,通常配置在调用端
5.7.1、断路器
5.7.2、工作原理
5.7.3、熔断与兜底
5.7.4、熔断策略
慢调用比例
举例:统计时长5秒,比例阈值70%的请求在最大RT(最大反应时间)1秒以上,则触发熔断降级。
熔断时长:断开时间,时间到了则会发一个请求试探,有用则通,没用则再继续熔断。
最小请求数:指统计时长内最少发的请求数量。
RT:response time
此处:由于是在远程调用处设置了熔断策略,所以熔断之后走openFeign
的fallback
处理
此处发生熔断后直接回调,不发远程请求
实验
异常比例
统计时长内发生异常的比例达到比例阈值,则发生熔断.
此处发生熔断后直接回调,不发远程请求。
异常数
统计时长内发生异常的数量达到设置的异常数,则发生熔断.
此处发送了10个远程调用的请求发现都是异常的,直接熔断,30秒内不会再进行远程调用
5.8、规则 - 热点参数
案例
@GetMapping("/seckill")
@SentinelResource(value = "seckill-order",fallback = "seckillFallback") //此处用于测试热点规则
// public Order seckill(@RequestParam("userId") Long userId,
// @RequestParam("productId") Long productId) {
//此处模拟不传参数,给默认参数
// public Order seckill(@RequestParam(value = "userId",defaultValue = "888") Long userId,
// @RequestParam(value = "productId",defaultValue = "1000") Long productId) {
public Order seckill(@RequestParam(value = "userId",required = false) Long userId,@RequestParam(value = "productId",required = false) Long productId) {Order order = orderService.createOrder(userId, productId);order.setId(Long.MAX_VALUE);return order;
}public Order seckillFallback(Long userId, Long productId, Throwable exception) {System.out.println("seckillFallback...");Order order = new Order();order.setId(productId);order.setUserId(userId);order.setAddress("seckillFallback异常信息" + exception.getClass());return order;
}
需求一
携带流控参数的参与流控,不携带的不参与。
eg: 带了userId
流空规则生效,不带不生效。
参数索引标注了参数位置
需求二
这里在参数索引位置为0的参数中,设置了参数值=6的特殊情况,它的限流阈值是10000.
意味着,除了userId=6的请求,其他请求还是每秒不能超过1
需求三
意味着在索引参数为1的参数上,其他请求阈值是100000,但是参数值=666的情况,则限流阈值=0,意味着参数值为666的商品,不让访问。
为什么这里没走fallback回调?
因为用错了异常,不能用BlockException
,应该用Throwable
六、Gateway - 网关
官网:https://spring.io/projects/spring-cloud-gateway
6.1、基础入门
6.1.1、功能
6.1.2、原理
6.1.3、环境配置
建module: gateway微服务
改pom
<dependencies><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-gateway</artifactId></dependency><dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId></dependency><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-loadbalancer</artifactId></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><scope>annotationProcessor</scope></dependency>
</dependencies>
写yml
application.yml
spring:application:name: gatewaycloud:nacos:discovery:server-addr: 127.0.0.1:8848profiles:include: route
server:port: 80
application-route.yml
先简单测试
spring:cloud:gateway:routes:- id: orderuri: lb://service-orderpredicates:- Path=/api/order/**- id: producturi: lb://service-productpredicates:- Path=/api/product/**
后期完整版
spring:cloud:gateway:globalcors:cors-configurations:'[/**]':allowedOriginPatterns: '*'allowedHeaders: '*'allowedMethods: '*'default-filters:- AddResponseHeader=X-Response-Abc,123routes:- id: binguri: https://cn.bing.com/predicates:- name: Pathargs:patterns: /search- name: Queryargs:param: qregexp: haha
# - Vip=user,lei- name: Vipargs:param: uservalue: leiorder: 10- id: order-routeuri: lb://service-orderpredicates:- name: Pathargs:patterns: /api/order/**matchTrailingSlash: truefilters:- RewritePath=/api/order/?(?<segment>.*), /$\{segment}- OnceToken=X-Response-Token,jwtorder: 1- id: product-routeuri: lb://service-productpredicates:- Path=/api/product/**filters:- RewritePath=/api/product/?(?<segment>.*), /$\{segment}order: 2
启动类
@SpringBootApplication
@EnableDiscoveryClient
public class GatewayMainApplication {public static void main(String[] args) {SpringApplication.run(GatewayMainApplication.class, args);}
}
业务类
由于配置文件写了路由过滤规则,则需要改
修改OrderController
修改ProductFeignClient
修改ProductController
测试
6.2、Predicate - 断言
短写法
这里根据不同服务的predicates
条件,将请求执行不同服务的uri
order:代表predicates
执行顺序,越小越优先
spring:cloud:gateway:routes:- id: orderuri: lb://service-orderpredicates:- Path=/api/order/**- id: producturi: lb://service-productpredicates:- Path=/api/product/**
长写法
predicates可选列表
名 | 参数(个数/类型) | 作用 |
---|---|---|
After | 1/datetime | 在指定时间之后 |
Before | 1/datetime | 在指定时间之前 |
Between | 2/datetime | 在指定时间区间内 |
Cookie | 2/string,regexp | 包含cookie名且必须匹配指定值 |
Header | 2/string,regexp | 包含请求头且必须匹配指定值 |
Host | N/string | 请求host必须是指定枚举值 |
Method | N/string | 请求方式必须是指定枚举值 |
Path | 2/List<String>,bool | 请求路径满足规则,是否匹配最后的/ |
Query | 2/string,regexp | 包含指定请求参数 |
RemoteAddr | 1/List<String> | 请求来源于指定网络域(CIDR写法) |
Weight | 2/string,int | 按指定权重负载均衡 |
XForwardedRemoteAddr | 1/List<string> | 从X-Forwarded-For请求头中解析请求来源,并判断是否来源于指定网络域 |
自定义predicates
根据QueryRoutePredicateFactory
仿写
@Component
public class VipRoutePredicateFactory extends AbstractRoutePredicateFactory<VipRoutePredicateFactory.Config> {public VipRoutePredicateFactory() {super(Config.class);}//段格式传参顺序@Overridepublic List<String> shortcutFieldOrder() {return Arrays.asList("param", "value");}@Overridepublic Predicate<ServerWebExchange> apply(Config config) {return new GatewayPredicate() {@Overridepublic boolean test(ServerWebExchange serverWebExchange) {// localhost/search?q=haha&user=leifengyangServerHttpRequest request = serverWebExchange.getRequest();String first = request.getQueryParams().getFirst(config.param);return StringUtils.hasText(first) && first.equals(config.value);}};}@Validatedpublic static class Config {@NotEmptyprivate String param;@NotEmptyprivate String value;public @NotEmpty String getParam() {return param;}public void setParam(@NotEmpty String param) {this.param = param;}public @NotEmpty String getValue() {return value;}public void setValue(@NotEmpty String value) {this.value = value;}}
}
6.3、Filter - 过滤器
写法
也有长写法和短写法
- RewritePath=/api/order/?(?<segment>.*), /$\{segment}
这条规则的作用是:将路径 /api/order/{任意内容}
重写为 /{任意内容}
。换句话说,它去掉了 /api/order/
前缀,保留了后面的路径部分。
例如:
- /api/order/123 会被重写为 /123
- /api/order/abc/xyz 会被重写为 /abc/xyz
这类配置通常用于将请求从一个路径重定向到另一个路径,或者将路径中的某些部分移除。
在此处的作用是不用在各微服务加前缀,这里可以统一管理。
Filter可选列表
名 | 参数(个数/类型) | 作用 |
---|---|---|
AddRequestHeader | 2/string | 添加请求头 |
AddRequestHeadersIfNotPresent | 1/List<string> | 如果没有则添加请求头,key:value方式 |
AddRequestParameter | 2/string、string | 添加请求参数 |
AddResponseHeader | 2/string、string | 添加响应头 |
CircuitBreaker | 1/string | 仅支持forward:/inCaseOfFailureUseThis方式进行熔断 |
CacheRequestBody | 1/string | 缓存请求体 |
DedupeResponseHeader | 1/string | 移除重复响应头,多个用空格分割 |
FallbackHeaders | 1/string | 设置Fallback头 |
JsonToGrpc | 请求体Json转为gRPC | |
LocalResponseCache | 2/string | 响应数据本地缓存 |
MapRequestHeader | 2/string | 把某个请求头名字变为另一个名字 |
ModifyRequestBody | 仅 Java 代码方式 | 修改请求体 |
ModifyResponseBody | 仅 Java 代码方式 | 修改响应体 |
PrefixPath | 1/string | 自动添加请求前缀路径 |
PreserveHostHeader | 0 | 保护Host头 |
RedirectTo | 3/string | 重定向到指定位置 |
RemoveJsonAttributesResponseBody | 1/string | 移除响应体中的某些Json字段,多个用,分割 |
RemoveRequestHeader | 1/string | 移除请求头 |
RemoveRequestParameter | 1/string | 移除请求参数 |
RemoveResponseHeader | 1/string | 移除响应头 |
RequestHeaderSize | 2/string | 设置请求大小,超出则响应431状态码 |
RequestRateLimiter | 1/string | 请求限流 |
RewriteLocationResponseHeader | 4/string | 重写Location响应头 |
RewritePath | 2/string | 路径重写 |
RewriteRequestParameter | 2/string | 请求参数重写 |
RewriteResponseHeader | 3/string | 响应头重写 |
SaveSession | 0 | session保存,配合spring-session框架 |
SecureHeaders | 0 | 安全头设置 |
SetPath | 1/string | 路径修改 |
SetRequestHeader | 2/string | 请求头修改 |
SetResponseHeader | 2/string | 响应头修改 |
SetStatus | 1/int | 设置响应状态码 |
StripPrefix | 1/int | 路径层级拆除 |
Retry | 7/string | 请求重试设置 |
RequestSize | 1/string | 请求大小限定 |
SetRequestHostHeader | 1/string | 设置Host请求头 |
TokenRelay | 1/string | OAuth2的token转发 |
自定义Filter
@Component
public class OnceTokenGatewayFilterFactory extends AbstractNameValueGatewayFilterFactory {@Overridepublic GatewayFilter apply(NameValueConfig config) {return new GatewayFilter() {@Overridepublic Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {return chain.filter(exchange).then(Mono.fromRunnable(()->{//注意这里是responseServerHttpResponse response = exchange.getResponse();HttpHeaders headers = response.getHeaders();String value = config.getValue();if ("uuid".equalsIgnoreCase(value)) {value = UUID.randomUUID().toString();}if ("jwt".equalsIgnoreCase(value)) {value = "JWT";}headers.add(config.getName(), value);}));}};}
}
测试:
设置默认filter
6.4、CORS - 跨域处理
全局跨域
spring:cloud:gateway:globalcors:cors-configurations:'[/**]':allowedOrigins: "https://docs.spring.io"allowedMethods:- GET
- 适用于所有路径(
[/**]
)。 - 允许任何来源(
allowedOriginPatterns: '*'
) 访问 API。 - 允许所有 HTTP 头部(
allowedHeaders: '*'
)。 - 允许所有请求方法(
allowedMethods: '*'
),包括 GET、POST、PUT、DELETE 等。
[/**]
其中 /**
代表所有 API 路径
注意:[/**]
必须加方括号 []
,否则 YAML
解析可能会出错。
测试
局部跨域
spring:cloud:gateway:routes:- id: cors_routeuri: https://example.orgpredicates:- Path=/service/**metadata:cors:allowedOrigins: '*'allowedMethods:- GET- POSTallowedHeaders: '*'maxAge: 30
6.5、GlobalFilter
/*
* 此实例作用:GlobalFilter 通常,全局过滤器在网关层 用于 拦截所有请求,
* 而局部过滤器(GatewayFilter)用于 单个路由。
* 案例:请求耗时=请求结束时间-请求进来时间
* */
@Component
@Slf4j
public class RTGlobalFilter implements GlobalFilter, Ordered {@Overridepublic Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {ServerHttpRequest request = exchange.getRequest();ServerHttpResponse response = exchange.getResponse();String uri = request.getURI().toString();long startTime = System.currentTimeMillis();log.info("请求【{}】开始,时间{}", uri, startTime);//=============上面是前置逻辑==================Mono<Void> filter = chain.filter(exchange).doFinally(r -> {long endTime = System.currentTimeMillis();log.info("请求【{}】结束,时间{},耗时:{}ms", uri, endTime, endTime - startTime);});return filter;}@Overridepublic int getOrder() {return 0;}
}
测试
参考链接:
https://blog.csdn.net/weixin_56884174/article/details/145573890
https://www.yuque.com/leifengyang/sutong/oz4gbyh5maa0rmxu#tHTwd