[spring-cloud-gateway]

[spring-cloud-gateway]

  • 我们讲讲网关吧
    • 1.网关的重要性。
    • gateway ,访问网关,调用指定服务的方法
    • 集成过滤器,token(权限),指定服务使用token校验。
    • 熔断 降级 限流(ip)
    • 2.怎么学习网关,举个例子了。这一篇就不说动态路由实现了,我们放在下一篇。
    • 3.我们主要说的是网关项目,开始吧!!!都着急了。
    • 4、启动我们的项目吧,开测。

我们讲讲网关吧

1.网关的重要性。

gateway ,访问网关,调用指定服务的方法

集成过滤器,token(权限),指定服务使用token校验。

熔断 降级 限流(ip)

  1. 引用一下《百度百科》的说法。
    网关(Gateway)又称网间连接器、协议转换器。网关在网络层以上实现网络互连,是最复杂的网络互连设备,仅用于两个高层协议不同的网络互连。网关既可以用于广域网互连,也可以用于局域网互连。 网关是一种充当转换重任的计算机系统或设备。使用在不同的通信协议、数据格式或语言,甚至体系结构完全不同的两种系统之间,网关是一个翻译器。与网桥只是简单地传达信息不同,网关对收到的信息要重新打包,以适应目的系统的需求。同层–应用层。
  2. 简而言之,就是将所有服务关在了一个大门的后边,而这个大门就是所谓的网关。所有的请求都将走这个大门才能够访问到所需的服务。所以网关的重要性就不言而喻了。
  3. 图片就不在网上找了,差不多就是一个样子,记住它是一扇门就很形象生动了。

2.怎么学习网关,举个例子了。这一篇就不说动态路由实现了,我们放在下一篇。

  1. 开始咱们的表演吧。首先这个项目要用到eureka-service , 服务的消费方consumer,在一个就是网关项目了,暂且叫他gateway-route.三个项目就可以进行演示了。
  2. 网关和消费者都将注册到eureka,我们访问网关时,通过eureka路由到相应的服务。实现服务的调用。

3.我们主要说的是网关项目,开始吧!!!都着急了。

  1. 引入所需要的依赖pom如下。
        <!--gateway不兼容springboot—-web,必须引入webflux.-->
        <!--入了个大坑,搞了我几个小时-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-webflux</artifactId>
            <version>2.0.4.RELEASE</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-gateway</artifactId>
            <version>2.0.1.RELEASE</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
            <version>2.0.1.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
            <version>2.0.4.RELEASE</version>
        </dependency>

        <!--熔断-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
            <version>2.0.1.RELEASE</version>
        </dependency>

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

        <dependency>
            <groupId>org.apache.logging.log4j</groupId>
            <artifactId>log4j</artifactId>
            <version>2.10.0</version>
            <type>pom</type>
        </dependency>
  1. 看到了我的注释,禁不住在啰嗦一句,spring-cloud-gateway 不兼容web ,只支持webflux,很是蛋疼。
  2. 这一块的东西基本上都在配置上边,配置文件显得有点多。
server:
  port: 9967
