是否有办法在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);
}
}
My Throttle class:
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;
}
}
}