SpringCloud微服务小白也能搭(Hoxton.SR8)(七)Gateway|服务网关

简单上手,直接照搬,就可搭建微服务(Hoxton.SR8) 2020.8.28发布,SpringCloud搭建的文章正在整理,干货不要错过哦

摘要

Spring Cloud Gateway 为 SpringBoot 应用提供了API网关支持,具有强大的智能路由与过滤器功能。Gateway是在Spring生态系统之上构建的API网关服务,基于Spring 5,Spring Boot 2和 Project Reactor等技术。Gateway旨在提供一种简单而有效的方式来对API进行路由,以及提供一些强大的过滤器功能, 例如:熔断、限流、重试等。

Spring Cloud Gateway 具有如下特性:

扫描二维码关注公众号,回复: 12067256 查看本文章
  • 基于Spring Framework 5, Project Reactor 和 Spring Boot 2.0 进行构建;
  • 动态路由:能够匹配任何请求属性;
  • 可以对路由指定 Predicate(断言)和 Filter(过滤器);
  • 集成Hystrix的断路器功能;
  • 集成 Spring Cloud 服务发现功能;
  • 易于编写的 Predicate(断言)和 Filter(过滤器);
  • 请求限流功能;
  • 支持路径重写。

配置组成

  • Route(路由):路由是构建网关的基本模块,它由ID,目标URI,一系列的断言和过滤器组成,如果断言为true则匹配该路由;
  • Predicate(断言):指的是Java 8 的 Function Predicate。 输入类型是Spring框架中的ServerWebExchange。 这使开发人员可以匹配HTTP请求中的所有内容,例如请求头或请求参数。如果请求与断言相匹配,则进行路由;
  • Filter(过滤器):指的是Spring框架中GatewayFilter的实例,使用过滤器,可以在请求被路由前后对请求进行修改。

1.创建一个api-gateway模块

  

2 单独使用gateway

2.1 pom.xml新增 gateway 依赖

<?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">
    <parent>
        <artifactId>cloud-hoxton-sr8</artifactId>
        <groupId>com.zqh.www</groupId>
        <version>0.0.1-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>api-gateway</artifactId>

    <dependencies>
        <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>
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
</project>

2.2 启动类

package com.zqh.www;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.core.env.Environment;

@SpringBootApplication
public class ApiGatewayApplication {

    private final static Logger logger = LoggerFactory.getLogger(ApiGatewayApplication.class);

    public static void main(String[] args) {
        Environment env = SpringApplication.run(ApiGatewayApplication.class, args).getEnvironment();
        logger.info(
                "\n----------------------------------------------------------\n\t"
                        + "Application '{}' is running! Access URLs:\n\t"
                        + "Local: \t\thttp://localhost:{}{}"
                        + "\n----------------------------------------------------------",
                env.getProperty("spring.application.name"), env.getProperty("server.port"),
                env.getProperty("server.servlet.context-path") != null ? env.getProperty("server.servlet.context-path") : "");
    }
}

2.3 yml配置

server:
  port: 8093

service-url:
  user-service: http://localhost:8084

