有哪些实现限流的技术方案?
4 个回答
我最喜欢自己仁至义尽后心安理得的无情无义。天魁Istio 是目前云原生架构中最为重要的服务网格(Service Mesh)技术之一,它为微服务架构中的服务间通信提供了流量管理、安全性、策略控制和监控等能力。Istio 并不需要对应用代码进行改动,而是通过代理层来接管服务间的通信。 Istio 的核心在于它的 Sidecar 模式,即为每个微服务实例注入一个 Envoy 代理作为边车容器,拦截服务之间的所有网络流量。这种代理模式无需对应用本身做任何修改,Istio 通过这些代理容器来实现其流量管理、安全和监控功能。 在 **Istio** 中,实现流量控制、负载均衡、服务发现、熔断、监控和安全等功能,主要是通过对 **Envoy** 代理的配置,以及使用 **VirtualService**、**DestinationRule** 等配置资源来完成。这些功能的实现方式虽然底层由 **Envoy** 执行,但用户主要通过定义和应用 YAML 文件进行配置。下面我会详细说明这些功能的配置方式。 ### 1. **流量控制(Traffic Control)** 流量控制主要通过 **VirtualService** 配置实现。你可以定义不同的路由规则,根据请求的特定条件(如请求头、路径等)来决定请求如何分发到不同的服务版本。 **示例**:将流量按 80% 和 20% 的比例分别路由到 `v1` 和 `v2` 版本的服务。 ```yaml apiVersion: networking.istio.io/v1alpha3 kind: VirtualService metadata: name: myservice spec: hosts: - myservice http: - route: - destination: host: myservice subset: v1 weight: 80 - destination: host: myservice subset: v2 weight: 20 ``` - `hosts`: 指定该 `VirtualService` 应用于哪个主机(即服务)。 - `route`: 定义了流量如何在不同版本的服务之间分配。 - `weight`: 用来定义流量的比例,80% 的流量路由到 `v1`,20% 路由到 `v2`。 ### 2. **负载均衡(Load Balancing)** 负载均衡策略是在 **DestinationRule** 中定义的。Istio 支持多种负载均衡策略,包括轮询(round robin)、最少连接(least connection)、随机(random)等。 **示例**:配置服务 `myservice` 使用最少连接的负载均衡策略。 ```yaml apiVersion: networking.istio.io/v1alpha3 kind: DestinationRule metadata: name: myservice spec: host: myservice trafficPolicy: loadBalancer: simple: LEAST_CONN connectionPool: tcp: maxConnections: 100 http: http1MaxPendingRequests: 1000 ``` - `host`: 目标服务名称。 - `loadBalancer`: 定义负载均衡策略,这里使用的是最少连接策略 `LEAST_CONN`。 - `connectionPool`: 定义连接池配置,限制最大连接数和最大挂起请求数。 ### 3. **服务发现(Service Discovery)** Istio 的服务发现功能与 Kubernetes 的服务发现机制集成,无需额外配置。Istio 自动通过 **Pilot** 组件获取 Kubernetes 中注册的服务信息,并将其传递给 **Envoy** 代理进行服务发现和路由。 - **Kubernetes 服务发现**:当在 Kubernetes 集群中部署一个服务时,Istio 自动通过 Kubernetes API 发现该服务,无需额外配置。 ```yaml apiVersion: v1 kind: Service metadata: name: myservice spec: selector: app: myservice ports: - port: 80 ``` 这个 `Service` 定义在 Kubernetes 中注册了一个名为 `myservice` 的服务,Istio 自动通过 Pilot 获取这个服务的信息。 ### 4. **熔断(Circuit Breaking)** 熔断机制通过 **DestinationRule** 进行配置,用于保护服务不因过载导致崩溃。熔断策略允许在达到一定的失败条件时,切断对特定服务的调用。 **示例**:配置服务 `myservice` 的熔断策略,当连接数达到 100 时,启动熔断。 ```yaml apiVersion: networking.istio.io/v1alpha3 kind: DestinationRule metadata: name: myservice spec: host: myservice trafficPolicy: connectionPool: tcp: maxConnections: 100 http: http1MaxPendingRequests: 1000 outlierDetection: consecutiveErrors: 5 interval: 5s baseEjectionTime: 30s maxEjectionPercent: 50 ``` - `maxConnections`: 定义最大并发连接数。 - `outlierDetection`: 定义熔断条件,例如连续 5 个错误时触发熔断,隔离 30 秒,最多隔离 50% 的实例。 ### 5. **监控(Monitoring)** Istio 默认集成了多个开源监控工具,如 **Prometheus**、**Grafana**、**Jaeger** 等,用于监控服务间的流量、错误率、延迟等指标。Envoy 代理会自动收集服务调用数据并上报给 Prometheus,开发者可以通过 Grafana 和 Jaeger 查看监控数据和分布式追踪信息。 **Prometheus 配置示例**: ```yaml apiVersion: v1 kind: ServiceMonitor metadata: name: istio-monitor spec: selector: matchLabels: app: istio endpoints: - port: http-metrics path: /metrics interval: 15s ``` 通过 Prometheus 和 Grafana,用户可以实时监控服务的 QPS、延迟、成功率等重要指标。 ### 6. **安全(Security)** Istio 提供了 **双向 TLS(mTLS)** 加密,保证服务之间的通信安全。Istio 使用 Citadel 组件为每个服务分发证书,实现身份认证,并确保服务之间的通信加密。 #### **配置双向 TLS(mTLS)** **示例**:强制服务 `myservice` 启用双向 TLS 加密通信。 ```yaml apiVersion: security.istio.io/v1beta1 kind: PeerAuthentication metadata: name: default spec: mtls: mode: STRICT ``` - `mtls`: 表示启用双向 TLS 加密,`STRICT` 模式下,所有服务间通信必须使用加密。 ### 7. **访问控制** Istio 提供了细粒度的访问控制策略,你可以通过 **AuthorizationPolicy** 定义哪些服务或用户可以访问哪些资源。Istio 支持基于身份的访问控制(RBAC),可以指定服务的访问权限。 **示例**:配置服务 `myservice` 只能被 `user1` 访问。 ```yaml apiVersion: security.istio.io/v1beta1 kind: AuthorizationPolicy metadata: name: myservice-policy spec: action: ALLOW rules: - from: - source: principals: ["cluster.local/ns/default/sa/user1"] selector: matchLabels: app: myservice ``` - `principals`: 指定允许访问的主体,这里是服务账户 `user1`。 - `selector`: 匹配目标服务 `myservice`。 ### 总结 Istio 提供了强大的流量管理、安全控制、监控和可视化功能,通过配置以下资源,你可以实现不同的控制策略: 1. **流量控制**:通过 `VirtualService` 实现路由、重试、超时等流量控制策略。 2. **负载均衡**:通过 `DestinationRule` 定义负载均衡策略,包括最少连接、轮询等。 3. **熔断机制**:通过 `DestinationRule` 的 `outlierDetection` 配置实现熔断,保护系统免受过载影响。 4. **服务发现**:与 Kubernetes 集成,实现自动化的服务发现。 5. **安全性**:通过 `PeerAuthentication` 和 `AuthorizationPolicy` 实现双向 TLS 和细粒度访问控制。 6. **监控与可视化**:集成 Prometheus、Grafana、Jaeger,实现对服务调用的监控、日志和分布式追踪。 Istio 的灵活配置使得它成为云原生微服务架构中的核心组件,帮助开发者轻松管理复杂的微服务间通信和安全策略。编辑于 2024-11-04 01:54 •IP属地 美国举报评论收藏
万事尽心尽力就好,有则锦上添花,无则顺其自然。真禅胜王### Sentinel 的限流技术方案详解 Sentinel 是阿里巴巴开源的一款流量防护组件,专门用于在分布式系统中进行流量控制和服务治理。它支持限流、熔断、降级、系统保护等功能,并且提供了动态规则配置和实时监控的能力。下面我将详细整理 Sentinel 的限流技术方案。 ### Sentinel 核心功能 1. **限流**:支持多种限流模式,如基于 QPS、并发数、预热模式、排队等待等进行限流控制,确保系统在高并发场景下的稳定性。 2. **熔断降级**:根据系统的响应时间(RT)、异常比例等因素,自动触发服务熔断和降级,防止系统过载或依赖服务失效时造成系统崩溃。 3. **系统自适应保护**:根据系统的整体负载(CPU、线程数、系统负载等)自动调整限流策略,防止系统因资源耗尽而崩溃。 4. **规则管理**:支持通过代码、配置文件、**Sentinel 控制台**或结合 **Nacos**、**Apollo**、**Redis** 等持久化存储方式动态管理规则。 5. **监控与报警**:通过 **Sentinel 控制台**,可以实时监控系统的流量情况,查看限流、熔断等状态,并支持动态调整规则。 ### Sentinel 的限流策略 Sentinel 提供了多种限流模式,以下是各限流策略的详细说明及适用场景: #### 1. **QPS(每秒请求数)限流** - **原理**:按每秒钟处理的请求数量进行限流,当超过设定的 QPS 阈值时,后续的请求将被限流处理。 - **应用场景**:适用于接口级别的流量控制,避免单个接口在短时间内被过度调用导致系统过载。 **示例**: ```java FlowRule rule = new FlowRule(); rule.setResource("myAPI"); rule.setGrade(RuleConstant.FLOW_GRADE_QPS); // 按QPS限流 rule.setCount(20); // 每秒最多处理20个请求 FlowRuleManager.loadRules(Collections.singletonList(rule)); ``` #### 2. **并发数限流** - **原理**:限制系统中同时处理的并发请求数,超过限制时,后续的请求将被限流。 - **应用场景**:适用于高计算量或高耗时任务的场景,防止大量并发请求占用过多系统资源。 **示例**: ```java FlowRule rule = new FlowRule(); rule.setResource("complexTask"); rule.setGrade(RuleConstant.FLOW_GRADE_THREAD); // 按并发线程数限流 rule.setCount(10); // 同时最多允许10个线程处理该任务 FlowRuleManager.loadRules(Collections.singletonList(rule)); ``` #### 3. **预热模式限流** - **原理**:系统在冷启动阶段,逐步增加处理请求的能力,避免系统在瞬间受到大量请求时负载过重。预热期后,限流策略逐步接近设定的最大 QPS。 - **应用场景**:适用于有流量突发需求的场景,比如系统重启或流量突然增长的情况下。 **示例**: ```java FlowRule rule = new FlowRule(); rule.setResource("dataLoad"); rule.setGrade(RuleConstant.FLOW_GRADE_QPS); rule.setCount(100); // 最大处理能力为每秒100个请求 rule.setWarmUpPeriodSec(10); // 预热时间为10秒 FlowRuleManager.loadRules(Collections.singletonList(rule)); ``` #### 4. **排队等待(基于漏桶原理)** - **原理**:通过设置固定的处理速率,允许一定数量的请求进入排队等待。当队列达到上限或请求等待时间超过设置的阈值时,后续的请求会被拒绝。 - **应用场景**:适用于需要保证处理速率的场景,例如一些需要精确控制吞吐量的场景,如支付系统、订单处理。 **示例**: ```java FlowRule rule = new FlowRule(); rule.setResource("orderProcessing"); rule.setGrade(RuleConstant.FLOW_GRADE_QPS); rule.setCount(50); // 每秒最多处理50个请求 rule.setControlBehavior(RuleConstant.CONTROL_BEHAVIOR_RATE_LIMITER); rule.setMaxQueueingTimeMs(500); // 最大排队等待时间为500毫秒 FlowRuleManager.loadRules(Collections.singletonList(rule)); ``` #### 5. **集群限流** - **原理**:在分布式系统中,多个服务节点共享限流规则和状态,通过集群模式协调限流。集群限流依赖 Sentinel 的集群模式,不需要依赖 Redis 等外部存储。 - **应用场景**:适用于分布式环境下的全局限流,确保多个节点共享流量控制,防止单个节点的限流策略失效。 **示例**: ```java ClusterFlowRule rule = new ClusterFlowRule(); rule.setResource("sharedResource"); rule.setGrade(RuleConstant.FLOW_GRADE_QPS); rule.setCount(200); // 集群限流,总体QPS限制为200 rule.setClusterMode(true); // 开启集群限流 ClusterFlowRuleManager.loadRules(Collections.singletonList(rule)); ``` ### Sentinel 控制台的动态调整与监控 1. **动态规则调整**:Sentinel 控制台允许在运行时动态调整限流、熔断、降级等规则。通过控制台,开发者可以实时修改限流阈值,而无需重启应用,规则会立即生效。 2. **实时监控**:控制台提供了对各个服务的实时监控,显示每个资源的 QPS、并发数、响应时间、限流次数等关键指标,帮助开发者了解系统流量状态,及时调整策略。 3. **规则持久化**:通过控制台可以将规则持久化到 **Nacos**、**Apollo**、**Redis** 等配置中心,实现规则的集中管理和动态推送。 #### Sentinel 控制台使用示例: - 启动 Sentinel 控制台: ```bash java -Dserver.port=8080 -Dcsp.sentinel.dashboard.server=localhost:8080 -jar sentinel-dashboard.jar ``` - 访问 `http://localhost:8080` 进行限流规则的配置和监控。 ### 结合 Redis 或 Nacos 实现规则持久化 虽然 Sentinel 本身具有集群限流功能,但在一些跨数据中心、需要持久化配置的场景下,结合 Redis 或 Nacos 可以实现更高级的限流方案: 1. **Redis**:可以作为共享状态存储,用于跨数据中心共享限流状态,持久化限流计数等信息。 2. **Nacos**:作为配置中心,Nacos 可以集中管理所有微服务的限流、熔断等规则,并在应用运行时动态推送规则。 #### 示例:使用 Nacos 进行规则持久化 ```yaml sentinel: nacos: server-addr: 127.0.0.1:8848 group-id: DEFAULT_GROUP data-id: sentinel-rules ``` ### 总结 **Sentinel** 提供了多种灵活的限流技术方案,包括 QPS 限流、并发数限流、预热模式、排队等待和集群限流等,适用于不同的业务场景。通过 Sentinel 控制台,开发者可以动态调整限流策略,并通过 Redis 或 Nacos 实现分布式系统的规则持久化管理。 Sentinel 的优势在于其 **动态调整能力、强大的监控功能** 以及支持分布式限流和熔断的 **集群模式**,能够确保系统在高并发环境下的稳定性和可用性。编辑于 2024-06-20 12:12 •IP属地 美国举报评论收藏
勇敢一点吧!我们不会比今年更年轻了。洪九寒限流是保证系统稳定性的重要手段,尤其在高并发场景中,限流可以有效防止系统过载,提升系统的可用性。以下是几种常见的限流技术方案及其对应的成熟框架,尤其是阿里巴巴在此领域提供的解决方案: ### 1. **计数器限流 (Counter Limiting)** - **原理**:通过计数器记录每个时间窗口内请求的数量,超过设定的阈值时进行限流。通常会使用 Redis 或本地内存来实现。 - **优点**:实现简单,适合短时间内并发请求不高的场景。 - **缺点**:如果在一个时间窗口内突发大量请求,容易导致限流不均匀。 - **应用场景**:适用于控制在固定时间窗口内的请求次数。 - **阿里框架**:可以使用 **Redis** 实现计数器限流方案,阿里的 **Tair**(类 Redis 的分布式缓存)也可用于这种限流方式。 ### 2. **漏桶算法 (Leaky Bucket)** - **原理**:将请求流入“漏桶”,请求按照恒定速率从桶中流出,桶满时额外的请求会被丢弃。该算法可以将请求速率平滑化。 - **优点**:可以平滑突发流量,保证请求速率恒定。 - **缺点**:突发请求被直接丢弃,可能会影响用户体验。 - **应用场景**:适用于控制整体请求速率恒定的场景,如带宽控制、请求处理速率控制等。 - **阿里框架**:可以结合 **Guava** 提供的 `RateLimiter` 实现漏桶算法,也可以通过 **Sentinel** 进行基于漏桶算法的限流。 ### 3. **令牌桶算法 (Token Bucket)** - **原理**:系统按照固定速率向桶中添加令牌,每个请求需要从桶中获取令牌才能被处理,当桶中没有令牌时请求会被限流或阻塞。 - **优点**:允许一定程度的突发请求,同时控制总体速率。 - **缺点**:需要额外的逻辑来维护令牌生成和分发。 - **应用场景**:适合需要应对突发流量并控制整体请求速率的场景。 - **阿里框架**: - **Sentinel**:阿里巴巴开源的流量控制框架,支持多种限流策略,包括基于令牌桶算法的限流。 - **Guava RateLimiter**:Google 提供的限流工具,也采用令牌桶算法。 ### 4. **滑动窗口计数 (Sliding Window)** - **原理**:将时间划分为多个小的窗口段,在每个窗口内记录请求的数量。通过滑动窗口可以更加精确地控制请求量,防止短时间内的流量突发。 - **优点**:限流更加精准,适合细粒度流量控制。 - **缺点**:实现相对复杂,特别是在分布式环境中。 - **应用场景**:需要平滑处理流量波动的场景,特别是对瞬时突发流量有控制要求的场景。 - **阿里框架**:可以在 **Sentinel** 中自定义限流规则,使用滑动窗口计数器来限流。 ### 5. **分布式限流** - **原理**:在分布式系统中,每个服务实例需要共享限流状态。通常可以使用 Redis、ZooKeeper 或其他分布式存储来实现全局限流。 - **优点**:适用于微服务和分布式架构,确保系统整体流量可控。 - **缺点**:需要外部存储来同步限流状态,性能上可能存在一定延迟。 - **应用场景**:适用于多实例、多节点的分布式系统。 - **阿里框架**: - **Sentinel + Redis**:可以通过 Redis 实现分布式限流,Sentinel 本身支持集群模式限流。 - **Nacos**:阿里巴巴的配置管理框架 Nacos 与 Sentinel 结合时可以用作限流的配置中心。 ### 6. **漏斗限流 (Funnel Limiting)** - **原理**:类似于漏斗,限流器将请求放入一个漏斗中,按照一定速率处理请求,超过容量的请求将被丢弃或排队处理。 - **优点**:对突发请求有一定容忍度。 - **缺点**:需要处理队列管理和请求丢弃逻辑。 - **应用场景**:适用于需要一定程度突发处理的场景。 - **阿里框架**:可以通过 Sentinel 自定义限流策略来实现类似漏斗的行为。 ### 7. **基于服务网关的限流** - **原理**:在 API 网关层面进行限流,可以对外部请求进行全局控制,不需要在各个服务中单独实现限流。 - **优点**:统一控制外部请求,方便维护。 - **缺点**:可能会对网关性能带来额外负担。 - **应用场景**:适合分布式系统中通过网关控制外部请求的流量。 - **阿里框架**: - **Spring Cloud Gateway + Sentinel**:阿里巴巴提供的 Sentinel 可以与 Spring Cloud Gateway 集成,在网关层实现流量控制。 - **Nginx + Lua 限流**:通过在 Nginx 中使用 Lua 脚本进行限流。 ### 8. **熔断限流 (Circuit Breaking)** - **原理**:当系统压力过大或依赖的下游服务响应异常时,通过熔断机制主动拒绝部分请求,确保系统核心功能不受影响。 - **优点**:保护系统核心服务,避免全局崩溃。 - **缺点**:熔断后的恢复策略需要妥善设计。 - **应用场景**:适用于高并发场景下保护核心服务,防止过载导致系统不可用。 - **阿里框架**:**Sentinel** 提供了熔断功能,可以对服务调用链中的依赖服务设置熔断规则。 ### 总结: 在限流技术方案中,阿里巴巴开源的 **Sentinel** 是一个非常成熟的限流、熔断和降级框架,能够支持多种限流策略,并且具有分布式限流的能力。此外,结合 **Redis**、**Nacos** 等阿里生态系统中的其他工具,可以实现更加灵活的分布式限流解决方案。编辑于 2024-03-14 13:07 •IP属地 美国举报评论收藏
勇敢一点吧!我们不会比今年更年轻了。澜夜师兄### 1.计数器(固定窗口限流+滑动窗口限流) 固定窗口限流: 固定窗口算法指每个单位时间相对隔离,一个单位区间的请求量统计跟其他单位区间的请求量统计完全独立。当一个单位时间过期,自动进入下一个时间阶段重新进行计数,固定窗口计数器算法逻辑图如下,固定窗口计数器算法相对简单,但会存在临界问题(用户流量并不会像我们所期望的匀速请求,而是可能在某个时间点集中爆发,在一个窗口快结束的时候来了大量的请求,然后消耗完这个窗口的次数,然后在下个窗口刚开始又来了大量的请求,消耗完这个窗口的次数,这样在这个很短的时间间隔内,处理的请求数会超过>单个窗口的次数限制)  ### 2.滑动窗口限流: 为了解决固定窗口算法的临界问题,有了滑动窗口限流,这种算法每次统计当前时间往前推一个单位的时间,统计这个单位时间内的请求量,是否超出阀值。(可以使用Redis中的有序集合sorted set来实现,就是每个member存储请求的信息,member的score就是请求发生的时间戳,每次接受请求时,先去查询有序集合的大小,如果数量超出,就删除时间戳过期的(也就是超出当前时间窗口的请求),删除后还是超出,就限流,否则就不限流,并且有一个定时任务定时删除时间戳过期的。) 但是由于每次都需要统计单位时间的请求量,开销远大于固定窗口算法,所以在真实的业务环境中需要慎重使用滑动窗口算法。   ### 3.令牌桶算法: 令牌以固定速率产生,并缓存到令牌桶中,令牌桶放满时,多余的令牌被丢弃。请求要获取令牌才能被处理,获取不到令牌时,请求被缓存。(Guava框架中的RateLimiter类就是令牌桶算法的解决方案) ### 4.漏桶算法: 可以认为这种算法有一定固定长度的队列,来缓存请求,消费端每次以固定的速率从队列取出请求进行处理,当队列满了时,请求就只能丢弃。 ### 区别: 漏桶算法和令牌桶算法都是限制数据的平均传输速率,区别在于,令牌桶算法能够在限制数据的平均传输速率的同时还允许某种程度的突发传输。 就是假设漏桶每秒钟处理10个请求,使用漏桶法,我们设置桶的大小为10,流出的速率为0.1s流出一个请求,也就是每0.1s能处理一个请求,在第一个0.1s内只处理了1个请求,后面的9个请求都在桶内等待,每过0.1s,处理一个。 而使用令牌桶的话,我们会设置每1s向桶中放入10个令牌,假设之前没有请求,桶内有10个令牌,那么来10个请求都可以获得令牌,然后直接进行处理,来第11个请求,如果桶内没有令牌才需要等待。 Nginx官方版本限制IP的连接和并发分别有两个模块: #### 1.限制请求数 limit_req_zone 用来限制单位时间内的请求数,即速率限制,默认采用的漏桶算法 ,也就是超出限制的请求会丢弃,如果设置了burst就会变成令牌桶算法,使用一个长度为burst的队列来存储这些超出限制的请求。(nginx使用的漏桶算法) 例子: 限制访问速度 ```java http { limit_req_zone $binary_remote_addr zone=one:10m rate=1r/s; server { location /search/ { limit_req zone=one burst=5 nodelay; } } ``` 这个例子中主要是限制每个IP请求的次数, limit_req_zone中的参数 1. 第一个参数:$binary_remote_addr 表示通过用户请求的ip地址这个标识来做限制,使用binary_remote_addr 而不是remote_addr目的是使用二进制格式的ip地址,可以缩小内存占用量。 2. 第二个参数:zone=one:10m表示生成一个大小为10M,名字为one的内存区域,用来存储访问的频次信息。 3. 第三个参数:rate=1r/s表示允许相同标识的客户端的访问频次,这里限制的是每秒1次只允许请求一次,还可以有比如30r/m的。 limit_req_zone中的参数 1. 第二个参数:burst=5,burst是爆发的意思,这个配置的意思是设置一个大小为5的缓冲区当有大量请求(爆发)过来时,超过了访问频次限制的请求可以先放到这个缓冲区内先进行处理,处理完之后后面的请求还是需要等待生成令牌的速度跟上了之后才能处理。 2. 第三个参数:nodelay,如果没有设置,那么超出缓存区的所有请求会进行排队等待等待,如果设置nodelay,超过访问频次而且缓冲区也满了的时候就会直接返回503。 默认情况下,当某个客户端超过它的限流,NGINX用503(Service Temporarily Unavailable状态码来响应。使用limit_req_status\指令设置一个不同的状态码,例如limit_req_status 444; ### 实战案例 ```java limit_req_zone $binary_remote_addr zone=mylimiter:10M rate=2r/s; server { location /search { limit_req_zone=mylimiter; } } ``` 这个例子是限制同一ip地址请求/search接口时,限制每秒转发2个请求,也就是500ms转发一个。如果瞬间同一ip来了6个请求,只有第一个请求会成功,后面5个请求会被拒绝,因为nginx的限流统计是基于毫秒的,设置的速度是2r/s,转换一下就是500ms内单个IP只允许通过1个请求,从501ms开始才允许通过第二个请求。(此时也是默认的漏桶的算法) ```java limit_req_zone $binary_remote_addr zone=mylimiter:10M rate:2r/s; server { location /search { limit_req_zone=mylimiter burst=4; } } ``` 多了一个burst参数,可以认为burst是一个缓冲队列,可以将多余的请求缓存请求,这些请求也不会立即处理,只能等着慢慢被处理。假设同一ip来了6个请求,第一个请求会被立即处理,然后会有4个请求被缓存在队列中,然后每0.5s会从队列中取出一个进行处理。而最后一个请求,由于队列满了,是会被拒绝,返回503。 ```java limit_req_zone $binary_remote_addr zone=mylimiter:10M rate:2r/s; server { location /search { limit_req_zone=mylimiter burst=4 nodelay; } } ``` 多了一个nodelay参数,就是快速转发的意思,不会增加每秒能处理的请求数,但是可以让处于burst队列中请求,就会立即被后台worker处理。假设同一ip来了6个请求,那么第一个请求会被立即处理,然后会有4个请求被缓存在队列中,缓存队列中的请求也是立即被转发处理,第六个请求则是由于队列满了,被拒绝,返回503。 #### 2.限制并发连接数 Nginx 的 ngx_http_limit_conn_module模块提供了对资源连接数进行限制的功能。 例如:一次只允许每个IP地址一个连接。 ```java limit_conn_zone $binary_remote_addr zone=addr:10m; server { location /download/ { limit_conn addr 1; } } ``` ### Guava实现限流 guava的RateLimiter主要使用令牌桶算法实现限流,可以设置一个令牌生产速率来生产令牌,然后调用acquire()来获得令牌,当调用时桶内令牌不够可以进行预支,并且下一个请求调用acquire()方法时需要等待令牌还完之后才能获得令牌。 ```java public static void main(String[] args) { RateLimiter limiter = RateLimiter.create(1); for(int i = 1; i < 10; i = i + 1 ) { double waitTime = limiter.acquire(10); System.out.println("cutTime=" + System.currentTimeMillis() + " acq:" + i + " waitTime:" + waitTime); } } ``` 例如每秒生产1个令牌,第一次一次性获得10个令牌,等待时间是0s,后面下一次获得令牌时需要将10个令牌全部还完才能获得令牌,也就是需要等待10s左右。 输出如下: ``` cutTime=1591437767990 acq:1 waitTime:0.0 cutTime=1591437777994 acq:2 waitTime:9.99607 cutTime=1591437787991 acq:3 waitTime:9.994006 ``` ### RateLimiter深度使用 使用RateLimiter完成简单的大流量限流,抢购秒杀限流。 RateLimiter是guava提供的基于令牌桶算法的实现类,可以非常简单的完成限流特技,并且根据系统的实际情况来调整生成token的速率。 通常可应用于抢购限流防止冲垮系统;限制某接口、服务单位时间内的访问量,譬如一些第三方服务会对用户访问量进行限制;限制网速,单位时间内只允许上传下载多少字节等。 下面来看一些简单的实践demo,需要先引入guava的maven依赖。 Demo1.有很多任务,但希望每秒不超过N个 ```java /** * * 有很多个任务,但希望每秒不超过X个,可用此类 */ public class Demo1 { public static void main(String[] args) { //0.5代表一秒最多多少个 RateLimiter rateLimiter = RateLimiter.create(0.5); List<Runnable> tasks = new ArrayList<Runnable>(); for (int i = 0; i < 10; i++) { tasks.add(new UserRequest(i)); } ExecutorService threadPool = Executors.newCachedThreadPool(); for (Runnable runnable : tasks) { System.out.println("等待时间:" + rateLimiter.acquire()); threadPool.execute(runnable); } } private static class UserRequest implements Runnable { private int id; public UserRequest(int id) { this.id = id; } public void run() { System.out.println(id); } } } ``` Demo2.抢购场景限流 如我们预估数据库能承受并发10,超过了可能会造成故障,我们就可以对该请求接口进行限流。 ```java @RestController public class IndexController { @Resource(name = "db") private GoodInfoService goodInfoService; RateLimiter rateLimiter = RateLimiter.create(10); @RequestMapping("/miaosha") public Object miaosha(int count, String code) { System.out.println("等待时间" + rateLimiter.acquire()); if (goodInfoService.update(code, count) > 0) { return "购买成功"; } return "购买失败"; } @RequestMapping("/add") public Object add() { for (int i = 0; i < 100; i++) { GoodInfo goodInfo = new GoodInfo(); goodInfo.setCode("iphone" + i); goodInfo.setAmount(100); goodInfoService.add(goodInfo); } return "添加成功"; } } ``` Demo3.抢购场景降级 ```java /** * tryAcquire(long timeout, TimeUnit unit) * 从RateLimiter 获取许可如果该许可可以在不超过timeout的时间内获取得到的话, * 或者如果无法在timeout 过期之前获取得到许可的话,那么立即返回false(无需等待) */ @RequestMapping("/buy") public Object miao(int count, String code) { //判断能否在1秒内得到令牌,如果不能则立即返回false,不会阻塞程序 if (!rateLimiter.tryAcquire(1000, TimeUnit.MILLISECONDS)) { System.out.println("短期无法获取令牌,真不幸,排队也瞎排"); return "失败"; } if (goodInfoService.update(code, count) > 0) { System.out.println("购买成功"); return "成功"; } System.out.println("数据不足,失败"); return "失败"; } ``` ### rediscell的使用 Redis 4.0 版本中提供的 Redis-Cell 模块,该模块使用的是漏斗算法,并且提供了原子的限流指令,而且依靠 Redis 这个天生的分布式程序就可以实现比较完美的限流了。Redis-Cell 实现限流的方法也很简单,只需要使用一条指令 cl.throttle 即可,使用示例如下: ```java > cl.throttle mylimit 15 30 60 1)(integer)0 # 0 表示获取成功,1 表示拒绝 2)(integer)15 # 漏斗容量 3)(integer)14 # 漏斗剩余容量 4)(integer)-1 # 被拒绝之后,多长时间之后再试(单位:秒)-1 表示无需重试 5)(integer)2 # 多久之后漏斗完全空出来 ```发布于 2022-02-27 09:43 •IP属地 天津举报评论收藏
公众号进入网站体验更佳

