前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >SpringCloud详细教程 | 第六篇:Gateway之路由器和过滤器、熔断、降级、限流(Greenwich版本)

SpringCloud详细教程 | 第六篇:Gateway之路由器和过滤器、熔断、降级、限流(Greenwich版本)

作者头像
小东啊
发布2019-06-26 15:23:08
5.9K0
发布2019-06-26 15:23:08
举报
文章被收录于专栏:李浩东的博客李浩东的博客

Spring Cloud Gateway 作为 Spring Cloud 生态系统中的网关,目标是替代 Netflix Zuul,其不仅提供统一的路由方式,并且基于 Filter 链的方式提供了网关基本的功能,例如:安全,监控/指标,和限流

一. 简介

1.概念理解

Spring Cloud Gateway 是 Spring Cloud 的一个全新项目,该项目是基于 Spring 5.0,Spring Boot 2.0 和 Project Reactor 等技术开发的网关,它旨在为微服务架构提供一种简单有效的统一的 API 路由管理方式。 Spring Cloud Gateway 作为 Spring Cloud 生态系统中的网关,目标是替代 Netflix Zuul,其不仅提供统一的路由方式,并且基于 Filter 链的方式提供了网关基本的功能,例如:安全,监控/指标,和限流。

2.Spring Cloud Gateway功能:
  • 基于Spring Framework 5,Project Reactor和Spring Boot 2.0构建
  • 能够匹配任何请求属性上的路由。
  • 谓词和过滤器特定于路线。
  • Hystrix断路器集成。
  • Spring Cloud DiscoveryClient集成
  • 易于编写谓词和过滤器
  • 请求率限制
  • 路径重写
3.Gateway相关概念
  • 路由:路由网关的基本构建块。它由ID,目标URI,谓词集合和过滤器集合定义。如果聚合谓词为真,则匹配路由。
  • 谓词:这是一个Java 8函数谓词。输入类型是Spring FrameworkServerWebExchange。这允许开发人员匹配来自HTTP请求的任何内容,例如标头或参数。
  • 过滤器:这些是使用特定工厂构建的Spring FrameworkGatewayFilter实例。这里,可以在发送下游请求之前或之后修改请求和响应。
4.工作流程

Spring Cloud Gateway Diagram

客户端向Spring Cloud Gateway发出请求。如果网关处理程序映射确定请求与路由匹配,则将其发送到网关Web处理程序。此处理程序运行通过特定于请求的过滤器链发送请求。滤波器被虚线划分的原因是滤波器可以在发送代理请求之前或之后执行逻辑。执行所有“预”过滤器逻辑,然后进行代理请求。在发出代理请求之后,执行“post”过滤器逻辑。

二. Greenwich实战

1. 新建gateway-server服务

引入gateway依赖

代码语言:javascript
复制
         <dependency>            <groupId>org.springframework.cloud</groupId>            <artifactId>spring-cloud-starter-gateway</artifactId>        </dependency>

修改后的pom文件

代码语言:javascript
复制
<?xml version="1.0" encoding="UTF-8"?><project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">    <modelVersion>4.0.0</modelVersion>    <parent>        <groupId>com.li</groupId>        <artifactId>SpringCloudLearn</artifactId>        <version>1.0-SNAPSHOT</version>        <relativePath>../</relativePath>    </parent>    <artifactId>gateway-server</artifactId>    <version>0.0.1-SNAPSHOT</version>    <name>gateway-server</name>    <description>Demo project for Spring Boot</description>
    <dependencies>        <!--gateway 网关-->        <dependency>            <groupId>org.springframework.cloud</groupId>            <artifactId>spring-cloud-starter-gateway</artifactId>        </dependency>        <dependency>            <groupId>org.springframework.cloud</groupId>            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>        </dependency>    </dependencies>
    <dependencyManagement>        <dependencies>            <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>
    <build>        <plugins>            <plugin>                <groupId>org.springframework.boot</groupId>                <artifactId>spring-boot-maven-plugin</artifactId>            </plugin>        </plugins>    </build>
</project>

启动类加入注解@EnableEurekaClient向服务中心注册

