SCA - Gateway入门实战

实战

环境

Nacos
Nacos Version:2.0.3

Sentinel
Sentinel Version:1.8.2

Demo Application:
System: macOS 10.14.6
JDK: 1.8.0_251
Spring Boot:2.2.13.RELEASE
Spring Cloud:Hoxton.SR12
Spring Cloud Alibaba: 2.2.5.RELEASE

核心概念

Route: 网关的基础组成部分。包含唯一标示id,路由目标uri,一系列断言过滤器集合。当所有断言集合为true时会匹配到该路由。
Predicate: 是Java 8 函数式断言。输入参数为ServerWebExchange,通过该参数匹配http请求参数。
Filter: 特定工厂类创建的一系列GatewayFilter实例。在发送到下游服务之前对请求或响应进行裁剪修改。

基础使用

配置路由

pom添加gateway依赖

<!-- spring cloud gateway -->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>

注意: Gateway其实是Spring Cloud下的生态组件。

properties添加路由配置

#spring.cloud.gateway.discovery.locator.enabled=true
## routes路由数组[路由 就是指定当请求满足什么条件的时候转到哪个微服务]
## 当前路由的标识, 要求唯一
spring.cloud.gateway.routes[0].id=good-service
## 请求要转发到的地址
spring.cloud.gateway.routes[0].uri=http://localhost:10012
## 路由的优先级,数字越小级别越高
spring.cloud.gateway.routes[0].order=1
## predicates: 断言(就是路由转发要满足的条件)
## 当请求路径满足Path指定的规则时,才进行路由转发
spring.cloud.gateway.routes[0].predicates[0]=Path=/good-apig/**
## 转发之前,删除第一层路径 即http://localhost:10016/good-apig/good/1 => http://localhost:10012/good/1
spring.cloud.gateway.routes[0].filters[0]=StripPrefix=1

以上代表请求http://localhost:10016/good-apig/good/1 时,若满足断言Path=/good-apig/**,网关会裁剪第一级路径/good-apig将请求代理到http://localhost:10012/good/1

结合Nacos

将路由配置放Nacos,结合Nacos服务发现及Ribbon的负责均衡进行服务调用。将配置从本地迁移到Nacos配置中心,可以动态配置路由信息。

Bootstrap添加Nacos配置

# nacos discovery
spring.cloud.nacos.discovery.server-addr=ip:port
# nacos config
spring.cloud.nacos.config.server-addr=ip:port

修改路由配置

## 结合nacos及ribbon负载均衡
spring.cloud.gateway.routes[0].uri=lb://good-service

自动配置微服务
让gateway可以发现nacos中的微服务,并做通用路由配置。

添加配置

spring.cloud.gateway.discovery.locator.enabled=true

默认访问路径

http://localhost:10016/good-service/good/1

结合Sentinel

Gateway整合Sentinel进行网关限流。限流维度有如下两种:

  • Route ID:网关中定义的路由id,以路由维度进行限流,即该路由下请求都进行流控。
  • API 分组:自定义Api分组,可以自定义多个匹配路径同属一个分组,对分组下的所有匹配路径都进行流控。

添加依赖

<!--sentinel integration gateway-->
<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 config
spring.cloud.sentinel.transport.dashboard=localhost:10000
spring.cloud.sentinel.web-context-unify=false

Route ID限流

Sentinel配置流控规则

在这里插入图片描述
Burst size:突发请求额外允许的请求数⽬。在快速失败模式下,当请求达到阈值,若该值为0立即失败。若该值为5,则达到阈值继续处理5个请求后才开始失败。

API 分组

定义分组
在这里插入图片描述
分组usergood下包含两个匹配路径/user-apig/**/good-apig/**

对分组usergood设置限流规则
在这里插入图片描述
验证规则是否生效
在这里插入图片描述

配置断言

Spring Cloud Gateway 将路由匹配为 Spring WebFlux HandlerMapping 基础结构的一部分。 Spring Cloud Gateway 包含许多内置的路由断言工厂。这些断言匹配 HTTP 请求的不同属性。可以按照逻辑自由组合。

内置断言

断言工厂功能说明配置
AfterRoutePredicateFactory接收一个时间参数,判断请求时间是否晚于指定时间(ZonedDateTime)- After=2017-01-20T17:42:47.789-07:00[America/Denver]
BeforeRoutePredicateFactory接收一个时间参数,判断请求时间是否早于指定时间(ZonedDateTime)- Before=2017-01-20T17:42:47.789-07:00[America/Denver]
BetweenRoutePredicateFactory接收两个时间参数,判断请求时间是否在指定时间段内(ZonedDateTime)- Between=2017-01-20T17:42:47.789-07:00[America/Denver], 2017-01-21T17:42:47.789-07:00[America/Denver]
CookieRoutePredicateFactory接收两个参数,判断请求cookie是否具有给定名称且值与正则表达式匹配- Cookie=chocolate, ch.p
HeaderRoutePredicateFactory接收两个参数,判断请求Header是否具有给定名称且值与正则表达式匹配- Header=X-Request-Id, \d+
HostRoutePredicateFactory接收一个参数,判断请求的Host是否满足匹配规则- Host=.somehost.org,.anotherhost.org
MethodRoutePredicateFactory接收一个参数,判断请求类型是否跟指定的类型匹配- Method=GET,POST
PathRoutePredicateFactory接收一个参数,判断请求的URI部分是否满足路径规则- Path=/red/{segment},/blue/{segment}
QueryRoutePredicateFactory接收两个参数,判断请求参数是否具有给定名称且值与正则表达式匹配- Query=color, gree.
RemoteAddrRoutePredicateFactory接收一个IP地址段,判断请求主机地址是否在地址段中- RemoteAddr=192.168.1.1/24
WeightRoutePredicateFactory接受两个参数分组和权重,对于同一个组内的路由按照权重转发- Weight=group1, 8
- Weight=group1, 2

自定义断言

必须按照xxxRoutePredicateFactory格式命名断言工厂。

自定义评分断言工厂类ScoreRoutePredicateFactory

@Component
public class ScoreRoutePredicateFactory extends AbstractRoutePredicateFactory<ScoreRoutePredicateFactory.Config> {

    public ScoreRoutePredicateFactory() {
        super(Config.class);
    }

    @Override
    public List<String> shortcutFieldOrder() {
        return Arrays.asList("minScore", "maxScore");
    }

    @Override
    public Predicate<ServerWebExchange> apply(Config config) {
        return exchange -> {
            // TODO: 获取请求中的 score 参数,并判断是否满足 minScore<score<=maxScore
            return false;
        };
    }

    @Data
    public static class Config {
        private Integer minScore;
        private Integer maxScore;
    }
}

配置过滤器

网关过滤器

内置网关过滤器
过滤器工厂作用参数
AddRequestHeader为原始请求添加HeaderHeader的名称及值
AddRequestParameter为原始请求添加请求参数参数名称及值
AddResponseHeader为原始响应添加HeaderHeader的名称及值
DedupeResponseHeader剔除响应头中重复的值需要去重的Header名称及去重策略
Hystrix为路由引入Hystrix的断路器保护HystrixCommand 的名称
FallbackHeaders为fallbackUri的请求头中添加具体的异常信息Header的名称
PrefixPath为原始请求路径添加前缀前缀路径
PreserveHostHeader为请求添加一个preserveHostHeader=true的属性,路由过滤器会检查该属性以决定是否要发送原始的Host
RequestRateLimiter用于对请求限流,限流算法为令牌桶keyResolver、rateLimiter、statusCode、denyEmptyKey、emptyKeyStatus
RedirectTo将原始请求重定向到指定的URLhttp状态码及重定向的url
RemoveHopByHopHeadersFilter为原始请求删除IETF组织规定的一系列Header默认就会启用,可以通过配置指定仅删除哪些Header
RemoveRequestHeader为原始请求删除某个HeaderHeader名称RemoveResponseHeader
RewritePath重写原始的请求路径原始路径正则表达式以及重写后路径的正则表达式RewriteResponseHeader
SaveSession在转发请求之前,强制执行WebSession::save操作
SecureHeaders为原始响应添加一系列起安全作用的响应头无,支持修改这些安全响应头的值
SetPath修改原始的请求路径修改后的路径
SetResponseHeader修改原始响应中某个Header的值Header名称,修改后的值
SetStatus修改原始响应的状态码HTTP 状态码,可以是数字,也可以是字符串
StripPrefix用于截断原始请求的路径使用数字表示要截断的路径的数量
Retry针对不同的响应进行重试retries、statuses、methods、series
RequestSize设置允许接收最大请求包的大小。如果请求包大小超过设置的值,则返回 413 Payload Too Large请求包大小,单位为字节,默认值为5M
ModifyRequestBody在转发请求之前修改原始请求体内容修改后的请求体内容ModifyResponseBody
自定义网关过滤器

必须以xxxGatewayFilterFactory格式命名。

@Slf4j
@Component
public class CustomTimeGatewayFilterFactory extends AbstractGatewayFilterFactory<CustomTimeGatewayFilterFactory.Config>{
    public CustomTimeGatewayFilterFactory() {
        super(Config.class);
    }

    @Override
    public List<String> shortcutFieldOrder() {
        return Arrays.asList("enabled");
    }

    @Override
    public GatewayFilter apply(Config config) {
        return (exchange, chain) -> {
            if (!config.enabled) {
                return chain.filter(exchange);
            }
            // pre阶段开启统计
            AtomicReference<StopWatch> sw = new AtomicReference<>(new StopWatch(exchange.getRequest().getURI().toString()));
            sw.get().start();
            return chain.filter(exchange).then(Mono.fromRunnable(()->{
                // post阶段结束统计
                sw.get().stop();
                log.info("CustomTimeGatewayFilterFactory - 执行完毕, \n请求uri[{}], \n请求耗时统计[{}ms]",exchange.getRequest().getURI(), sw.get().getTotalTimeMillis());
            }));
        };
    }

    @Getter
    @Setter
    public static class Config {
        // 默认开启耗时统计
        private boolean enabled = true;

    }
}

全局过滤器

内置全局过滤器
过滤器作用
ReactiveLoadBalancerClientFilter将lb形式url解析为真实的ip:port
ForwardRoutingFilter根据 forward:// 前缀( Scheme )过滤处理,将请求转发到当前网关实例本地接口
NettyRoutingFilter根据 http:// 或 https:// 前缀( Scheme )过滤处理,使用基于 Netty 实现的 HttpClient 请求后端 Http 服务
NettyWriteResponseFilter如果后端返回的是Netty HttpClientResponse,将响应代理返回给客户端
RouteToRequestUrlFilter根据匹配的 Route ,计算请求的地址。注意,这里的地址指的是 URL ,而不是 URI
WebsocketRoutingFilter根据 ws:// / wss:// 前缀( Scheme )过滤处理,代理后端 Websocket 服务,提供给客户端连接
GatewayMetricsFilter开启网关指标,添加一个名为 gateway.requests 的计时器指标。可整合Prometheus
自定义全局过滤器

自定义全局鉴权过滤器:CumtomAuthGlobalFilter

@Slf4j
@Component
public class CumtomAuthGlobalFilter implements GlobalFilter, Ordered {
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        String accessToken = exchange.getRequest() == null || exchange.getRequest().getHeaders().get(HttpHeaders.AUTHORIZATION) == null ? StringUtils.EMPTY : exchange.getRequest().getHeaders().get(HttpHeaders.AUTHORIZATION).get(0);
        if (!check(accessToken)) {
            log.error("请求路径:[{}],token:[{}] 鉴权失败", exchange.getRequest().getURI(), accessToken);
            exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
            return exchange.getResponse().setComplete();
        }

        return chain.filter(exchange);
    }

    private boolean check(String token) {
        if (StringUtils.isBlank(token)) {
            return false;
        }
        // 调用鉴权服务鉴权
        return true;
    }

    @Override
    public int getOrder() {
        return 0;
    }
}

HttpHeaders过滤器

过滤器作用
ForwardedHeadersFilter在请求发往下游前,创建一个Forwarded header。如果已经存在Forwarded header,则把当前请求的host,port,scheme添加到Forwarded header中
RemoveHopByHopHeadersFilter默认删除转发的请求中header:Connection、Keep-Alive、Proxy-Authenticate、Proxy-Authorization、TE、Trailer、Transfer-Encoding、Upgrade。可通过修改属性spring.cloud.gateway.filter.remove-non-proxy-headers.headers 指定要删除的header
XForwardedHeadersFilter在发向下游前可以创建一系列的X-Forwarded-* headers。它会使用当前请求的Host header,scheme,port和path去创建对应的headers

其他配置

自定义异常返回

配置类GatewayConfiguration添加方法:

@PostConstruct
public void initBlockHandlers() {
    BlockRequestHandler blockRequestHandler = new BlockRequestHandler() {
        private final String FLOW_ERROR_MESSAGE ="触发限流";
        private final Integer FLOW_ERROR_CODE =106;
        @Override
        public Mono<ServerResponse> handleRequest(ServerWebExchange exchange, Throwable throwable) {

            Map map = new HashMap<>();
            map.put("code", FLOW_ERROR_CODE);
            map.put("msg", FLOW_ERROR_MESSAGE);
            log.error("请求uri: [{}], 异常编码: [{}],异常描述: [{}]", exchange.getRequest().getURI(), FLOW_ERROR_CODE, FLOW_ERROR_MESSAGE);
            return ServerResponse.status(HttpStatus.OK).
                    contentType(MediaType.APPLICATION_JSON).
                    body(BodyInserters.fromValue(map));
        }
    };
    GatewayCallbackManager.setBlockHandler(blockRequestHandler);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值