云原生服务的限流方案
限流的目的,是在系统流量过大的时候,对系统进行保护,避免因为流量过大,导致系统不稳定,甚至出现故障。云原生环境下的限流方案比较多。 轻量级的方案可以使用 Bucket4j +Hazelcast/ignite/infinispan 的内存数据结合的方案。 完整的方案可以使用 Sentinel 集群。
限流的目的,是在系统流量过大的时候,对系统进行保护,避免因为流量过大,导致系统不稳定,甚至出现故障。
云原生环境下的限流方案比较多。 轻量级的方案可以使用 Bucket4j + Hazelcast/ignite/infinispan 的内存数据结合的方案。 完整的方案可以使用 Sentinel 集群。
1.Sentinel
使用 sentinel 进行限流,可以单机限流,也可以多机集群限流。
优点:可以灵活进行控制。 有集群的方案。
缺点:需要写代码,侵入性较高。
Sentinel 的 规则在 sentinel 集群中配置。 集群配置参考:
集群流控 · alibaba/Sentinel Wiki · GitHub
限流需要结合每一个服务的实例数来做限流, 因此可以监听 k8s 的 pod 状态变化来更新限流策略。 监听 k8s 集群:
java/WatchExample.java at master · kubernetes-client/java · GitHub
Using Watch with the Kubernetes API | Baeldung
How to watch events on a kubernetes service using its go client - Stack Overflowj
监听 k8s 的实例代码:
import com.google.gson.reflect.TypeToken;
import io.kubernetes.client.openapi.ApiClient;
import io.kubernetes.client.openapi.ApiException;
import io.kubernetes.client.openapi.Configuration;
import io.kubernetes.client.openapi.apis.CoreV1Api;
import io.kubernetes.client.openapi.models.V1Namespace;
import io.kubernetes.client.util.Config;
import io.kubernetes.client.util.Watch;
import java.io.IOException;
import java.util.concurrent.TimeUnit;
import okhttp3.OkHttpClient;
/** A simple example of how to use Watch API to watch changes in Namespace list. */
public class WatchExample {
public static void main(String[] args) throws IOException, ApiException {
ApiClient client = Config.defaultClient();
// infinite timeout
OkHttpClient httpClient =
client.getHttpClient().newBuilder().readTimeout(0, TimeUnit.SECONDS).build();
client.setHttpClient(httpClient);
Configuration.setDefaultApiClient(client);
CoreV1Api api = new CoreV1Api();
Watch<V1Namespace> watch =
Watch.createWatch(
client,
api.listNamespaceCall(
null, null, null, null, null, 5, null, null, null, Boolean.TRUE, null),
new TypeToken<Watch.Response<V1Namespace>>() {}.getType());
try {
for (Watch.Response<V1Namespace> item : watch) {
System.out.printf("%s : %s%n", item.type, item.object.getMetadata().getName());
}
} finally {
watch.close();
}
}
}
2.Bucket4j 对 API 进行限流
基本使用
Rate Limiting a Spring API Using Bucket4j | Baeldung
How to set rate limit for each user in Spring Boot?
https://youssefelyamani.medium.com/secure-your-spring-boot-api-with-rate-limit-20d868148fd
<dependency> <groupId>com.github.vladimir-bukhtoyarov</groupId> <artifactId>bucket4j-core</artifactId> <version>4.10.0</version> </dependency>
示例
@RestController
class AreaCalculationController {
private final Bucket bucket;
public AreaCalculationController() {
Bandwidth limit = Bandwidth.classic(20, Refill.greedy(20, Duration.ofMinutes(1)));
this.bucket = Bucket4j.builder()
.addLimit(limit)
.build();
}
//..
}
@PostMapping(value = "/api/v1/area/rectangle")
public ResponseEntity<AreaV1> rectangle(@RequestBody RectangleDimensionsV1 dimensions) {
if (bucket.tryConsume(1)) {
return ResponseEntity.ok(new AreaV1("rectangle", dimensions.getLength() * dimensions.getWidth()));
}
return ResponseEntity.status(HttpStatus.TOO_MANY_REQUESTS).build();
}
测试验证限流的效果
$ curl -X POST http://localhost:9001/api/v1/area/rectangle \
-H "Content-Type: application/json" \
-d '{ "length": 10, "width": 12 }'
{ "shape":"rectangle","area":120.0 }
优点:
提供了 RateLimiter, 以及集群限流的方案。
缺点:需要 Hazelcast/ignite/infinispan 的内存数据结合来用做集群限流,增加了比较重的外部依赖。
集群限流方案
可以使用 Hazelcast, ignite, infinispan 做为分布式内存数据库。
Hazelcast, ignite 各有优劣。Ignite 的性能更好,开放性更好一些。
3.Resilience4j ratelimiter
Resilience4j 使用示例 & 动态调整 limit:
Rate-Limiting with Spring Boot and Resilience4j
Implementing Rate Limiting with Resilience4j
优点:提供了 RateLimiter, CircuitBreaker, Retry, BulkHead 等不同的稳定性保障方法。
缺点:没有提供集群的方案。
4.使用 Traefik 来控制流量
Rate limiting on Kubernetes applications | Traefik Blog
How to use Traefik reverse proxy
traefik-helm-chart/README.md at master · traefik/traefik-helm-chart · GitHub
优点:能够实时调整限制的阈值。 可以对整个集群限流,且能使用 qps 和 同时并发数进行限流。
缺点:一个入口API 对应到后面多个算法(根据 不同请求条件路由到不同算法服务),路由逻辑不能使用 Traefik 的 Route 规则来表达。
示例,如下设置 /prod 路径的请求平均每秒最大 100 qps, 最大并发量 50。两个参数一起可以精确控制 qps。
定义一个 rate limit 的 middleware。 然后在 IngressRoute 里面引用这个 middleware。
在 集群发生扩缩容的时候, 更新 middleware 的配置即可改变限流的策略。
apiVersion: traefik.containo.us/v1alpha1
kind: Middleware
metadata:
name: prod-rate-limit
spec:
rateLimit:
average: 100
burst: 50
--
apiVersion: traefik.containo.us/v1alpha1
kind: IngressRoute
metadata:
name: prod-route
spec:
entryPoints:
- web
routes:
- match: PathPrefix(`/prod`)
kind: Rule
services:
- name: prod-service
port: 80
middlewares:
- name: prod-rate-limit
Traefik 除了 提供限流,同时提供了 API的统计和监控 Dashboard,方便观测集群的健康度,可以根据需要开启。
5.使用 istio 限流
Istio / Traffic Management
Istio / Circuit Breaking
Istio / Destination Rule
Istio / Ingress Gateways
Istio / Istio as a Proxy for External Services
可以控制并发连接数,不能准确控制 qps。
apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule
metadata:
name: reviews
spec:
host: reviews
subsets:
- name: v1
labels:
version: v1
trafficPolicy:
connectionPool:
tcp:
maxConnections: 100
另外可以使用 Spring Cloud Zuul RateLimit 来做限流, 也可以在 nginx ingress 中也可以控制 qps 流量,灵活性比较小。 参考:
managed - Rate Limiting based on URL and Path in Kubernetes - Stack Overflow
更多推荐
所有评论(0)