代码语言:javascript
复制
package com.li.gatewayserver;
import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;import org.springframework.cloud.gateway.route.RouteLocator;import org.springframework.cloud.gateway.route.builder.RouteLocatorBuilder;import org.springframework.cloud.netflix.eureka.EnableEurekaClient;import org.springframework.context.annotation.Bean;import org.springframework.http.HttpHeaders;import org.springframework.http.HttpMethod;import org.springframework.http.HttpStatus;import org.springframework.http.server.reactive.ServerHttpRequest;import org.springframework.http.server.reactive.ServerHttpResponse;import org.springframework.web.cors.reactive.CorsUtils;import org.springframework.web.server.ServerWebExchange;import org.springframework.web.server.WebFilter;import org.springframework.web.server.WebFilterChain;import reactor.core.publisher.Mono;
@EnableEurekaClient@SpringBootApplicationpublic class GatewayServerApplication {
    public static void main(String[] args) {        SpringApplication.run(GatewayServerApplication.class, args);    }}

application.yml配置文件

代码语言:javascript
复制
server:  port: 8768eureka:  port: 8761spring:  application:    name: gateway-server    eureka:      client:        service-url:          defaultZone: http://${eureka.instance.hostname}:${eureka.port}/eureka/  redis:    host: 127.0.0.1    port: 6379  cloud:    gateway:      routes:        # 请求路径匹配        - id: path_route          uri: http://www.lhdyx.cn          predicates:            - Path=/lhd/** # 请求地址携带lhd的,则转发           # 加上StripPrefix=1,否则转发到后端服务时会带上前缀          filters:            - StripPrefix=1

配置说明:

  • id: 自定义的路由 ID,保持唯一
  • uri: 目标服务地址
  • predicates:路由条件,Predicate 接受一个输入参数,返回一个布尔值结果。该接口包含多种默认方法来将 Predicate 组合成其他复杂的逻辑(比如:与,或,非)。
  • filters:过滤规则 StripPrefix=1 作用于请求转发不带前缀

配置了一个 id 为 path_route的路由规则,当访问地址 http://localhost:8768/lhd 时会自动转发到地址:http://www.lhdyx.cn 启动服务发现报错

代码语言:javascript
复制
Description:
Parameter 0 of method modifyRequestBodyGatewayFilterFactory in org.springframework.cloud.gateway.config.GatewayAutoConfiguration required a bean of type 'org.springframework.http.codec.ServerCodecConfigurer' that could not be found.

Action:
Consider defining a bean of type 'org.springframework.http.codec.ServerCodecConfigurer' in your configuration

经查阅资料

代码语言:javascript
复制
<dependency>    <groupId>org.springframework.boot</groupId>    <artifactId>spring-boot-starter-web</artifactId></dependency>

代码语言:javascript
复制
<dependency>     <groupId>org.springframework.cloud</groupId>     <artifactId>spring-cloud-starter-gateway</artifactId> </dependency>
<dependency>    <groupId>org.springframework.boot</groupId>    <artifactId>spring-boot-starter-webflux</artifactId></dependency>

有冲突 为了快速启动 我先将父pom文件的web依赖去掉 后面用上再加上 继续启动程序 打开浏览器访问 http://localhost:8768/lhd 页面展示为我的博客地址 并且没有带上/lhd前缀

2.自定义代码配置路由转发 代码如下
代码语言:javascript
复制
/**     * 代码实现路由跳转     * 配置了一个id为p_route的路由 当访问地址http://localhost:8768/lhdblog时会     * 自动转发到地址:http://www.lhdyx.cn 和上面的转发效果一样     *     * @param builder     * @return     */    @Bean    public RouteLocator customRouteLocator(RouteLocatorBuilder builder) {        return builder.routes()                .route("p_route", r -> r.path("/lhdblog")                        .uri("http://www.lhdyx.cn"))                .build();    }

配置了一个 id 为 proute 的路由,当访问地址http://localhost:8768/lhdblog时会自动转发到地址:http://www.lhdyx.cn/lhdblog和上面的转发效果一样

