实战

路由过滤器工厂

路由过滤器允许以某种方式修改传入的HTTP请求或传出的HTTP响应。路由过滤器的作用域是特定的路由。SpringCloud Gateway包括许多内置的GatewayFilter工厂。目前官网提供33种路由过滤器工厂,前面示例中filters里的StripPrefix就是其中一种:

在库存微服务控制器中增加打印

    @RequestMapping("/deduct")
public String storage(HttpServletRequest request){
log.info("请求头X-Request-red:{}",request.getHeader("X-Request-red"));
return "扣减库存";
}

Nacos中网关的配置增加AddRequestHeader=X-Request-red, blue

server:
port: 4090
spring:
cloud:
gateway:
routes:
- id: storage_route
uri: lb://ecom-storage-service
predicates:
- Path=/storage-service/**
- Quantity=100,200
filters:
- StripPrefix=1
- AddRequestHeader=X-Request-red, blue

访问http://localhost:4090/storage-service/deduct?quantity=100 ,从库存微服务的日志可以看到网关的过滤器已经添加请求头信息

其他很多种过滤器实现各位有时间可以一一尝试

自定义局部过滤器工厂

建立一个授权的自定义工厂过滤器工厂,从redis读取授权信息验证,先创建RedisConfig配置类

package cn.itxs.ecom.gateway.config;

import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer; @Configuration
@Slf4j
public class RedisConfig{
@Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
RedisTemplate<String, Object> template = new RedisTemplate<String, Object>();
template.setConnectionFactory(redisConnectionFactory);
Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
ObjectMapper om = new ObjectMapper();
om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
//om.activateDefaultTyping(LaissezFaireSubTypeValidator.instance , ObjectMapper.DefaultTyping.NON_FINAL);
jackson2JsonRedisSerializer.setObjectMapper(om);
StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
// key采用String的序列化方式
template.setKeySerializer(stringRedisSerializer);
// hash的key也采用String的序列化方式
template.setHashKeySerializer(stringRedisSerializer);
// value序列化方式采用jackson
//template.setValueSerializer(jackson2JsonRedisSerializer);
template.setValueSerializer(stringRedisSerializer);
// hash的value序列化方式采用jackson
//template.setHashValueSerializer(jackson2JsonRedisSerializer);
template.setHashValueSerializer(stringRedisSerializer);
template.afterPropertiesSet();
return template;
}
}

redis信息放在Nacos配置commons-dev.yaml里

创建过滤器工厂AuthorizeGatewayFilterFactory.java继承自AbstractGatewayFilterFactory

package cn.itxs.ecom.gateway.factory;

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import java.util.Arrays;
import java.util.List; @Component
@Slf4j
public class AuthorizeGatewayFilterFactory extends AbstractGatewayFilterFactory<AuthorizeGatewayFilterFactory.Config> { private static final String AUTHORIZE_TOKEN = "token";
private static final String AUTHORIZE_UID = "uid"; @Autowired
private RedisTemplate redisTemplate; public AuthorizeGatewayFilterFactory() {
super(Config.class);
log.info("Loaded GatewayFilterFactory [Authorize]");
} @Override
public List<String> shortcutFieldOrder() {
return Arrays.asList("enabled");
} @Override
public GatewayFilter apply(AuthorizeGatewayFilterFactory.Config config) {
return (exchange, chain) -> {
if (!config.isEnabled()) {
return chain.filter(exchange);
} ServerHttpRequest request = exchange.getRequest();
HttpHeaders headers = request.getHeaders();
String token = headers.getFirst(AUTHORIZE_TOKEN);
String uid = headers.getFirst(AUTHORIZE_UID);
if (token == null) {
token = request.getQueryParams().getFirst(AUTHORIZE_TOKEN);
}
if (uid == null) {
uid = request.getQueryParams().getFirst(AUTHORIZE_UID);
} ServerHttpResponse response = exchange.getResponse();
if (!StringUtils.hasText(token) || !StringUtils.hasText(uid)) {
response.setStatusCode(HttpStatus.UNAUTHORIZED);
return response.setComplete();
}
String authToken = (String) redisTemplate.opsForValue().get(uid);
if (authToken == null || !authToken.equals(token)) {
response.setStatusCode(HttpStatus.UNAUTHORIZED);
return response.setComplete();
}
return chain.filter(exchange);
};
} public static class Config {
// 控制是否开启认证
private boolean enabled; public Config() {} public boolean isEnabled() {
return enabled;
} public void setEnabled(boolean enabled) {
this.enabled = enabled;
}
}
}

如果授权不通过返回HttpStatus.UNAUTHORIZED也即是 http 401

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7fDzQbqT-1657432430026)(http://www.itxiaoshen.com:3001/assets/1657431654370DW5dZAyW.png)]

网关Nacos配置文件增加Authorize=true

server:
port: 4090
spring:
cloud:
gateway:
routes:
- id: storage_route
uri: lb://ecom-storage-service
predicates:
- Path=/storage-service/**
- Quantity=100,200
filters:
- StripPrefix=1
- AddRequestHeader=X-Request-red, blue
- Authorize=true

由于目前redis里面没有uid和token的信息,访问http://localhost:4090/storage-service/deduct?quantity=100&uid=1001&token=abc 返回401状态

我们往redis写入uid为1001的数据

再次访问http://localhost:4090/storage-service/deduct?quantity=100&uid=1001&token=abc ,则可以正常获取结果

全局过滤器

  • 前面配置使用的lb就是全局过滤器来实现的。ReactiveLoadBalancerClientFilter在名为ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR的交换属性中查找URI。如果URL有一个lb方案(例如lb://myservice),它使用Spring Cloud ReactorLoadBalancer来解析名称(在本例中是myservice)到一个实际的主机和端口,并替换相同属性中的URI。未修改的原始URL被追加到ServerWebExchangeUtils中的列表中。GATEWAY_ORIGINAL_REQUEST_URL_ATTR属性。GATEWAY_SCHEME_PREFIX_ATTR属性来查看它是否等于lb。如果等于,则应用相同的规则。
  • 全局过滤器作为bean注册成功后,不需要进行配置,就可以直接生效。全局过滤器的作用范围是对所有的请求,而局部过滤器是针对路由。GlobalFilter 是用来定义全局过滤器的接口,通过实现GlobalFilter接口可以实现各种自定义过滤器。有多个拦截器时通过Ordered接口实现getOrder()方法来指定执行顺序,返回值越小执行顺序越靠前需要添加@Component注解;
  • 利用全局过滤器,可以实现统一的鉴权处理,日志处理等。
  • 在配置时可以通过spring.cloud.gateway.default-filters实现所配置的过滤器全局生效,但这种方法在实际中比较少用。

官方目前全局过滤器有,详细可以查阅

  • org.springframework.cloud.gateway.filter.NettyWriteResponseFilter, order = -1

    • 将Netty代理调用的response数据流写入ServerHttpResponse的body中。当NettyRouting拿到远程调用的结果数据流之后会将其写入当前请求exchange的attributes中。
  • org.springframework.cloud.gateway.filter.RouteToRequestUrlFilter, order = 10000
    • 将网关上的请求转为对应业务应用的真实ip的请求。请求进来时path的前缀是gateway的地址(ip+port或域名),需要将其uri映射至服务id上;比如:将path的192.168.20.134:10080映射至服务lb://{serviceId};对于绝对路径配置的服务,exchange的GATEWAY_ROUTE_ATTR属性将会是null,直接过滤到下一个过滤器,不会发生path的真实映射。
  • org.springframework.cloud.gateway.filter.ReactiveLoadBalancerClientFilter, order = 10150
    • 负责服务真实ip的映射,对服务进行负载均衡。
  • org.springframework.cloud.gateway.filter.WebsocketRoutingFilter, order = 2147483646
    • 实现了gateway对于websocket的支持,内部通过websocketClient实现将一个http请求协议换转成websocket,实现调用方无感知的请求websocket的服务,只需要将schme设置成ws或者wss这么简单。
  • org.springframework.cloud.gateway.filter.NettyRoutingFilter, order = 2147483647
  • org.springframework.cloud.gateway.filter.ForwardRoutingFilter, order = 2147483647
    • 是一个结束操作,经过filter chain的链式调用,最终将exchange交还给web handler做http请求处理。

自定义全局过滤器

创建ItxsGloablFilter.java的全局过滤器做简单日志打印

package cn.itxs.ecom.gateway.factory;

import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono; @Component
@Slf4j
public class ItxsGloablFilter implements GlobalFilter, Ordered {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
log.info("ItxsGloablFilter start");
return chain.filter(exchange);
} @Override
public int getOrder() {
return -1;
}
}

配置类将ItxsGloablFilter作为Spring的Bean

package cn.itxs.ecom.gateway.config;

import cn.itxs.ecom.gateway.factory.ItxsGloablFilter;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; @Configuration
public class FilterConfig {
@Bean
public GlobalFilter itxsFilter() {
return new ItxsGloablFilter();
}
}

启动网关微服务,访问http://localhost:4090/storage-service/deduct?quantity=100&uid=1001&token=abc ,可以看到打印日志信息

Reactor Netty Access Logs

要启用Reactor Netty访问日志,设置-Dreactor.netty.http.server.accessLogEnabled=true。您可以将日志记录系统配置为具有单独的访问日志文件。创建Logback配置的示例如下:

    <appender name="accessLog" class="ch.qos.logback.core.FileAppender">
<file>access_log.log</file>
<encoder>
<pattern>%msg%n</pattern>
</encoder>
</appender>
<appender name="async" class="ch.qos.logback.classic.AsyncAppender">
<appender-ref ref="accessLog" />
</appender> <logger name="reactor.netty.http.server.AccessLog" level="INFO" additivity="false">
<appender-ref ref="async"/>
</logger>

CORS Configuration

通过配置网关控制CORS行为。“全局”CORS配置是URL模式到Spring Framework CorsConfiguration的映射。配置CORS的示例如下:

spring:
cloud:
gateway:
globalcors:
cors-configurations:
'[/**]':
allowedOrigins: "http://www.itxiaoshen.com"
allowedMethods:
- GET

这个配置对于所有GET请求路径,来自www.itxiaoshen.com的请求都允许使用CORS请求。要为没有被网关路由谓词处理的请求提供相同的CORS配置,可以设置spring.cloud.gateway.globalcors。属性add-to-simple url-handler-mapping为true。

Gateway整合Sentinel流控降级

SpringCloud Gateway本身就有限流的功能,但是结合功能更加强大的专业限流组件如sentinel是生产环境的首选,虽然在前面有两篇文章《SpringCloud Alibaba分布式流量控制组件Sentinel实战与源码分析》介绍了Sentinel的内容,sentinel可以作为各微服务的限流组件,也可以作为网关的限流组件,也即是说流控即可以放在各微服务端也可以放在网关中,这小节我们就要完成网关整合Sentinel的流控使用。

Sentinel 从 1.6.0 版本开始就提供了 Spring Cloud Gateway 的适配,可以提供两种资源维度的限流:

  • route维度:即在配置文件中配置的路由条目,资源名为对应的 routeId,这种属于粗粒度的限流,一般是对某个微服务进行限流。
  • 自定义API维度:用户可以利用 Sentinel 提供的API来自定义一些API分组,这种属于细粒度的限流,针对某一类的uri进行匹配限流,可以跨多个微服务。

在网关微服务加入sentinel及sentinel-gateway的依赖

        <dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-sentinel-gateway</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>

网关的配置文件中加入控制台配置

spring:
cloud:
sentinel:
transport:
dashboard: localhost:8858

我们还是放在网关的Nacos配置

启动sentinel的控制台,前面文章《SpringCloud Alibaba分布式流量控制组件Sentinel实战与源码分析-上》中启动命令

访问http://localhost:4090/storage-service/deduct?quantity=100&uid=1001&token=abc 后在sentinel控制台中可以看到和前面sentinel启动控制台有一些不一样,如多了一个API管理菜单,在请求链路中也少了一些操作功能,包括流控功能都有锁变化,各位可以一一感受。

新增一个网关流控规则测试下,在请求链路找到API名称为刚问的访问路由id,点击流控按钮,这个页面和原本Sentinel控制台已经有差异了,选择API类型为Route ID,新增规则后

快速连续访问2次http://localhost:4090/storage-service/deduct?quantity=100&uid=1001&token=abc 后出现被流控的提示,网关整合sentinel流控功能已生效。

Sentinel流控降级详细配置

Burst size设置

点击流控规则菜单,在网关流控规则列表中选择上一次设置storage_route这个流控规则,点击右边编辑按钮,修改Burst size为2点击保存

这时候快速连续访问2次 http://localhost:4090/storage-service/deduct?quantity=100&uid=1001&token=abc 后不会被流控,而快速连续访问4次后则又出现被流控的提示。

针对请求属性设置

编辑storage_route,勾选针对请求属性,其中我们选择URL参数,填写参数名称和属性值匹配,点击保存

这时候快速连续访问4次 http://localhost:4090/storage-service/deduct?quantity=100&uid=1001&token=abc&code=1002 不会被流控,而再快速连续访问4次http://localhost:4090/storage-service/deduct?quantity=100&uid=1001&token=abc&code=1001 则出现被流控的提示。还可以设置为子串或者正则匹配模式。

API分组

如果需要针对URL进行流控,可以使用API分组的功能,先删除前面流控规则,这里使用库存微服务中两个接口

添加自定API,输入两个匹配串/storage-service/deduct和/storage-service/list

在请求链路中设置流控规则,API类型为API分组,然后选择刚才创建的storage-api,点击新增

快速连续访问2次http://localhost:4090/storage-service/deduct?quantity=100&uid=1001&token=abc 出现被流控的提示

快速连续访问2次http://localhost:4090/storage-service/list?quantity=100&uid=1001&token=abc 也出现被流控的提示

其他如降级设置,可以参考前面sentinel的文章,这里就不再做介绍了

统一返回降级提示的yaml配置如下,也可以通过GatewayCallbackManager的java配置方式实现。

spring:
cloud:
sentinel:
scg:
fallback:
mode: response
response-body: '{"code":403,"message":"被降级了"}'

**本人博客网站 **IT小神 www.itxiaoshen.com

SpringCloudGateway微服务网关实战与源码分析 - 中的更多相关文章

  1. SpringCloud Gateway微服务网关实战与源码分析-上

    概述 定义 Spring Cloud Gateway 官网地址 https://spring.io/projects/spring-cloud-gateway/ 最新版本3.1.3 Spring Cl ...

  2. SpringCloudAlibaba分布式流量控制组件Sentinel实战与源码分析-中

    实战示例 控制台初体验 Sentinel的控制台启动后,控制台页面的内容数据都是空的,接下来我们来逐步操作演示结合控制台的使用,在上一节也已说明整合SpringCloud Alibaba第一步先加入s ...

  3. SpringCloudAlibaba分布式事务解决方案Seata实战与源码分析-中

    事务模式 概述 在当前的技术发展阶段,不存一个分布式事务处理机制可以完美满足所有场景的需求.一致性.可靠性.易用性.性能等诸多方面的系统设计约束,需要用不同的事务处理机制去满足. 目前使用的流行度情况 ...

  4. Java生鲜电商平台-秒杀系统微服务架构设计与源码解析实战

    Java生鲜电商平台-秒杀系统微服务架构设计与源码解析实战 Java生鲜电商平台-  什么是秒杀 通俗一点讲就是网络商家为促销等目的组织的网上限时抢购活动 比如说京东秒杀,就是一种定时定量秒杀,在规定 ...

  5. 【一起学源码-微服务】Nexflix Eureka 源码十:服务下线及实例摘除,一个client下线到底多久才会被其他实例感知?

    前言 前情回顾 上一讲我们讲了 client端向server端发送心跳检查,也是默认每30钟发送一次,server端接收后会更新注册表的一个时间戳属性,然后一次心跳(续约)也就完成了. 本讲目录 这一 ...

  6. 【一起学源码-微服务】Nexflix Eureka 源码十三:Eureka源码解读完结撒花篇~!

    前言 想说的话 [一起学源码-微服务-Netflix Eureka]专栏到这里就已经全部结束了. 实话实说,从最开始Eureka Server和Eureka Client初始化的流程还是一脸闷逼,到现 ...

  7. 从flink-example分析flink组件(3)WordCount 流式实战及源码分析

    前面介绍了批量处理的WorkCount是如何执行的 <从flink-example分析flink组件(1)WordCount batch实战及源码分析> <从flink-exampl ...

  8. Netty服务端的启动源码分析

    ServerBootstrap的构造: public class ServerBootstrap extends AbstractBootstrap<ServerBootstrap, Serve ...

  9. 微服务网关实战——Spring Cloud Gateway

    导读 作为Netflix Zuul的替代者,Spring Cloud Gateway是一款非常实用的微服务网关,在Spring Cloud微服务架构体系中发挥非常大的作用.本文对Spring Clou ...

随机推荐

  1. XCTF练习题---MISC---快乐游戏题

    XCTF练习题---MISC---快乐游戏题 flag:UNCTF{c783910550de39816d1de0f103b0ae32} 解题步骤: 1.观察题目,下载附件 2.还真是一个游戏,赢了就得 ...

  2. python matplotlib在mac os x 中如何显示中文,完美解决

    一. 下载相关的中文字体 simhei 文件: 下载地址 二.通过以下代码查找matplotlib的数据存放位置: import matplotlib print(matplotlib.matplot ...

  3. Open Harmony移植:build lite配置目录全梳理

    摘要:本文主要介绍build lite 轻量级编译构建系统涉及的配置目录的用途,分析相关的源代码. 本文分享自华为云社区<移植案例与原理 - build lite配置目录全梳理>,作者:z ...

  4. MySQL8新增降序索引

    MySQL8新增降序索引 桃花坞里桃花庵,桃花庵里桃花仙.桃花仙人种桃树,又摘桃花卖酒钱. 一.MySQL5.7 降序索引 MySQL在语法上很早就已经支持降序索引,但实际上创建的却仍然是升序索引,如 ...

  5. 斯坦福NLP课程 | 第11讲 - NLP中的卷积神经网络

    作者:韩信子@ShowMeAI,路遥@ShowMeAI,奇异果@ShowMeAI 教程地址:http://www.showmeai.tech/tutorials/36 本文地址:http://www. ...

  6. python 通过线上API查询ip归属地

    API为国外API,频率限制1分钟45个ip 脚本如下 1 #!/usr/bin/env python3 2 #-*-coding:utf-8-*- 3 4 import requests,re,js ...

  7. Soa: 一个轻量级的微服务库

    Soa 项目地址:Github:MatoApps/Soa 介绍 一个轻量级的微服务库,基于.Net 6 + Abp框架 可快速地将现有项目改造成为面向服务体系结构,实现模块间松耦合. 感谢 Rabbi ...

  8. hihocoder 1193 树堆 解题报告

    题目大意:给出一棵有根树(根为 \(0\) ),点有点权.可以删除点(非根),并将其子树接到其父亲上.我们称一个树为树堆当前仅当树上每个点都满足其权值大于等于其子树中所有点的点权.现在对于每个点要求其 ...

  9. application.properties文件中暗藏玄机

    上次分享了如何一步一步搭建一个springboot的项目,详细参见<5分钟快速搭建一个springboot的项目>,最终的结果是在"8080"端口搭建起了服务,并成功访 ...

  10. 我的第一篇随笔-Test

    用于测试 中文字符 English character