zoukankan      html  css  js  c++  java
  • SpringCloud-Greenwich版本新特性探索(1)---SpringCloudGateway

    一、前言

    1、SpringCloudGateway是SpringCloud新推出的网关框架,比较于上一代Zuul,功能和性能有很大的提升。Zuul1.x采用的是阻塞多线程方式,也就是一个线程处理一个连接请求,高并发情况下性能较差,即使是Zuul2.x虽然做到了非阻塞,但是面对连续跳票,看起来Zuul要被抛弃了。取而代之的是SpringCloudGateway,SpringCloudGateway是基于Webflux,是一个非阻塞异步的框架,性能上有很大提升,而且包含了Zuul的所有功能,可以从Zuul无缝切换到SpringCloudGateway

    2、SpringCloud环境版本:Greenwich.RELEASE

    3、SpringBoot环境版本:2.1.3.RELEASE

    二、环境搭建

    1、在父工程下新建一个网关模块

    2、引入SpringCloudGateway需要的POM,记得引入actuator组件,否则服务发现中心会认为服务不在线,导致网关无法路由到服务,并且加入熔断组件Hystrix

    <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>
    <!-- 健康检查 -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-actuator</artifactId>
    </dependency>
    <!-- Hystrix -->
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
    </dependency>

    3、主函数很简单,加入必要的注解,比如服务发现等

    @SpringBootApplication
    @EnableDiscoveryClient
    public class GatewayCarfacApplication
    {
        public static void main(String[] args)
        {
            SpringApplication.run(GatewayCarfacApplication.class, args);
        }
    }

    4、进行SpringCloudGateway的配置,配置可以使用Java代码进行配置或者yml配置,这里使用yml进行配置

    spring:
      cloud:
        gateway:
          discovery:
            locator:
              enabled: true # 设置可以路由到其他服务
          routes: # 可以配置多个路由
            - id: service-media-1 # 路由id唯一
              uri: lb://service-media # 可以直接跳转到具体的地址,如果要跳转到其他服务,则填写lb://<服务id>
              predicates:
                - Path=/media/** # 路由规则
              filters:
                - StripPrefix=1 # 不填则无法路由到其他服务
                - AddRequestHeader=X-Request-Foo, Bar
                - name: Hystrix # 添加熔断
                  args:
                    name: fallbackcmd
                    fallbackUri: forward:/test/fallback # 熔断跳转地址
    
    # 熔断超时时间
    hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds: 3000

    5、上面我们配置了熔断的配置,一旦发生熔断就会跳转到/test/fallback这个地址,下面我们实现一下这个接口,这里简单的返回了error,我们可以自定义处理熔断的逻辑

    @Controller
    @RequestMapping(value = "/test")
    public class TestController
    {
        @RequestMapping(value = "/fallback", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE)
        @ResponseBody
        public ResponseEntity<String> fallback()
        {
            return new ResponseEntity<>("error.", HttpStatus.OK);
        }
    }

    6、网关配置好后,再启动一个其他的服务模块,记得添加引入actuator,否则无法被路由到

    <!-- Web -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <!-- 健康检查 -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-actuator</artifactId>
    </dependency>

    7、编写一个HTTP接口,来测试网关的调用,为了检测熔断的效果,这里通过参数来控制接口的响应时间

    @Controller
    @RequestMapping(value = "/test")
    public class TestController
    {
        @Value("${server.port}")
        private String port;
    
        @RequestMapping(value = "/get/{time}", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE)
        @ResponseBody
        public ResponseEntity<String> get(@PathVariable("time") long time)
        {
            try
            {
                Thread.sleep(time);
            }
            catch (InterruptedException e)
            {
                e.printStackTrace();
            }
            return new ResponseEntity<>(port + " get ok.", HttpStatus.OK);
        }
    }

    8、在同一注册中心下启动网关和服务,根据我们配置的路由规则/media/**,我们可以这样调用http://127.0.0.1:8501/media/get/100,可以看到正确的路由到了服务

    9、我们将响应时间改成5秒http://127.0.0.1:8501/media/get/5000,超高熔断的检测时间,可以发现接口熔断,并且跳转到了指定的链接

    三、高级特性---断言

    1、以上我们实现了SpringCloudGateway的基本使用办法,可以应付大部分应用场景了,我们同时可以细粒度的去改造路由,就使用到了断言Predict,如果不满足我们设置的断言条件,则无法被路由

    2、设置断言,我们只需要在predicates下面加入配置即可,要注意如果设置了多个断言,则请求必须满足所有断言才可以被正确路由到

    spring:
      cloud:
        gateway:
          routes: # 可以配置多个路由
            - id: service-media-1 # 路由id唯一
              uri: lb://service-media # 可以直接跳转到具体的地址,如果要跳转到其他服务,则填写lb://<服务id>
              predicates:
                # 加入断言

    3、常用的一些断言,还有很多其他断言就不一一列举了,当然最常用的可能是Path断言,我们需要通过路径来指定路由

    (1)- Header=<key>, <value>(必须有指定的HTTP Header才能路由)

    (2)- Cookie=<key>, <value>(必须有指定的Cookie才能路由)

    (3)- Host=aaa.bbb.com(请求域名必须是aaa.bbb.com才能路由)

    (4)- Method=GET(请求方式必须是Get请求才能路由)

    四、高级特性---过滤器

    1、通过设置网关的过滤器,我们可以在用户访问的入口增加一些处理,比如鉴权、接口监控等,下面介绍一些常用的过滤器

    2、常用的过滤器

    (1)- AddRequestHeader=<key>, <value>(增加自定义HTTP请求头)

    (2)- SetStatus=401(设置响应的HTTP错误码)

    (3)- RedirectTo=302, http://acme.org(重定向到指定链接)

    3、自定义过滤器,可以通过代码定制更加灵活的过滤器,下面实现一个简单的接口耗时统计的过滤器,思路:设置两个过滤器,一个前置过滤器用来收集接口开始调用的时间,一个后置过滤器来将结束时间减去开始时间得到接口的耗时时间,并打印出来

    (1)前置过滤器工厂,将开始时间写到attributes里面

    public class PreGatewayFilterFactory extends AbstractGatewayFilterFactory<PreGatewayFilterFactory.Config> {
    
        public PreGatewayFilterFactory() {
            super(Config.class);
        }
    
        @Override
        public GatewayFilter apply(Config config) {
            return (exchange, chain) -> {
                exchange.getAttributes().put("requestTime", System.currentTimeMillis());
                return chain.filter(exchange);
            };
        }
    
        public static class Config {
    
        }
    
    }

    (2)后置过滤器工厂,获取attribute里面的开始时间,并用当前时间减去,得到耗时时间,打印

    public class PostGatewayFilterFactory extends AbstractGatewayFilterFactory<PostGatewayFilterFactory.Config>
    {
        public PostGatewayFilterFactory() {
            super(Config.class);
        }
    
        @Override
        public GatewayFilter apply(Config config) {
            return (exchange, chain) -> {
                return chain.filter(exchange).then(Mono.fromRunnable(() -> {
                    Long startTime = exchange.getAttribute("requestTime");
                    long time = System.currentTimeMillis() - startTime;
                    System.out.println("接口耗时时间(ms):"+time);
                }));
            };
        }
    
        public static class Config {
            
        }
    
    }

    (3)接下来将两个过滤器注入到Spring里面

    @Configuration
    public class FilterConfig
    {
        @Bean
        public PreGatewayFilterFactory preGatewayFilterFactory() {
            return new PreGatewayFilterFactory();
        }
    
        @Bean
        public PostGatewayFilterFactory postGatewayFilterFactory() {
            return new PostGatewayFilterFactory();
        }
    }

    (4)前往application.yml进行过滤器的配置,在filter属性下配置上我们的自定义过滤器,根据框架的约定引用过滤器:比如我们的过滤器名称是PreGatewayFilterFactory,那我们引用的名称就是去掉GatewayFilterFactory这个后缀,也就是Pre,具体配置如下

    spring:
      cloud:
        gateway:
          discovery:
            locator:
              enabled: true # 设置可以路由到其他服务
          routes: # 可以配置多个路由
            - id: service-media-1 # 路由id唯一
              uri: lb://service-media # 可以直接跳转到具体的地址,如果要跳转到其他服务,则填写lb://<服务id>
              predicates:
                - Path=/media/** # 路由规则
              filters:
                - StripPrefix=1 # 不填则无法路由到其他服务
                - Pre # 自定义过滤器
                - Post # 自定义过滤器

    (5)接下来我们调用接口,可以看到耗时时间打在公屏上

    五、网关限流

    1、我们可以在网关层面做限流的功能,防止高并发时把服务器搞崩,或者应对一些网络攻击等情况

    2、SpringCloudGateway为我们提供了一个很方便使用的令牌桶限流,思路:我们设置一个固定大小的令牌桶,如果令牌桶不满,则根据一定的频率向桶里放入令牌,每当有客户端的请求发来,会先从令牌桶里面取令牌,取到了则继续执行,取不到请求则会被拒绝,这样就达到了限流的作用,我们可以灵活地调整令牌补充速率和令牌桶大小,来细粒度的控制限流,我们使用redis来存储令牌,下面是一个简单的demo

    (1)引入redis依赖

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-redis-reactive</artifactId>
    </dependency>

    (2)接下来自定义限流的维度,我们可以从域名、uri等维度进行限流

    public class HostAddrKeyResolver implements KeyResolver
    {
        @Override
        public Mono<String> resolve(ServerWebExchange exchange)
        {
            // 域名维度限流
            return Mono.just(exchange.getRequest().getRemoteAddress().getAddress().getHostAddress());
            // uri维度限流
            // return Mono.just(exchange.getRequest().getURI().getPath());
        }
    }

    (3)将解析器注入到Spring

    @Configuration
    public class TokenLimitConfig
    {
        @Bean
        public HostAddrKeyResolver hostAddrKeyResolver() {
            return new HostAddrKeyResolver();
        }
    }

    (4)接下来配置限流器的配置,将我们编写的解析器引用,并且配置令牌桶的属性,这里我配置的是,每秒补充一个令牌,令牌桶的大小为3,最后配置redis的参数,用起来非常简单

    spring:
      cloud:
        gateway:
          discovery:
            locator:
              enabled: true # 设置可以路由到其他服务
          routes: # 可以配置多个路由
            - id: service-media-1 # 路由id唯一
              uri: lb://service-media # 可以直接跳转到具体的地址,如果要跳转到其他服务,则填写lb://<服务id>
              predicates:
                - Path=/media/** # 路由规则
              filters:
                - StripPrefix=1 # 不填则无法路由到其他服务
                - name: RequestRateLimiter # 配置限流器
                  args:
                    key-resolver: '#{@hostAddrKeyResolver}' # 自定义限流过滤器
                    redis-rate-limiter.replenishRate: 1 # 令牌桶每秒填充平均速率
                    redis-rate-limiter.burstCapacity: 3 # 令牌桶总容量
      redis:
        host: 127.0.0.1
        port: 4000
        password: 123456

    (5)配置好后,使用JMeter来压测一下接口,可以发现某些请求返回429错误码,被限流

  • 相关阅读:
    Mybatis使用map传递参数与模糊查询写法
    mybatis实现简单的crud
    普通maven项目导入mybatis依赖后找不到程序包(已解决)
    MarkDown语法学习
    CentOS 7 配置hadoop(一) 安装虚拟机(伪分布)
    CentOS 7 配置hadoop(二) 配置hdfs(伪分布)
    生成32个的字母加数字
    mysql 横变竖 竖变横
    Java实现短息验证
    spring+springmvc+mybatis+Redis的配置文件
  • 原文地址:https://www.cnblogs.com/orange911/p/10641608.html
Copyright © 2011-2022 走看看