- Spring Cloud Gateway快速开始
- 环境搭建
1. 引入依赖
主项目
<properties>
<java.version>1.8</java.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<spring-boot.version>2.3.2.RELEASE</spring-boot.version>
<spring-cloud.version>Hoxton.SR9</spring-cloud.version>
<spring-cloud-alibaba.version>2.2.6.RELEASE</spring-cloud-alibaba.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.16</version>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>${spring-boot.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>${spring-cloud-alibaba.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
子项目1
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.38</version>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.4.2</version>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>2.9.2</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>2.9.2</version>
</dependency>
</dependencies>
子项目2网关
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
<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>
</dependencies>
注意:会和spring-webmvc的依赖冲突,需要排除spring-webmvc
2.编写yml配置文件
子项目1
server:
port: 9001
spring:
application:
name: stockmodule
datasource:
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql://192.168.64.135:3306/mydemo?useSSL=false&serverTimezone=Asia/Shanghai&characterEncoding=utf-8&autoReconnect=true
username: root
password: 3090_Cmok
cloud:
nacos:
discovery:
server-addr: 192.168.64.135:8848
username: nacos
password: nacos
namespace: public
子项目2 网关
server:
port: 9002
spring:
application:
name: gateway
cloud:
gateway:
routes:
- id: stockservices
uri: lb://stockmodule
predicates:
- Path=/xxx/**
# - After=2022-07-01T16:59:59.259+08:00[Asia/Shanghai]
# - Header=token,\d+
- Method=GET
- Query=tt,12
filters:
- StripPrefix=1
- AddRequestHeader=token,123456
nacos:
discovery:
server-addr: 192.168.64.135:8848
username: nacos
password: nacos
namespace: public
集成Nacos
现在在配置文件中写死了转发路径的地址, 前面我们已经分析过地址写死带来的问题, 接下来我们从注册中心获取此地址。
1. 引入依赖
编写yml配置文件
简写: 去掉关于路由的配置,自动寻找服务
编写子项目1 (domian mapper service controller)
#domain
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
@ApiModel("商品库存类")
public class Stocks {
@ApiModelProperty("库存编号")
private Integer id;
@ApiModelProperty("商品编号")
private Integer shopid;
@ApiModelProperty("当前编号")
private Integer storenum;
}
#mapper接口
@Mapper
public interface StockMapper extends BaseMapper<Stocks> {
}
#service接口
public interface StockServic {
public List<Stocks> findStock();
}
#service实现类
@Service
public class StockServicImpl extends ServiceImpl<StockMapper, Stocks> implements StockServic {
@Override
public List<Stocks> findStock() {
return this.list();
}
}
#controller层
@RestController
@RequestMapping("/stock-serv")
@Api(value = "库存服务API")
//public class StockCtrl {
// @Resource
// private StockServic stockServic;
@RequestHeader("token")String token
//
// @ApiOperation(value = "查询所有的商品当前库存",httpMethod = "GET")
// @ApiImplicitParams({
// @ApiImplicitParam(name = "id",value = "用户id",required = true,dataType = "int",paramType ="query")
// })
// @RequestMapping(value = "/findall")
// public List<Stocks> findAll(@RequestParam("id") int id){
System.out.println("获取头部信息token:============》"+token);
// return stockServic.findStock();
// }
//}
public class StockCtrl {
@Resource
private StockServic stockServic;
// @RequestHeader("token")String token
@RequestMapping(value = "/findall")
public List<Stocks> findAll(){
// System.out.println("获取头部信息token:============》"+token);
return stockServic.findStock();
}
}
swagger配置
@Configuration
@EnableSwagger2
public class SwaggerConfiguration {
@Bean
public Docket docket(){
return new Docket(DocumentationType.SWAGGER_2)
.apiInfo(apiInfo());
}
//配置接口页面上的信息
public ApiInfo apiInfo(){
return new ApiInfoBuilder().title("网关项目接口测试")
.description("测试接口说明")
.version("v1.0")
.termsOfServiceUrl("http://www.baidu.com")
.contact("程序员")
.build();
}
}
3)测试
这时候,就发现只要按照网关地址/
http://localhost:9001/stock-serv/findall
#配置网关浏览地址
http://localhost:9002/xxx/stock-serv/findall
-
- 路由断言工厂(Route Predicate Factories)配置
作用: 当请求gateway的时候, 使用断言对请求进行匹配, 如果匹配成功就路由转发, 如果匹配失败就返回404 类型:内置,自定义
SpringCloud Gateway包括许多内置的断言工厂,所有这些断言都与HTTP请求的不同属性匹配。具体如下:
基于Datetime类型的断言工厂
此类型的断言根据时间做判断,主要有三个:
AfterRoutePredicateFactory: 接收一个日期参数,判断请求日期是否晚于指定日期BeforeRoutePredicateFactory: 接收一个日期参数,判断请求日期是否早于指定日期BetweenRoutePredicateFactory: 接收两个日期参数,判断请求日期是否在指定时间段内ZonedDateTime.now()
基于远程地址的断言工厂
RemoteAddrRoutePredicateFactory:接收一个IP地址段,判断请求主机地址是否在地址段中
基于Cookie的断言工厂
CookieRoutePredicateFactory:接收两个参数,cookie 名字和一个正则表达式。 判断请求cookie是否具有给定名称且值与正则表达式匹配。
1 | ‐Cookie | =chocolate, ch. |
基于Header的断言工厂
HeaderRoutePredicateFactory:接收两个参数,标题名称和正则表达式。 判断请求Header是否具有给定名称且值与正则表达式匹配。
基于Host的断言工厂
HostRoutePredicateFactory:接收一个参数,主机名模式。判断请求的Host是否满足匹配规则。
基于Method请求方法的断言工厂
MethodRoutePredicateFactory:接收一个参数,判断请求类型是否跟指定的类型匹配。
1 | ‐Method | =GET |
基于Path请求路径的断言工厂
PathRoutePredicateFactory:接收一个参数,判断请求的URI部分是否满足路径规则。
基于Query请求参数的断言工厂
QueryRoutePredicateFactory :接收两个参数,请求param和正则表达式, 判断请求参数是否具有给定名称且值与正则表达式匹配。
基于路由权重的断言工厂
WeightRoutePredicateFactory:接收一个[组名,权重], 然后对于同一个组内的路由按照权重转发
2.2.5 自定义路由断言工厂
自定义路由断言工厂需要继承 AbstractRoutePredicateFactory 类,重写 apply 方法的逻辑。在 apply 方法中可以通过exchange.getRequest() 拿到 ServerHttpRequest 对象,从而可以获取到请求的参数、请求方式、请求头等信息。
1、 必须spring组件 bean
- 类必须加上RoutePredicateFactory作为结尾
- 必须继承AbstractRoutePredicateFactory
- 必须声明静态内部类 声明属性来接收 配置文件中对应的断言的信息
- 需要结合shortcutFieldOrder进行绑定 6.通过apply进行逻辑判断 true就是匹配成功 false匹配失败
注意: 命名需要以 RoutePredicateFactory 结尾
1@Component
2@Slf4j
3public class CheckAuthRoutePredicateFactory extends AbstractRoutePredicateFactory<CheckAuthRoutePredicateFactory.Con fig> {
4
5public CheckAuthRoutePredicateFactory() {
6super(Config.class);
7 }
8
9@Override
10public Predicate<ServerWebExchange> apply(Config config) {
11return new GatewayPredicate() {
12
13@Override
14public boolean test(ServerWebExchange serverWebExchange) {
15log.info("调用CheckAuthRoutePredicateFactory" + config.getName());
16if(config.getName().equals("xushu")){
17return true;
18 }
19 return false;
20 }
21 };
22 }
23
24 /**
25* 快捷配置
26* @return
27 */
28@Override
29public List<String> shortcutFieldOrder() {
30return Collections.singletonList("name");
31 }
32
33 public static class Config {
34
35 private String name;
36
37public String getName() {
38return name;
39 }
40
41public void setName(String name) {
42this.name = name;
43 }
44 }
45 }
yml中配置
| ||
12 | ‐ CheckAuth | =xushu |
-
- 过滤器工厂( GatewayFilter Factories)配置
Gateway 内置了很多的过滤器工厂,我们通过一些过滤器工厂可以进行一些业务逻辑处理器,比如添加剔除响应头,添加去除参数等
过滤器工厂 | 作用 | 参数 |
AddRequestHeader | 为原始请求添加Header | Header的名称及值 |
AddRequestParameter | 为原始请求添加请求参数 | 参数名称及值 |
AddResponseHeader | 为原始响应添加Header | Header的名称及值 |
DedupeResponseHeader | 剔除响应头中重复的值 | 需要去重的Header名称及去重策略 |
Hystrix | 为路由引入Hystrix的断路器保护 | HystrixCommand 的名称 |
FallbackHeaders | 为fallbackUri的请求头中添加具体的异常信息 | Header的名称 |
PrefixPath | 为原始请求路径添加前缀 | 前缀路径 |
PreserveHostHeader | 为请求添加一个preserveHostHeader=true 的 属 性,路由过滤器会检查该属性以决定是否要发送原始的Host | 无 |
RequestRateLimiter | 用于对请求限流,限流算法为令牌桶 | keyResolver、rateLimiter、statusCode、denyEmptyKey、emptyKeyStatus |
RedirectTo | 将原始请求重定向到指定的URL | http状态码及重定向的url |
RemoveHopByHopHeadersFilter | 为原始请求删除IETF组织规定的一系列Header | 默认就会启用,可以通过配置指定仅删除哪些Header |
RemoveRequestHeader | 为原始请求删除某个Header | Header名称 |
RemoveResponseHeader | 为原始响应删除某个Header | Header名称 |
RewritePath | 重写原始的请求路径 | 原始路径正则表达式以及重写后路径的正则表达式 |
RewriteResponseHeader | 重写原始响应中的某个Header | Header名称,值的正 则表达式,重写后的值 |
SaveSession | 在转发请求之前,强制执行WebSession::save 操作 | 无 |
secureHeaders | 为原始响应添加一系列起安全作用的响应头 | 无,支持修改这些安全响应头的值 |
SetPath | 修改原始的请求路径 | 修改后的路径 |
SetResponseHeader | 修改原始响应中某个Header的值 | Header名称,修改后的值 |
SetStatus | 修改原始响应的状态码 | HTTP 状态码,可以是数字,也可以是字符串 |
StripPrefix | 用于截断原始请求的路径 | 使用数字表示要截断的路径的数量 |
Retry | 针对不同的响应进行重试 | retries、statuses、methods、series |
设置允许接收最大请求包的大 小。如果请求包大小超过设置的值,则返回 413 Payload Too | 请求包大小,单位为字节,默认值为5M |
RequestSize | Large | |
在转发请求之前修改原始请求体内容 | 修改后的请求体内容 | |
ModifyRequestBody | ||
ModifyResponseBody | 修改原始响应体的内容 | 修改后的响应体内容 |
-
-
- 添加请求头
-
测试http://localhost:8888/order/testgateway
-
-
- 添加请求参数
-
测试http://localhost:8888/order/testgateway3
-
-
- 为匹配的路由统一添加前缀
-
mall-order中需要配置
| |||
3 context | ‐path: | /mall | ‐order |
测试:http://localhost:8888/order/findOrderByUserId/1 ====》 http://localhost:8020/mallorder/order/findOrderByUserId/1
-
-
- 重定向操作
-
测试:http://localhost:8888/order/findOrderByUserId/1
-
-
- 自定义过滤器工厂
-
继承AbstractNameValueGatewayFilterFactory且我们的自定义名称必须要以GatewayFilterFactory结尾并交给spring管理。
配置自定义的过滤器工厂
测试
全局过滤器(Global Filters)配置
局部过滤器和全局过滤器区别:
局部:局部针对某个路由, 需要在路由中进行配置全局:针对所有路由请求, 一旦定义就会投入使用
GlobalFilter 接口和 GatewayFilter 有一样的接口定义,只不过, GlobalFilter 会作用于所有路由。
-
-
- LoadBalancerClientFilter
-
LoadBalancerClientFilter 会查看exchange的属性 ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR 的值(一个URI),如果该值的scheme是 lb,比如:lb://myservice ,它将会使用Spring Cloud的LoadBalancerClient 来将 myservice 解析成实际的host和port,并替换掉 ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR 的内容。
其实就是用来整合负载均衡器Ribbon的
| |
8 | ‐ Path=/order/** |
-
-
- 自定义全局过滤器
-
要启用 Reactor Netty 访问日志,请设置Dreactor.netty.http.server.accessLogEnabled=true.
它必须是 Java 系统属性,而不是 Spring Boot 属性。
您可以将日志记录系统配置为具有单独的访问日志文件。以下示例创建一个 Logback 配置:
例 67.logback.xml
-
- Gateway跨域配置(CORS Configuration)
通过yml配置的方式
通过java配置的方式
-
- gateway整合sentinel流控降级
网关作为内部系统外的一层屏障, 对内起到一定的保护作用, 限流便是其中之一. 网关层的限流可以简单地针对不同路由进行限流, 也可针对业务的接口进行限流,或者根据接口的特征分组限流。网关限流 · alibaba/Sentinel Wiki · GitHub
1. 添加依赖
2.添加配置
-
-
- 控制台实现方式:
-
Sentinel 1.6.3 引入了网关流控控制台的支持,用户可以直接在 Sentinel 控制台上查看 API Gateway 实时的 route 和自定义 API 分组监控,管理网关规则和 API 分组配置。
从 1.6.0 版本开始,Sentinel 提供了 Spring Cloud Gateway 的适配模块,可以提供两种资源维度的限流: route 维度:即在 Spring 配置文件中配置的路由条目,资源名为对应的 routeId
自定义 API 维度:用户可以利用 Sentinel 提供的 API 来自定义一些 API 分组
自定义异常方式: 1.通过yml
1 spring:cloud.sentinel.scg.fallback.mode | = response | ||
2 spring.cloud.sentinel.scg.fallback.response | ‐body | = '{"code":403,"mes":"限流了"}' |
2.通过GatewayCallbackManager
2.6.1 代码实现方式:(了解)
用户可以通过 GatewayRuleManager.loadRules(rules) 手动加载网关规则GatewayConfiguration中添加
-
- 网关高可用
为了保证 Gateway 的高可用性,可以同时启动多个 Gateway 实例进行负载,在 Gateway 的上游使用 Nginx 或者 F5 进行负载转发以达到高可用。