c# - gRPC - 限制客户端请求(限制)
问题描述
有没有办法在 gRPC 一元调用中添加节流阀?我的目标是将每个用户限制为每秒 10 个请求。
在我的研究中,我发现(也许)它将被放置在一个继承 Interceptor 的类中,如下所示:
public class LimitClientRequestsInterceptor : Interceptor
{
public override UnaryServerHandler<TRequest, TResponse>(
TRequest request,
ServerCallContext context,
UnaryServerMethod<TRequest, TResponse> continuation)
{
// Add code to limit 10 requests per second for each user.
return await base.UnaryServerHandler(request, context, continuation);
}
}
解决方案
我找到了解决问题的方法,因为 gRPC 没有用于速率限制或节流的内置方法(在 c# 中)。但是,我能够做到这一点,如下所示。
我的拦截器类:
public class LimitClientRequestsInterceptor : Interceptor
{
private readonly ThrottleGauge _gauge;
public override UnaryServerHandler<TRequest, TResponse>(
TRequest request,
ServerCallContext context,
UnaryServerMethod<TRequest, TResponse> continuation)
{
var username = context.GetHttpContext().User.GetUserNameInIdentity();
if (ThrottlingAttribute.UserHasReachedMaxRateLimit(username))
{
throw new RpcException(new Status(
StatusCode.Cancelled,
Newtonsoft.Json.JsonConvert.SerializeObject(new
{ Code = 429, Detail = $"Throttle: {username} exceeded
{_gauge.MaxMessagesPerTimeSlice} messages in {_gauge.TimeSlice}" })));
}
return await base.UnaryServerHandler(request, context, continuation);
}
}
我的油门类:
internal class ThrottlingAttribute
{
private static Dictionary<string, ThrottleGauge> _byUser;
private static TimeSpan _defaultThrottle_TimeSliceInMilliseconds = TimeSpan.FromMilliseconds(ServiceSettings.Instance.DefaultThrottle_TimeSliceInMilliseconds);
private static int _defaultThrottle_MaxMessagesPerTimeSlice = ServiceSettings.Instance.DefaultThrottle_MaxMessagesPerTimeSlice;
public static bool UserHasReachedMaxRateLimit(string username)
{
ThrottleGauge gauge;
if (!_byUser.TryGetValue(username, out gauge))
{
gauge = new ThrottleGauge(
_defaultThrottle_TimeSliceInMilliseconds,
_defaultThrottle_MaxMessagesPerTimeSlice);
_byUser[username] = gauge;
}
return gauge.WillExceedRate();
}
}
还有我的 ThrottleGauge 课程:
internal class ThrottleGauge
{
private readonly object _locker = new object();
private Queue<DateTime> _Queue = new Queue<DateTime>();
public TimeSpan TimeSlice { get; private set; }
public int MaxMessagesPerTimeSlice { get; private set; }
public ThrottleGauge(TimeSpan timeSlice, int maxMessagesPerTimeSlice)
{
TimeSlice = timeSlice;
MaxMessagesPerTimeSlice = maxMessagesPerTimeSlice;
}
// returns true if sending a message now message exceeds limit rate
public bool WillExceedRate()
{
lock (_locker)
{
var now = DateTime.Now;
if (_Queue.Count < MaxMessagesPerTimeSlice)
{
_Queue.Enqueue(now);
return false;
}
DateTime oldest = _Queue.Peek();
if ((now - oldest).TotalMilliseconds < TimeSlice.TotalMilliseconds)
return true;
_Queue.Dequeue();
_Queue.Enqueue(now);
return false;
}
}
}
推荐阅读
- android - EditText 后隐藏提示不为空
- javascript - 西蒙游戏挑战
- c - “这里首先定义的多重定义”。STM32、AC6工作室
- css - 来自 Vue 单文件组件的 CSS 覆盖了一些来自 buefy/bulma 的 CSS,而另一些则没有
- ubuntu - 允许 GitHub Actions 中断的 iptables 规则(链默认为 DROP)
- node.js - 快速添加到 Heroku 应用程序不会转发到正确的 url
- c++ - 如何使 GtkScrolledWindow 与 GtkOverlay 中的 GtkFixed 一起工作
- discord.py - 如何在 discord.py 1.5.0 中做一个囚徒角色?
- python - 在 CNN 中训练每个 epoch 期间验证准确度很高,但分类报告中的最终准确度很低,这是什么意思?
- html - 在引导程序 4 中以平板电脑大小截断文本的列