在实际项目使用中可以将 uri 指向对外提供服务的项目地址,统一对外输出接口, 上面两个示例中 uri 都是指向了我的个人网站,在实际项目使用中可以将 uri 指向对外提供服务的项目地址

这里简单介绍了以路径规则的路由转发

3.路由规则

Spring Cloud Gateway 的功能很强大,我们仅仅通过 Predicates 的设计就可以看出来,前面我们只是使用了 predicates 进行了简单的条件匹配,其实 Spring Cloud Gataway 帮我们内置了很多 Predicates 功能。

Spring Cloud Gateway将路由作为Spring WebFlux HandlerMapping基础结构的一部分进行匹配。Spring Cloud Gateway包含许多内置的Route Predicate工厂。所有这些谓词都匹配HTTP请求的不同属性, 这些 Predicates 工厂通过不同的 HTTP 请求参数来匹配,多个 Predicates 工厂可以组合使用

Predicate 来源于 Java 8,是 Java 8 中引入的一个函数,Predicate接受一个输入参数,返回一个布尔值结果。该接口包含多种默认方法来将Predicate组合成其他复杂的逻辑(比如:与,或,非)。可以用于接口请求参数校验、判断新老数据是否有变化需要进行更新操作。add--与、or--或、negate--非

在 Spring Cloud Gateway 中 Spring 利用 Predicate 的特性实现了各种路由匹配规则,有通过 Header、请求参数等不同的条件来进行作为条件匹配到对应的路由。网上有一张图总结了 Spring Cloud 内置的几种 Predicate 的实现。

以下是Spring Cloud GateWay 内置几种 Predicate 的使用

代码语言:javascript
复制
          # 请求路径匹配        - id: path_route          uri: http://www.lhdyx.cn          predicates:            - Path=/lhd/** # 请求地址携带lhd的,则转发           # 加上StripPrefix=1,否则转发到后端服务时会带上前缀          filters:            - StripPrefix=1          # 请求参数匹配        - id: query_route          uri: http://www.lhdyx.cn          predicates:            - Query=lhd # 请求参数含有lhd          filters:            - StripPrefix=1          # Cookie 匹配        - id: cookie_route          uri: http://www.lhdyx.cn          predicates:            - Cookie=www.lhdyx.cn, lhd # 如果携带cookie,参数名为www.lhdyx.cn,值为lhd,则转发          filters:            - StripPrefix=1          # 时间匹配        - id: after_route          uri: http://www.lhdyx.cn          predicates:            - After=2019-04-02T06:06:06+08:00[Asia/Shanghai] # 此路线与2019年4月2日之后的所有要求相匹配          filters:            - StripPrefix=1          # Header 属性匹配        - id: header_route          uri: http://www.lhdyx.cn          predicates:            - Header=request, \d+ # 如果请求头含有request,且为数字,则转发          filters:            - StripPrefix=1          # 请求方法匹配 post get等        - id: method_route          uri: http://www.lhdyx.cn          predicates:            - Method=/get # 请求方法过滤          filters:            - StripPrefix=1          # ip匹配        - id: ip_route          uri: http://www.lhdyx.cn          predicates:            - RemoteAddr=192.168.1.101 # ip地址          filters:            - StripPrefix=1          # Host 匹配        - id: host_route          uri: http://www.lhdyx.cn          predicates:            - Host=**.lhdyx.cn          filters:            - StripPrefix=1

这里不多介绍了 大家可以尝试下

官方文档 https://cloud.spring.io/spring-cloud-gateway/spring-cloud-gateway.html

4.Spring Cloud Gateway如何配合服务注册中心进行路由转发

配置文件

  • spring.cloud.gateway.discovery.locator.enabled为true, 表明gateway开启服务注册和发现的功能,并且spring cloud gateway自动根据服务发现为每一个服务创建了一个router,这个router将以服务名开头的请求路径转发到对应的服务
  • spring.cloud.gateway.discovery.locator.lower-case-service-id是将请求路径上的服务名配置为小写,大家可能会发现服务注册的时候,向注册中心注册时将服务名转成大写