spring:
  application:
    name: gateway-service
  redis:
    host: 127.0.0.1
    password: 123456
    port: 6379
    database: 0
  cloud:
    gateway:
      discovery:
        locator:
          enabled: true
          # 服务名小写
          lower-case-service-id: true
      routes:
      - id: consumer-service
        # lb代表从注册中心获取服务,且已负载均衡方式转发
        uri: lb://consumer-service  #注册中心服务Id,serviceId
        predicates:
        - Path=/consumer/**   #*/#网关拦截的访问路径。(针对服务拦截)
        # 加上StripPrefix=1,否则转发到后端服务时会带上consumer前缀
        filters:
        # 进行token过滤
        - TokenAuthenticationFilter   #加上这个时候接口需要鉴权  token
        - StripPrefix=1               #否则转发到后端服务时会带上consumer前缀
        # 熔断降级配置
        - name: Hystrix    #不会配置看看源码
          args:
            name: default
            fallbackUri: 'forward:/defaultfallback'  #服务超时的回调,及时响应给前端。
        # redis限流 , filter名称必须是RequestRateLimiter
        - name: RequestRateLimiter
          args:
            # 与使用SpEL名称引用Bean,上面新建的RateLimiterConfig类中的bean的name相同
            key-resolver: '#{@remoteAddrKeyResolver}' #是一个简单的获取用户请求参数 我这里以主机地址为key来作限流
            redis-rate-limiter.replenishRate: 20 # 允许用户每秒执行多少请求,而不会丢弃任何请求。这是令牌桶填充的速率。
            redis-rate-limiter.burstCapacity: 20 # 是一秒钟内允许用户执行的最大请求数。这是令牌桶可以容纳的令牌数。将此值设置为零将阻止所有请求
      - id: consumer-service
        # lb代表从注册中心获取服务,且已负载均衡方式转发
        uri: lb://consumer-service
        predicates:
        - Path=/a/**
        #*/# 加上StripPrefix=1,否则转发到后端服务时会带上consumer前缀
        filters:
        # 进行token过滤
        #        - TokenAuthenticationFilter
        - StripPrefix=1
        # 熔断降级配置
        - name: Hystrix
          args:
            name: default
            fallbackUri: 'forward:/defaultfallback' #服务超时的回调,及时响应给前端。


# hystrix 信号量隔离,3秒后自动超时
hystrix:
  command:
    default:
      execution:
        isolation:
          strategy: SEMAPHORE
          thread:
            timeoutInMilliseconds: 10000  #指定超时时间
  shareSecurityContext: true

# 注册中心
eureka:
  instance:
    prefer-ip-address: true
  client:
    service-url:
      defaultZone: http://localhost:8899/eureka/


# 暴露监控端点
management:
  endpoints:
    web:
      exposure:
        include: '*'
  endpoint:
    health:
      show-details: always
  1. 注释写的很详细,路由就是在配置文件里写死的,生产环境下一般不这样做,因为网关至关重要,不会说添加一个服务,添加一个路由就要重启网关,这是不显现实的。因此这里只是尝试网关的作用。下一篇进阶将动态路由,不重启网关的情况下,使新增的路由生效。
  2. 配置文件很详细,包含了熔断,限流,验证, 路由。

4、启动我们的项目吧,开测。

[spring-cloud-gateway]_第1张图片
配置文件加上了鉴权,因此我们在没加token的情况下,访问401了。
[spring-cloud-gateway]_第2张图片加上我们的token,就访问成功了。

在这里就要说一下我们的过滤器。TokenAuthenticationFilter 继承了AbstractGatewayFilterFactory,代码如下,

@Component
public class TokenAuthenticationFilter extends AbstractGatewayFilterFactory {
    private static final String Bearer_ = "Bearer ";
    @Override
    public GatewayFilter apply(Object config) {
        return (exchange, chain) -> {
            ServerHttpRequest request = exchange.getRequest();
            ServerHttpRequest.Builder mutate = request.mutate();
            ServerHttpResponse response = exchange.getResponse();
            try {
                //1.获取header中的Authorization
                String header = request.getHeaders().getFirst("Authorization");
                if (header == null || !header.startsWith(Bearer_)) {
                    throw new RuntimeException("请求头中Authorization信息为空");
                }
                //2.截取Authorization Bearer
                String token = header.substring(7);
                //可以配合redis来提高性能
                if (!StringUtils.isEmpty(token)) {
                    System.out.println("成功");
                    //3.有token,把token设置到header中,传递给后端服务
                    mutate.header("token", token).build();
                } else {
                    //4.token无效
                    System.out.println("token无效");
                    DataBuffer bodyDataBuffer = responseErrorInfo(response, HttpStatus.UNAUTHORIZED.toString(), "无效的请求");
                    return response.writeWith(Mono.just(bodyDataBuffer));
                }
            } catch (Exception e) {
                //没有token
                DataBuffer bodyDataBuffer = responseErrorInfo(response, HttpStatus.UNAUTHORIZED.toString(), e.getMessage());
                return response.writeWith(Mono.just(bodyDataBuffer));
            }
            ServerHttpRequest build = mutate.build();
            return chain.filter(exchange.mutate().request(build).build());
        };
    }
    /**
     * 自定义返回错误信息
     *
     */
    public DataBuffer responseErrorInfo(ServerHttpResponse response, String status, String message) {
        HttpHeaders httpHeaders = response.getHeaders();
        httpHeaders.add("Content-Type", "application/json; charset=UTF-8");
        httpHeaders.add("Cache-Control", "no-store, no-cache, must-revalidate, max-age=0");
        response.setStatusCode(HttpStatus.UNAUTHORIZED);
        Map<String, String> map = new HashMap<>();
        map.put("code", status);
        map.put("msg", message);
        DataBuffer bodyDataBuffer = response.bufferFactory().wrap(map.toString().getBytes());
        return bodyDataBuffer;
    }
}