spring:
  application:
    name: api-gateway
  cloud:
    gateway:
      routes:
        # 路由的ID
        - id: user-service
          # 匹配后路由地址
          uri: ${service-url.user-service}
          predicates:
            # 发送指定路径的请求会匹配该路由。
            - Path=/api/user/*
#            # 发送指定方法的请求会匹配该路由。
#            - Method=GET
#            # 在指定时间之后的请求会匹配该路由。
#            - After=2020-10-20T12:00:00+08:00[Asia/Shanghai]
#            # 在指定时间之前的请求会匹配该路由。
#            - Before=2020-10-20T12:00:00+08:00[Asia/Shanghai]
#            # 在指定时间区间内的请求会匹配该路由。
#            - Between=2020-10-20T12:00:00+08:00[Asia/Shanghai], 2020-10-30T12:00:00+08:00[Asia/Shanghai]
#            # 带有指定Cookie的请求会匹配该路由
#            - Cookie=username,jourwon
#            # 带有指定请求头的请求会匹配该路由。
#            - Header=auth
#            #带有指定Host的请求会匹配该路由。
#            - Host=www.zqh.com
#            # 从指定远程地址发起的请求可以匹配该路由。
#            - RemoteAddr=192.168.1.1/24
          filters:
#            - AddRequestParameter=username,z
#            - StripPrefix=1
#            - PrefixPath=/api

logging:
  level:
    org.springframework.cloud.gateway: debug

2.4 predicates - 测试用例

注意:如果存在多个条件,则需要所有条件满足才会匹配到相关的路由

2.4.1 predicates - Path

描述:发送匹配路径或的请求会匹配该路由。
yml配置:- Path=/api/user/*
接口请求:http://localhost:8093/api/user/getUserList
相当于:http://localhost:8084/api/user/getUserList

2.4.2 predicates - Method

描述:发送指定方法的请求会匹配该路由。
接口请求:http://localhost:8093/api/user/getUserList

  

2.4.3 predicates - After

描述:在指定时间之后的请求会匹配该路由。
接口请求:http://localhost:8093/api/user/getUserList

  

2.4.4 predicates - Before

描述:在指定时间之前的请求会匹配该路由。

2.4.5 predicates - Between

描述:在指定时间区间内的请求会匹配该路由。

2.4.6 predicates - Cookie

描述:带有指定Cookie的请求会匹配该路由。
配置:

predicates:
    # 带有指定Cookie的请求会匹配该路由
    - Cookie=username,z

接口请求:

2.4.7 predicates - Header

描述:带有指定请求头的请求会匹配该路由。
设置:

predicates:
    # 带有指定请求头的请求会匹配该路由。
    - Header=auth

接口请求:
 

2.4.8 predicates - Host

描述:带有指定Host的请求会匹配该路由。
设置:

predicates:
    #带有指定Host的请求会匹配该路由。
    - Host=www.zqh.com

2.4.9 predicates - RemoteAddr

描述:从指定路径的请求会匹配该路由。
设置:

predicates:
    #从指定远程地址发起的请求可以匹配该路由。
    - RemoteAddr=192.168.0.100

2.5 filters - 测试用例

2.5.1 filters - AddRequestParameter

描述:会对GET请求添加请求参数
配置:

service-url:
  user-service: http://localhost:8084
spring:
  application:
    name: api-gateway
  cloud:
    gateway:
      routes:
        # 路由的ID
        - id: user-service
          # 匹配后路由地址
          uri: ${service-url.user-service}
          predicates:
            # 发送指定路径的请求会匹配该路由。
            - Path=/api/user/*
          filters:
            - AddRequestParameter=username,z

请求:http://localhost:8093/api/user/getUserList
相当于:http://localhost:8084/api/user/getUserList?username=z

2.5.2 filters - StripPrefix

描述:会把开头的请求的路径去除指定位数
配置:

service-url:
  user-service: http://localhost:8084
spring:
  application:
    name: api-gateway
  cloud:
    gateway:
      routes:
        # 路由的ID
        - id: user-service
          # 匹配后路由地址
          uri: ${service-url.user-service}
          predicates:
            # 发送指定路径的请求会匹配该路由。
            - Path=/api/user/*
          filters:
            - StripPrefix=1

请求:http://localhost:8093/api/user/getUserList
相当于:http://localhost:8084/user/getUserList

2.5.3 filters - PrefixPath

描述:会对所有GET请求添加指定路径前缀
配置:

service-url:
  user-service: http://localhost:8084
spring:
  application:
    name: api-gateway
  cloud:
    gateway:
      routes:
        # 路由的ID
        - id: user-service
          # 匹配后路由地址
          uri: ${service-url.user-service}
          predicates:
            # 发送指定路径的请求会匹配该路由。
            - Path=/user/*
          filters:
            - PrefixPath=/api

请求:http://localhost:8093/user/getUserList
相当于:http://localhost:8084/api/user/getUserList

2.5.3 filters - hystrix

描述:Hystrix 过滤器允许你将断路器功能添加到网关路由中,使你的服务免受级联故障的影响,并提供服务降级处理。
pom.xml:

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>

 yml:

service-url:
  user-service: http://localhost:8084
spring:
  application:
    name: api-gateway
  cloud:
    gateway:
      routes:
        # 路由的ID
        - id: user-service
          # 匹配后路由地址
          uri: ${service-url.user-service}
          predicates:
            # 发送指定路径的请求会匹配该路由。
            - Path=/api/user/*
          filters:
            - name: Hystrix
              args:
                name: fallbackcmd
                #转发路径
                fallback-uri: forward:/fallback

controller:

package com.zqh.www.controller;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.HashMap;
import java.util.Map;

@RestController
public class FallbackController {

    @GetMapping("/fallback")
    public Object fallback() {
        Map<String, Object> result = new HashMap<>();
        result.put("data", null);
        result.put("message", "Get request fallback!");
        result.put("code", 500);
        return result;
    }
}

 测试:
正常情况:                                                       服务异常:(关闭user-service服务模拟服务断开)

     

2.5.3 filters - RequestRateLimiter

描述:RequestRateLimiter 过滤器可以用于限流,使用RateLimiter实现来确定是否允许当前请求继续进行,如果请求太大默认会返回HTTP 429-状态。
pom.xml:

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

yml:

service-url:
  user-service: http://localhost:8084

spring:
  application:
    name: api-gateway
  cloud:
    gateway:
      routes:
        # 路由的ID
        - id: user-service
          # 匹配后路由地址
          uri: ${service-url.user-service}
          predicates:
            # 发送指定路径的请求会匹配该路由。
            - Path=/api/user/*
          filters:
            - name: RequestRateLimiter
              args:
                # 每秒允许处理的请求数量
                redis-rate-limiter.replenishRate: 1
                # 每秒最大处理的请求数量
                redis-rate-limiter.burstCapacity: 2
                # 限流策略,对应策略的Bean
                key-resolver: "#{@ipKeyResolver}"

config:

package com.zqh.www.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;

import java.util.Objects;

@Configuration
public class RedisRateLimiterConfig {

    // 如果你是采用第二个的策略,那么第一个策略的@Bean记得要注释掉,否则他会报找到2个参数
    //    @Bean
    public KeyResolver userKeyResolver() {
        return exchange -> Mono.just(Objects.requireNonNull(exchange.getRequest().getQueryParams().getFirst("username")));
    }

    @Bean
    public KeyResolver ipKeyResolver() {
        return exchange -> Mono.just(Objects.requireNonNull(exchange.getRequest().getRemoteAddress()).getHostName());
    }
}

测试:
正常情况:                                                         异常情况:在快速请求的情况下返回429
  

2.5.4 filters - Retry

描述:对路由请求进行重试的过滤器,可以根据路由请求返回的HTTP状态码来确定是否进行重试。

yml:

service-url:
  user-service: http://localhost:8084
spring:
  application:
    name: api-gateway
  cloud:
    gateway:
      routes:
        # 路由的ID
        - id: user-service
          # 匹配后路由地址
          uri: ${service-url.user-service}
          predicates:
            # 发送指定路径的请求会匹配该路由。
            - Path=/api/user/*
          filters:
            - name: Retry
              args:
                #需要进行重试的次数
                retries: 1
                #返回哪个状态码需要进行重试,返回状态码为5XX进行重试
                statuses: BAD_GATEWAY 
                backoff:
                  firstBackoff: 10ms
                  maxBackoff: 50ms
                  factor: 2
                  basedOnPreviousValue: false

测试:
正常:                                                                       异常:(在提供服务的增加一个null指针),会输出2次错误,表示重试了1次
   

3.微服务使用gateway

3.1 pom.xm依赖

<dependencies>
        <!-- 配合注册中心使用 -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</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>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis-reactive</artifactId>
        </dependency>
    </dependencies>

3.2 启动类

package com.zqh.www;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.core.env.Environment;

/**
 * 开启服务发现客户端
 *
 * @EnableEurekaClient只适用于Eureka作为注册中心,@EnableDiscoveryClient 可以是其他注册中心。
 */
