目录
网关简介
1. 什么是Spring Cloud Gateway
1.1 核心概念
1.2 工作原理
2. Spring Cloud Gateway快速开始
2.1 环境搭建
2.2集成Nacos
3. 路由断言工厂(Route Predicate Factories)配置
3.1内置断言工厂
3.2自定义
4. 过滤器工厂( GatewayFilter Factories)配置
4.1Gateway 内置的过滤器工厂
4.2 自定义过滤器工厂
4.3 全局过滤器(Global Filters)配置
4.4 自定义全局过滤器
4.5Reactor Netty 访问日志
4.6Gateway跨域配置(CORS Configuration)
5.gateway整合sentinel流控降级
6. 网关高可用
7.demo源码:springcloudAlibaba: Alibaba微服务学习demo
网关简介
- 每个业务都会需要鉴权、限流、权限校验、跨域等逻辑,如果每个业务都各自为战,自己造轮子实现一遍,会 很蛋疼,完全可以抽出来,放到一个统一的地方去做。
- 如果业务量比较简单的话,这种方式前期不会有什么问题,但随着业务越来越复杂,比如淘宝、亚马逊打开一 个页面可能会涉及到数百个微服务协同工作,如果每一个微服务都分配一个域名的话,一方面客户端代码会很难维 护,涉及到数百个域名,另一方面是连接数的瓶颈,想象一下你打开一个APP,通过抓包发现涉及到了数百个远程 调用,这在移动端下会显得非常低效。
- 后期如果需要对微服务进行重构的话,也会变的非常麻烦,需要客户端配合你一起进行改造,比如商品服务, 随着业务变的越来越复杂,后期需要进行拆分成多个微服务,这个时候对外提供的服务也需要拆分成多个,同时需 要客户端配合你进行改造,非常蛋疼。
1. 什么是Spring Cloud Gateway
性能强劲:是第一代网关Zuul的1.6倍
- 基于Spring Framework 5, Project Reactor 和 Spring Boot 2.0 进行构建;
- 动态路由:能够匹配任何请求属性;
- 支持路径重写;
- 集成 Spring Cloud 服务发现功能(Nacos、Eruka);
- 可集成流控降级功能(Sentinel、Hystrix);
- 可以对路由指定易于编写的 Predicate(断言)和 Filter(过滤器);
1.1 核心概念
- 路由(route)
- 断言(predicates)
- 过滤器(Filter)
1.2 工作原理
2. Spring Cloud Gateway快速开始
2.1 环境搭建
<!--gateway的依赖 springcloud开发-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
2.编写yml配置文件
server:
port: 8083
spring:
application:
name: api-gateway
cloud:
#gateway配置
gateway:
#路由规则
routes:
- id: order_route #路由唯一标识,路由到order
uri: http://localhost:8022 #需要转发的地址
#路由断言 规则 用于规则配置
predicates:
- Path=/order-serv/**
# http://localhost:8083/order-serv/order/add 路由到↓
# http://localhost:8022/order-serv/order/add
filters:
- StripPrefix=1 #转发之前去掉第一层
#http://localhost:8022/order/add
2.2集成Nacos
<!--nacos注册发现依赖-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
server:
port: 8083
spring:
application:
name: api-gateway
cloud:
#gateway配置
gateway:
discovery:
locator:
enabled: true #是否启动自动识别nacos服务
nacos:
server-addr: localhost:8848
discovery:
username: nacos
password: nacos
namespace: public
3.测试
3. 路由断言工厂(Route Predicate Factories)配置
文档:https://docs.spring.io/spring-cloud-gateway/docs/current/reference/html/#gateway-request-predicates-factories
作用: 当请求gateway的时候, 使用断言对请求进行匹配, 如果匹配成功就路由转发, 如果匹配失败就返回404
类型:内置,自定义
3.1内置断言工厂
- 基于Datetime类型的断言工厂
- AfterRoutePredicateFactory: 接收一个日期参数,判断请求日期是否晚于指定日期
- BeforeRoutePredicateFactory: 接收一个日期参数,判断请求日期是否早于指定日期
- BetweenRoutePredicateFactory: 接收两个日期参数,判断请求日期是否在指定时间段内 ZonedDateTime.now()
‐ After=2019‐12‐31T23:59:59.789+08:00[Asia/Shanghai]
- 基于远程地址的断言工厂
‐ RemoteAddr=192.168.1.1/24
- 基于Cookie的断言工厂
‐Cookie=chocolate, ch
- 基于Header的断言工厂
‐Header=X‐Request‐Id, \d+
- 基于Host的断言工厂
‐Host=**.testhost.org
- 基于Method请求方法的断言工厂
‐Method=GET
- 基于Path请求路径的断言工厂
‐Path=/foo/{segment}
- 基于Query请求参数的断言工厂
‐Query=baz, ba.
- 基于路由权重的断言工厂
routes:
‐id: weight_route1
uri: host1
predicates:
‐Path=/product/**
‐Weight=group3, 1
‐id: weight_route2
uri: host2
predicates:
‐Path=/product/**
‐Weight= group3, 9
3.2自定义
package com.wang.predicates;
import org.springframework.cloud.gateway.handler.predicate.AbstractRoutePredicateFactory;
import org.springframework.cloud.gateway.handler.predicate.GatewayPredicate;
import org.springframework.stereotype.Component;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.server.ServerWebExchange;
import java.util.Arrays;
import java.util.List;
import java.util.function.Predicate;
/**
* @author 飞
*/
@Component
public class CheckAuthRoutePredicateFactory
extends AbstractRoutePredicateFactory<CheckAuthRoutePredicateFactory.Config> {
public CheckAuthRoutePredicateFactory() {
super(Config.class);
}
@Override
public List<String> shortcutFieldOrder() {
return Arrays.asList("name");
}
@Override
public Predicate<ServerWebExchange> apply(Config config) {
return new GatewayPredicate() {
@Override
public boolean test(ServerWebExchange exchange) {
if(config.getName().equals("xushu")){
return true;
}
return false;
}
};
}
// 用于接收配置文件中 断言的信息
@Validated
public static class Config {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
}
server:
port: 8083
spring:
application:
name: api-gateway
cloud:
#gateway配置
gateway:
#路由规则
routes:
- id: order_route #路由唯一标识,路由到order
uri: lb://order-nacos #需要转发的地址 lb:使用naocs本地负载均衡
#路由断言 规则 用于规则配置
predicates:
- Path=/order-nacos/**
# http://localhost:8083/order-nacosorder/add 路由到↓
# http://localhost:8022/order-nacos/order/add
# - After=2000-12-31T23:59:59.789+08:00[Asia/Shanghai]
# - Header=X-Request-Id,\d+
# - Method=GET
# - Query=name,xushu|zhuge
- CheckAuth=xushu #自定义断言工厂
filters:
- StripPrefix=1 #转发之前去掉第一层 #http://localhost:8022/order/add
# - CheckAuth=xushu #自定义过滤器
- AddRequestHeader=X-Request-color,red #添加请求头
# - PrefixPath=/demo # 添加前缀 对应微服务需要配置context-path
# - AddRequestParameter=name,xushu #添加参数 这里的顺序应该与body的顺序保持一
# - RedirectTo=302, https://www.baidu.com/ #重定向
# - SetStatus= 404
# 跨域配置
globalcors:
cors-configurations:
'[/**]': # 允许跨域访问的资源
allowedOrigins: "*" #跨域允许来源
allowedMethods:
- GET
- POST
nacos:
server-addr: localhost:8848
discovery:
username: nacos
password: nacos
namespace: public
# 配置sentinel
sentinel:
transport:
dashboard: 127.0.0.1:8080
# scg:
# fallback: #自定义异常返回,推荐
# mode: response
# response-body: "{code:'',message:''}"
4. 过滤器工厂( GatewayFilter Factories)配置
4.1Gateway 内置的过滤器工厂
Gateway 内置了很多的过滤器工厂,我们通过一些过滤器工厂可以进行一些业务逻辑处理器,比如添加剔除响应头,添加去除参数等
官方文档:https://docs.spring.io/spring-cloud-gateway/docs/current/reference/html/#gatewayfilter-factories
几个实例:
4.2 自定义过滤器工厂
package com.wang.filters;
import org.apache.commons.lang.StringUtils;
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory;
import org.springframework.cloud.gateway.support.GatewayToStringStyler;
import org.springframework.cloud.gateway.support.HttpStatusHolder;
import org.springframework.cloud.gateway.support.ServerWebExchangeUtils;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.util.Assert;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
import java.net.URI;
import java.util.Arrays;
import java.util.List;
/**
* @author 飞
*/
@Component
public class CheckAuthGatewayFilterFactory extends AbstractGatewayFilterFactory<CheckAuthGatewayFilterFactory.Config> {
public CheckAuthGatewayFilterFactory() {
super(Config.class);
}
public List<String> shortcutFieldOrder() {
return Arrays.asList("value");
}
public GatewayFilter apply(Config config) {
return new GatewayFilter() {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
//获取name参数
String name = exchange.getRequest().getQueryParams().getFirst("name");
if (StringUtils.isNotBlank(name)) {
if (config.getValue().equals(name)){
return chain.filter(exchange);
}else {
//如果不等于value就失败
//返回404
exchange.getResponse().setStatusCode(HttpStatus.NOT_FOUND);
return exchange.getResponse().setComplete();
}
}
//否则正常访问
return chain.filter(exchange);
}
};
}
public static class Config {
private String value;
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
}
}
4.3 全局过滤器(Global Filters)配置
- 局部:局部针对某个路由, 需要在路由中进行配置
- 全局:针对所有路由请求, 一旦定义就会投入使用
- LoadBalancerClientFilter
4.4 自定义全局过滤器
package com.wang.filters;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
/**
* 全局过滤器自定义,本例子为日志过滤器
*/
@Component
public class LogFilter implements GlobalFilter {
Logger log= LoggerFactory.getLogger(this.getClass());
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
log.info(exchange.getRequest().getPath().value());
return chain.filter(exchange);
}
}
4.5Reactor Netty 访问日志
<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>
4.6Gateway跨域配置(CORS Configuration)
- 通过yml配置的方式
globalcors:
cors-configurations:
'[/**]': # 允许跨域访问的资源
allowedOrigins: "*" #跨域允许来源
allowedMethods:
- GET
- POST
- 通过java配置的方式
package com.wang.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.reactive.CorsWebFilter;
import org.springframework.web.cors.reactive.UrlBasedCorsConfigurationSource;
import org.springframework.web.util.pattern.PathPatternParser;
/**
* @author 飞
* 跨域配置
*/
@Configuration
public class CorsConfig {
@Bean
public CorsWebFilter corsFilter() {
CorsConfiguration config = new CorsConfiguration();
config.addAllowedMethod("*"); // 允许的method
config.addAllowedOrigin("*"); // 允许的来源
config.addAllowedHeader("*"); // 允许的请求头参数
// 运行访问的资源
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(new PathPatternParser());
source.registerCorsConfiguration("/**", config);
return new CorsWebFilter(source);
}
}
5.gateway整合sentinel流控降级
<!--sentinel整合gateway 以前的版本adapter-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-sentinel-gateway</artifactId>
</dependency>
<!--sentinel的依赖-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
# 配置sentinel
sentinel:
transport:
dashboard: 127.0.0.1:8080
# scg:
# fallback: #自定义异常返回,推荐
# mode: response
# response-body: "{code:'',message:''}"
- route 维度:即在 Spring 配置文件中配置的路由条目,资源名为对应的 routeId
- 自定义 API 维度:用户可以利用 Sentinel 提供的 API 来自定义一些 API 分组
# 配置sentinel
sentinel:
transport:
dashboard: 127.0.0.1:8080
# scg:
# fallback: #自定义异常返回,推荐
# mode: response
# response-body: "{code:'',message:''}"
package com.wang.config;
import com.alibaba.csp.sentinel.adapter.gateway.sc.callback.BlockRequestHandler;
import com.alibaba.csp.sentinel.adapter.gateway.sc.callback.GatewayCallbackManager;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.web.reactive.function.BodyInserters;
import org.springframework.web.reactive.function.server.ServerResponse;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
import javax.annotation.PostConstruct;
import java.util.HashMap;
/**
* @author 飞
*/
@Configuration
public class GatewayConfig {
@PostConstruct
public void init(){
BlockRequestHandler blockRequestHandler = new BlockRequestHandler() {
@Override
public Mono<ServerResponse> handleRequest(ServerWebExchange exchange, Throwable t) {
System.out.println(t);
HashMap<String,String> map=new HashMap<>();
map.put("code",HttpStatus.TOO_MANY_REQUESTS.toString());
map.put("message","限流了");
// 自定义异常处理
return ServerResponse.status(HttpStatus.OK)
.contentType(MediaType.APPLICATION_JSON)
.body(BodyInserters.fromValue(map));
}
};
GatewayCallbackManager.setBlockHandler(blockRequestHandler);
}
}
6. 网关高可用
完整的yml:
server:
port: 8083
spring:
application:
name: api-gateway
cloud:
#gateway配置
gateway:
#路由规则
routes:
- id: order_route #路由唯一标识,路由到order
uri: lb://order-nacos #需要转发的地址 lb:使用naocs本地负载均衡
#路由断言 规则 用于规则配置
predicates:
- Path=/order-nacos/**
# http://localhost:8083/order-nacosorder/add 路由到↓
# http://localhost:8022/order-nacos/order/add
# - After=2000-12-31T23:59:59.789+08:00[Asia/Shanghai]
# - Header=X-Request-Id,\d+
# - Method=GET
# - Query=name,xushu|zhuge
- CheckAuth=xushu #自定义断言工厂
filters:
- StripPrefix=1 #转发之前去掉第一层 #http://localhost:8022/order/add
- CheckAuth=xushu #自定义过滤器
- AddRequestHeader=X-Request-color,red #添加请求头
# - PrefixPath=/demo # 添加前缀 对应微服务需要配置context-path
# - AddRequestParameter=name,xushu #添加参数 这里的顺序应该与body的顺序保持一
# - RedirectTo=302, https://www.baidu.com/ #重定向
# - SetStatus= 404
# 跨域配置
globalcors:
cors-configurations:
'[/**]': # 允许跨域访问的资源
allowedOrigins: "*" #跨域允许来源
allowedMethods:
- GET
- POST
nacos:
server-addr: localhost:8848
discovery:
username: nacos
password: nacos
namespace: public
# 配置sentinel
sentinel:
transport:
dashboard: 127.0.0.1:8080
# scg:
# fallback: #自定义异常返回,推荐
# mode: response
# response-body: "{code:'',message:''}"