卖了个关子,加上过滤器就ok了。
还有一个限流的配置类,如下

@Configuration
public class RateLimiterConfig {

    /**
     * Ip限流
     *
     * @return
     */
    @Bean(value = "remoteAddrKeyResolver")
    public KeyResolver remoteAddrKeyResolver() {
        return exchange -> Mono.just(exchange.getRequest().getRemoteAddress().getAddress().getHostAddress());
    }

//
//    /**
//     * 用户限流,使用这种方式限流,请求路径中必须携带userId参数
//     *
//     * @return
//     */
//    @Bean
//    KeyResolver userKeyResolver() {
//        return exchange -> Mono.just(exchange.getRequest().getQueryParams().getFirst("userId"));
//    }
//
//
//    /**
//     * 接口限流,获取请求地址的uri作为限流key
//     * @return
//     */
//    @Bean
//    KeyResolver apiKeyResolver() {
//        return exchange -> Mono.just(exchange.getRequest().getPath().value());
//    }

访问a下的hello,可以看到接口成功返回了数据
[spring-cloud-gateway]_第3张图片
我们将token去掉试试,也是成功的,因为我们在a下并没有配置token鉴权。
[spring-cloud-gateway]_第4张图片
在访问一个不存在的路由吧,不出所料404了。
[spring-cloud-gateway]_第5张图片
看来网关还是起到了,关键性作用。

明天接着把限流也给测一下。拜拜了
起到限流作用的配置是:

 # 与使用SpEL名称引用Bean,上面新建的RateLimiterConfig类中的bean的name相同
 key-resolver: '#{@remoteAddrKeyResolver}' #是一个简单的获取用户请求参数 我这里以主机地址为key来作限流
 redis-rate-limiter.replenishRate: 2 # 允许用户每秒执行多少请求,而不会丢弃任何请求。这是令牌桶填充的速率。
 redis-rate-limiter.burstCapacity: 10 # 是一秒钟内允许用户执行的最大请求数。这是令牌桶可以容纳的令牌数。将此值

使用压测工具jmeter,测试接口。
不会安装就jmeter工具的参考下面的博客:
https://blog.csdn.net/a13124837937/article/details/79628838
https://blog.csdn.net/fangfeixinlingamp/article/details/79982215
https://blog.csdn.net/cbzcbzcbzcbz/article/details/78023327
[spring-cloud-gateway]_第6张图片
100个线程 ,1s内执行完,执行3次。看一下限流情况。
[spring-cloud-gateway]_第7张图片
可以看到部分接口访问成功,部分被阻止掉了。
此时看一下redis限流情况。可以看到是本机ip被限流了。
[spring-cloud-gateway]_第8张图片
其他的限流方式还没有尝试。暂且先不做了。基本上这个层面上已经实现。

你可能感兴趣的:([spring-cloud],gateway)