// 如果你是启动的是alone的配置,请注释@EnableDiscoveryClient,并且注释 pom.xml 里面的 eureka-client 依赖,不然会出现链接错误
@EnableDiscoveryClient
@SpringBootApplication
public class ApiGatewayApplication {

    private final static Logger logger = LoggerFactory.getLogger(ApiGatewayApplication.class);

    public static void main(String[] args) {
        Environment env = SpringApplication.run(ApiGatewayApplication.class, args).getEnvironment();
        logger.info(
                "\n----------------------------------------------------------\n\t"
                        + "Application '{}' is running! Access URLs:\n\t"
                        + "Local: \t\thttp://localhost:{}{}"
                        + "\n----------------------------------------------------------",
                env.getProperty("spring.application.name"), env.getProperty("server.port"),
                env.getProperty("server.servlet.context-path") != null ? env.getProperty("server.servlet.context-path") : "");
    }
}

3.3 yml配置

server:
  port: 8093

eureka:
  client:
    service-url:
      #注册地址
      defaultZone: http://root:root@localhost:8081/eureka/
  #显示服务器IP加端口
  instance:
    hostname: localhost
    prefer-ip-address: true
    instance-id: ${spring.cloud.client.ip-address}:${server.port}

spring:
  application:
    name: api-gateway
  cloud:
    gateway:
      discovery:
        locator:
          #开启从注册中心动态创建路由的功能
          enabled: true
          #使用小写服务名
          lower-case-service-id: true
      routes:
        # 路由的ID
        - id: user-service
          # 在结合注册中心使用过滤器的时候,uri的协议为lb,这样才能启用Gateway的负载均衡功能。
          uri: lb://user-service
          predicates:
            # 发送指定路径的请求会匹配该路由。
            - Path=/api/user/*
#            # 发送指定方法的请求会匹配该路由。
#            - Method=GET
#            # 在指定时间之后的请求会匹配该路由。
#            - After=2020-10-20T18:00:00+08:00[Asia/Shanghai]
#            # 在指定时间之前的请求会匹配该路由。
#            - Before=2020-10-20T12:00:00+08:00[Asia/Shanghai]
#            # 在指定时间区间内的请求会匹配该路由。
#            - Between=2020-10-20T12:00:00+08:00[Asia/Shanghai], 2020-10-30T12:00:00+08:00[Asia/Shanghai]
#            # 带有指定Cookie的请求会匹配该路由
#            - Cookie=username,z
#            # 带有指定请求头的请求会匹配该路由。
#            - Header=auth
#            #带有指定Host的请求会匹配该路由。
#            - Host=www.zqh.com
#            # 从指定远程地址发起的请求可以匹配该路由。
#            - RemoteAddr=192.168.1.1/24
          filters:
            - name: Retry
              args:
                #需要进行重试的次数
                retries: 3
                #返回哪个状态码需要进行重试,返回状态码为5XX进行重试
                statuses: BAD_GATEWAY
                backoff:
                  firstBackoff: 10ms
                  maxBackoff: 50ms
                  factor: 2
                  basedOnPreviousValue: false
#            - name: RequestRateLimiter
#              args:
#                # 每秒允许处理的请求数量
#                redis-rate-limiter.replenishRate: 1
#                # 每秒最大处理的请求数量
#                redis-rate-limiter.burstCapacity: 2
#                # 限流策略,对应策略的Bean
#                key-resolver: "#{@ipKeyResolver}"
#            - name: Hystrix
#              args:
#                name: fallbackcmd
#                #转发路径
#                fallback-uri: forward:/fallback
#            - AddRequestParameter=username,z
#            - StripPrefix=1
#            - PrefixPath=/api

logging:
  level:
    org.springframework.cloud.gateway: debug

3.4 测试

4.gitee地址

源码参考

猜你喜欢

转载自blog.csdn.net/itjavaee/article/details/109119266