关于java:面试官限流的实现方式有哪些

5次阅读

共计 3944 个字符,预计需要花费 10 分钟才能阅读完成。

限流是指在各种利用场景中,通过技术和策略伎俩对数据流量、申请频率或资源耗费进行有打算的限度,以防止零碎负载过高、性能降落甚至解体的状况产生。限流的指标在于保护零碎的稳定性和可用性,并确保服务质量。

应用限流的益处有以下几个:

  1. 爱护零碎稳定性 :过多的并发申请可能导致服务器内存耗尽、CPU 使用率饱和,从而引发零碎响应慢、无奈失常服务的问题。
  2. 避免资源滥用 :确保无限的服务资源被正当偏心地调配给所有用户,避免个别用户或恶意程序适度耗费资源。
  3. 优化用户体验 :对于网站和应用程序而言,如果任由高并发导致响应速度变慢,会影响所有用户的失常应用体验。
  4. 保障平安 :在网络层面,限流有助于防备 DoS/DDoS 攻打,升高零碎蒙受歹意攻打的危险。
  5. 运维老本管制 :正当的限流措施能够帮忙企业缩小不必要的硬件投入,节俭经营老本。

在 Java 中,限流的实现形式有很多种,例如以下这些:

  1. 单机限流 :应用 JUC 下的 Semaphore 限流,或一些罕用的框架,例如 Google 的 Guava 框架进行限流,但这种限流形式都是基于 JVM 层面的内存级别的单台机器限流。
  2. 网关层限流 :单机限流往往不适用于分布式系统,而分布式系统能够在网关层限流,如 Spring Cloud Gateway 通过 Sentinel、Hystrix 对整个集群进行限流。
  3. Nginx 限流 :通常在网关层的上游,咱们会应用 Nginx 一起来配合应用,也就是用户申请会先到 Nginx(或 Nginx 集群),而后再将申请转发给网关,网关再调用其余的微服务,从而实现整个流程的申请调用,因而 Nginx 限流也是分布式系统中罕用的限流伎俩。

它们限流的具体实现如下。

1. 单机限流

JVM 层面多线程级别的限流能够应用 JUC 下的 Semaphore,具体应用示例如下:

import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;

public class SemaphoreExample {private final Semaphore semaphore = new Semaphore(5); // 只容许 5 个线程同时拜访

    public void accessResource() {
        try {semaphore.acquire(); // 获取许可,如果以后许可数有余,则会阻塞
            System.out.println(Thread.currentThread().getName() + "取得了许可,正在拜访资源...");
            // 模仿拜访资源的工夫耗费
            Thread.sleep(2000);
            System.out.println(Thread.currentThread().getName() + "拜访资源完结,开释许可...");
        } catch (InterruptedException e) {Thread.currentThread().interrupt();
            e.printStackTrace();} finally {semaphore.release(); // 拜访完结后开释许可
        }
    }

    public static void main(String[] args) {SemaphoreExample example = new SemaphoreExample();

        for (int i = 0; i < 10; i++) {new Thread(() -> example.accessResource()).start();}
    }
}

想要实现更平滑的单机限流,能够思考 Google 提供的 Guava 框架,它的应用示例如下。

首先在 pom.xml 增加 guava 援用,配置如下:

<!-- https://mvnrepository.com/artifact/com.google.guava/guava -->
<dependency>
  <groupId>com.google.guava</groupId>
  <artifactId>guava</artifactId>
  <version>28.2-jre</version>
</dependency>

具体实现代码如下:

import com.google.common.util.concurrent.RateLimiter;
import java.time.Instant;

/**
 * Guava 实现限流
 */
public class RateLimiterExample {public static void main(String[] args) {
        // 每秒产生 10 个令牌(每 100 ms 产生一个)RateLimiter rt = RateLimiter.create(10);
        for (int i = 0; i < 11; i++) {new Thread(() -> {
                // 获取 1 个令牌,获取到令牌就执行,否则就阻塞期待
                rt.acquire();
                System.out.println("失常执行办法,ts:" + Instant.now());
            }).start();}
    }
}

