服务网关Gateway

内容纲要

服务网关在微服务中的应用

  • 官方主推
  • 底层Netty构建
  • 社区维护

    Gateway做什么

    1. 路由寻址(主要)
    2. 负载均衡(Ribbon)
    3. 限流
    4. 鉴权

      第二代网关组件Gateway介绍(Zuul的对比)

      Gateway Zuul 1.x Zuul 1.x
      靠谱性 官方支持 曾经靠谱过 专业放鸽子
      性能 Netty 同步阻塞性能慢 Netty
      RPS >32000 20000左右 25000左右
      Spring Cloud 已整合 已整合 占无整合计划
      长链接 支持 不支持 支持
      编程体验 略难 简单易上手 略难
      调试&链路追踪 略难 无压力 略难
  • 用Gateway

    Gateway急速落地

    创建gateway-sample项目

    1. 引入依赖
      <dependencies>
      <dependency>
      <groupId>org.springframework.cloud</groupId>
      <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
      </dependency>
      <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-actuator</artifactId>
      </dependency>
      <dependency>
      <groupId>org.springframework.cloud</groupId>
      <artifactId>spring-cloud-starter-gateway</artifactId>
      </dependency>
      <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-data-redis-reactive</artifactId>
      </dependency>
      </dependencies>
    2. 创建启动类
      @EnableDiscoveryClient
      @SpringBootApplication
      public class GatewayApplication {
      public static void main(String[] args) {
      SpringApplication.run(GatewayApplication.class,args);
      }
      }
    3. 配置文件
      server:
      port: 65000
      spring:
      rabbitmq:
      username: gateway-service
      cloud:
      gateway:
      discovery:
      locator:
        enabled: true
      eureka:
      instance:
      preferIpAddress: true
      instance-id: ${spring.cloud.client.ip-address}:${server.port}
      client:
      service-url:
      defaultZone: http://localhost:20000/eureka/
      management:
      security:
      enabled: false
      endpoints:
      web:
      exposure:
      include: "*"
      endpoint:
      health:
      show-details: always

      连接Eureka自动创建路由

    4. 启动服务
    5. 验证路由规则
    6. 设置小写访问
      spring:
      application:
      name: gateway-service
      cloud:
      gateway:
      discovery:
      locator:
        enabled: true
        # 新增
        lower-case-service-id: true
    7. 验证路由规则
  • url: http://localhost:65000/actuator/gateway/routes/{动态路由名称}
  • 传参格式: application/json
  • 传参数据
    {
    "predicates":[
        {
        "name":"Path",
        "args":{
            "_genkey_0":"/dynamic/**"
            }
        }
    ],
    "filters":[
        {
            "name":"StripPrefix",
            "args":{
                "_genkey_0":"1"
            }
        }
    ],
    "uri":"lb://FEIGN-CLIENT",
    "order":0
    }

    相关接口文档

    Gateway断言功能

    Path断言

    1. 使用Path断言转发请求(yml配置+java配置)
  • yml配置
    spring:
    application:
    name: gateway-service
    cloud:
    gateway:
      discovery:
        locator:
          enabled: true
          lower-case-service-id: true
      # 新增
      routes:
      - id: feignclient
        uri: lb://FEIGN-CLIENT
        predicates:
        - Path=/yml/**
        filters:
        - StripPrefix=1
  • java配置

    @Configuration
    public class GatewayConfiguration {
    @Bean
    @Order
    public RouteLocator customizedRoutes(RouteLocatorBuilder builder){
        return builder.routes()
                .route(r-> r.path("/java/**")
                    .and().method(HttpMethod.GET)
                    .and().header("name")
                    .filters(f->f.stripPrefix(1)
                        .addRequestHeader("java-param","gateway-config")
                    )
                    .uri("lb://FEIGN-CLIENT")
                ).build();
    }
    }

    After断言

    1. 创建模拟下单接口
      
      @RestController
      @Slf4j
      @RequestMapping("/gateway")
      public class GatewayController {
      public static final Map<Long, Product> items = new ConcurrentHashMap<>();

    @GetMapping("/details")
    public Product get(@RequestParam("pid") Long pid) {
    if (items.containsKey(pid)) {
    Product prod = Product.builder()
    .productId(pid)
    .description("好吃不贵")
    .stock(100L)
    .build();
    items.putIfAbsent(pid, prod);
    }
    return items.get(pid);
    }

    @PostMapping("/placeOrder")
    public String buy(@RequestParam("pid") String pid) {
    Product prod = items.get(pid);
    if (prod == null) {
    return "Product not found";
    } else if (prod.getStock() <= 0L) {
    return "Sold out";
    }
    synchronized (prod) {
    if (prod.getStock() <= 0L) {
    return "Sold out";
    }
    prod.setStock(prod.getStock()-1);
    }
    return "Order Placed";
    }
    }

  1. 通过After断言设置生效时间

    @Configuration
    public class GatewayConfiguration {
    @Bean
    @Order
    public RouteLocator customizedRoutes(RouteLocatorBuilder builder) {
        return builder.routes()
                .route(r -> r.path("/java/**")
                        .and().method(HttpMethod.GET)
                        .and().header("name")
                        .filters(f -> f.stripPrefix(1)
                                .addRequestHeader("java-param", "gateway-config")
                        )
                        .uri("lb://FEIGN-CLIENT")
                )
                .route(r -> r.path("/seckill/**")
                        .and().after(ZonedDateTime.now().plusMinutes(1))
                        // .and().before()
                        // .and().between()
                        .filters(f -> f.stripPrefix(1))
                        .uri("lb://FEIGN-CLIENT"))
    
                .build();
    }
    }

    Gat过滤器原理和生命周期

    自定义过滤器

    @Slf4j
    @Component
    public class TimerFilter implements GatewayFilter, Ordered {
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        StopWatch timer = new StopWatch();
        timer.start(exchange.getRequest().getURI().getRawPath());
    //        exchange.getAttributes().put("requestTimeBegain",System.currentTimeMillis());
        return chain.filter(exchange).then(
                Mono.fromRunnable(()->{
                    timer.stop();
                    log.info(timer.prettyPrint());
                })
    
        );
    }
    
    @Override
    public int getOrder() {
        return 0;
    }
    }

    添加TimerFilter到路由

    • 局部filter
      .route(r -> r.path("/java/**")
      .and().method(HttpMethod.GET)
      .and().header("name")
      .filters(f -> f.stripPrefix(1)
              .addRequestHeader("java-param", "gateway-config")
              .filter(timerFilter)
      )
      .uri("lb://FEIGN-CLIENT")
      )
    • 全局filter
      将TimerFilter实现的GatewayFilter换为GlobalFilter
THE END
分享
二维码
< <上一篇
下一篇>>