配置了一个Path 的predict,将以/client/**开头的请求都会转发到uri为lb://eureka-client的地址上,lb://eureka-client即eureka-client服务的负载均衡地址

配置完成后 启动之前的eureka-client服务 并查看注册情况

打开浏览器访问: http://localhost:8768/client/hello?name=lhd 浏览器返回以下的响应

说明已经成功转发到服务上

三. 实现过滤器

只要实现GlobalFilter, Ordered接口即可加 GlobalFilter 全局过滤器,打印每次请求的url

代码语言:javascript
复制
package com.li.gatewayserver.config;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;import org.springframework.cloud.gateway.filter.GlobalFilter;import org.springframework.core.Ordered;import org.springframework.http.server.reactive.ServerHttpRequest;import org.springframework.stereotype.Component;import org.springframework.web.server.ServerWebExchange;import reactor.core.publisher.Mono;
/** * @Classname RequestFilter * @Description 全局过滤器 * @Author 李号东 lihaodongmail@163.com * @Date 2019-04-02 22:50 * @Version 1.0 */@Componentpublic class MyFilter implements GlobalFilter, Ordered {
    //执行逻辑    @Override    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {        ServerHttpRequest request = exchange.getRequest();        String uri = request.getURI().toString();        //打印每次请求的url        System.out.println(" uri : " + uri);        return chain.filter(exchange);    }
    //执行顺序    @Override    public int getOrder() {        return 1;    }}

重启gateway-server服务 打开浏览器访问: http://localhost:8768/client/hello?name=lhd

控制台打印出url 说明过滤器生效

四. 熔断器

集成 Hystrix 熔断降级,引用hystrix依赖,在filters下加入熔断降级配置,设置降级后返回的路由 引入依赖

代码语言:javascript
复制
        <!-- 断路器-->        <dependency>            <groupId>org.springframework.cloud</groupId>            <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>        </dependency>

application.yml配置

新建一个HystrixController 的RestController,内容如下:

代码语言:javascript
复制
package com.li.gatewayserver.controller;
import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RestController;
import java.util.HashMap;import java.util.Map;
/** * @Classname HystrixController * @Description 熔断调用地址 * @Author 李号东 lihaodongmail@163.com * @Date 2019-04-02 23:05 * @Version 1.0 */@RestControllerpublic class HystrixController {
    @RequestMapping("/defaultfallback")    public Map<String,String> defaultfallback(){        System.out.println("降级操作...");        Map<String,String> map = new HashMap<>();        map.put("resultCode","fail");        map.put("resultMessage","服务异常");        map.put("resultObj","null");        return map;    }}

关闭eureka-client服务 重启gateway-server服务, 然后浏览器访问: http://localhost:8768/client/hello?name=lhd 响应如下:

说明熔断器成功

五. 集成限流

Spring Cloud Gateway默认集成了Redis限流,可以对不同服务做不同维度的限流,如:IP限流、用户限流 、接口限流 本文演示的是 IP限流 ,先添加redis依赖,添加KeyResolver,再添加配置,需启动redis

引入依赖

代码语言:javascript
复制
        <!-- 限流 -->        <dependency>            <groupId>org.springframework.boot</groupId>            <artifactId>spring-boot-starter-data-redis-reactive</artifactId>        </dependency>

通过KeyResolver来指定限流的Key,新建一个 RateLimiterConfig 类,IP限流代码如下:

代码语言:javascript
复制
package com.li.gatewayserver.config;
import org.springframework.cloud.gateway.filter.ratelimit.KeyResolver;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import reactor.core.publisher.Mono;
/** * @Classname RateLimiterConfig * @Description 限流方法 * @Author 李号东 lihaodongmail@163.com * @Date 2019-04-02 23:09 * @Version 1.0 */@Configurationpublic class RateLimiterConfig {    @Bean(value = "remoteAddrKeyResolver")    public KeyResolver remoteAddrKeyResolver() {        return exchange -> Mono.just(exchange.getRequest().getRemoteAddress().getAddress().getHostAddress());    }}

application.yml下添加限流配置:

配置说明:

  • filter名称必须是 RequestRateLimiter
  • redis-rate-limiter.replenishRate:允许用户每秒处理多少个请求
  • redis-rate-limiter.burstCapacity:令牌桶的容量,允许在一秒钟内完成的最大请求数
  • key-resolver:使用SpEL按名称引用bean

配置完成后 启动redis 和 eureka-client服务 重启gateway-server服务

测试网关是否做到了限流,使用 jmeter 测试工具 ,jmeter 阿帕奇的压测工具 自行百度 这里不做介绍

测试配置如下,100个线程,2秒内,循环2次,总共200个请求,请求地址:http://localhost:8768/client/hello?name=lhd

启动测试工具 结果如下

还可以使用用户限流、接口限流 用户限流,使用这种方式限流,请求路径中必须携带userId参数

代码语言:javascript
复制
@BeanKeyResolver userKeyResolver() {    return exchange -> Mono.just(exchange.getRequest().getQueryParams().getFirst("userId"));}

接口限流,获取请求地址的uri作为限流key

代码语言:javascript
复制
@BeanKeyResolver apiKeyResolver() {    return exchange -> Mono.just(exchange.getRequest().getPath().value());}

在真实场景中,限流数的调整需要依赖配置中心,当网站做活动时,动态调整限流数,新服务上线时,通过配置中心做动态路由等

后面有时间继续介绍

六. 跨域设置

有时候前端需要支持跨域访问, 这里简单配置允许所有域名访问.

代码语言:javascript
复制
/**     * 跨域配置     * @return     */    @Bean    public WebFilter corsFilter() {        return (ServerWebExchange ctx, WebFilterChain chain) -> {            ServerHttpRequest request = ctx.getRequest();            if (!CorsUtils.isCorsRequest(request)) {                return chain.filter(ctx);            }
            HttpHeaders requestHeaders = request.getHeaders();            ServerHttpResponse response = ctx.getResponse();            HttpMethod requestMethod = requestHeaders.getAccessControlRequestMethod();            HttpHeaders headers = response.getHeaders();            headers.add(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN, requestHeaders.getOrigin());            headers.addAll(HttpHeaders.ACCESS_CONTROL_ALLOW_HEADERS, requestHeaders.getAccessControlRequestHeaders());            if (requestMethod != null) {                headers.add(HttpHeaders.ACCESS_CONTROL_ALLOW_METHODS, requestMethod.name());            }            headers.add(HttpHeaders.ACCESS_CONTROL_ALLOW_CREDENTIALS, "true");            headers.add(HttpHeaders.ACCESS_CONTROL_EXPOSE_HEADERS, "all");            headers.add(HttpHeaders.ACCESS_CONTROL_MAX_AGE, "3600");            if (request.getMethod() == HttpMethod.OPTIONS) {                response.setStatusCode(HttpStatus.OK);                return Mono.empty();            }            return chain.filter(ctx);        };    }

注入bean即可

Gateway介绍就到这里大家去试试吧 功能比较强大 源码下载: https://github.com/LiHaodong888/SpringCloudLearn

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2019-04-03,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 李浩东的博客 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一. 简介
    • 1.概念理解
      • 2.Spring Cloud Gateway功能:
        • 3.Gateway相关概念
          • 4.工作流程
          • 二. Greenwich实战
            • 1. 新建gateway-server服务
              • 2.自定义代码配置路由转发 代码如下
                • 3.路由规则
                  • 4.Spring Cloud Gateway如何配合服务注册中心进行路由转发
                  • 三. 实现过滤器
                  • 四. 熔断器
                  • 五. 集成限流
                  • 六. 跨域设置
                  相关产品与服务
                  微服务引擎 TSE
                  微服务引擎(Tencent Cloud Service Engine)提供开箱即用的云上全场景微服务解决方案。支持开源增强的云原生注册配置中心(Zookeeper、Nacos 和 Apollo),北极星网格(腾讯自研并开源的 PolarisMesh)、云原生 API 网关(Kong)以及微服务应用托管的弹性微服务平台。微服务引擎完全兼容开源版本的使用方式,在功能、可用性和可运维性等多个方面进行增强。
                  领券
                  问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档