2. 网关层限流

在 Spring Cloud Gateway 网关层限流,能够借助 Sentinel 等限流框架来实现,它的实现步骤如下。

首先,在 pom.xml 中增加 Gateway 和 Sentinel 相干依赖,如下所示:

<dependency>
  <groupId>com.alibaba.cloud</groupId>
  <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
<dependency>
  <groupId>org.springframework.cloud</groupId>
  <artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<dependency>
  <groupId>com.alibaba.cloud</groupId>
  <artifactId>spring-cloud-alibaba-sentinel-gateway</artifactId>
</dependency>

配置限流相干的规定,如下示例所示:

spring:
  application:
    name: gate-way-blog
  cloud:
    sentinel:
      transport:
        dashboard: localhost:18080
      scg: # 配置限流之后,响应内容
        fallback:
          # 两种模式,一种是 response 返回文字提示信息,# 另一种是 redirect 重定向跳转,不过配置 redirect 也要配置对应的跳转的 uri
          mode: response
          # 响应的状态
          response-status: 200
          # 响应体
          response-body: '{"code": -10,"message":" 被熔断或限流!"}'

最初在 Sentinel 控制台配置网关的限流设置即可,当然也能够应用 Nacos 作为数据源,两者抉择配置其中一个即可。

3.Nginx 限流

Nginx 提供了两种限流伎俩:

  • 通过管制速率来实现限流。
  • 通过管制并发连接数来实现限流。

咱们一个一个来看。

3.1 管制速率实现限流

咱们须要应用 limit_req_zone 用来限度单位工夫内的申请数,即速率限度,示例配置如下:

limit_req_zone $binary_remote_addr zone=mylimit:10m rate=2r/s;
server {
  location / {limit_req zone=mylimit;}
}

以上配置示意,限度每个 IP 拜访的速度为 2r/s,因为 Nginx 的限流统计是基于毫秒的,咱们设置的速度是 2r/s,转换一下就是 500ms 内单个 IP 只容许通过 1 个申请,从 501ms 开始才容许通过第 2 个申请。

咱们应用单 IP 在 10ms 内发并发送了 6 个申请的执行后果如下:

从以上后果能够看出他的执行合乎咱们的预期,只有 1 个执行胜利了,其余的 5 个被回绝了(第 2 个在 501ms 才会被失常执行)。

速率限度升级版

下面的速率管制尽管很精准然而利用于实在环境未免太刻薄了,真实情况下咱们应该管制一个 IP 单位总工夫内的总拜访次数,而不是像下面那么准确但毫秒,咱们能够应用 burst 关键字开启此设置,示例配置如下:

limit_req_zone $binary_remote_addr zone=mylimit:10m rate=2r/s;
server {
  location / {limit_req zone=mylimit burst=4;}
}

burst=4 示意每个 IP 最多容许 4 个突发申请,如果单个 IP 在 10ms 内发送 6 次申请的后果如下:

从以上后果能够看出,有 1 个申请被立刻解决了,4 个申请被放到 burst 队列里排队执行了,另外 1 个申请被回绝了。

3.2 管制并发数实现限流

利用 limit_conn_zone 和 limit_conn 两个指令即可管制并发数,示例配置如下:

limit_conn_zone $binary_remote_addr zone=perip:10m;
limit_conn_zone $server_name zone=perserver:10m;
server {
  ...
  limit_conn perip 10;
  limit_conn perserver 100;
}

其中 limit_conn perip 10 示意限度单个 IP 同时最多能持有 10 个连贯;limit_conn perserver 100 示意 server 同时能解决并发连贯的总数为 100 个。

小贴士:只有当 request header 被后端解决后,这个连贯才进行计数。

课后思考

Semaphore 限流和 Guava 限流有什么区别?Sentinel 和 Nginx 限流有什么有余?应该如何防止?

正文完
 0