C# 如何限制每个客户端的WCF服务
我正在开发一项服务,它将在互联网上仅向少数选定的客户公开。但是,我不希望一个客户端能够频繁调用该服务,从而阻止另一个客户端调用该服务,或在合理的时间内得到响应。我意识到WCF内置了许多节流配置设置,但我知道这些仅适用于整个服务 是否有任何内置机制使我能够配置服务,使单个客户端只能执行(例如)10个并发调用或类似操作 这个问题与这里的另一个问题有关:C# 如何限制每个客户端的WCF服务,c#,wcf,C#,Wcf,我正在开发一项服务,它将在互联网上仅向少数选定的客户公开。但是,我不希望一个客户端能够频繁调用该服务,从而阻止另一个客户端调用该服务,或在合理的时间内得到响应。我意识到WCF内置了许多节流配置设置,但我知道这些仅适用于整个服务 是否有任何内置机制使我能够配置服务,使单个客户端只能执行(例如)10个并发调用或类似操作 这个问题与这里的另一个问题有关: 我仍在努力确定是否需要,以及如何识别个人客户 您在这里寻找的短语是。而且,没有内置的方式来限制WCF服务的费率。正如您所说,您可以随意使用WCF功
我仍在努力确定是否需要,以及如何识别个人客户 您在这里寻找的短语是。而且,没有内置的方式来限制WCF服务的费率。正如您所说,您可以随意使用WCF功能集,但这是一个服务级别设置,而不是每个客户端 为了实现速率限制,一般的指导似乎是使用内存中的集合(或类似于redis的扩展场景)对传入的用户字符串或IP地址执行快速查找。然后,您可以围绕该信息定义一些限制算法
更多信息和。您可以如下更改配置:
ConcurrencyMode:=ConcurrencyMode.Single
InstanceContextMode:=InstanceContextMode.Single
然后,在代码中设置两个服务级别变量:
- 一个字符串变量,用于保存最后一个请求者的ID
- 访问次数的一个整数变量李>
它不是一个配置解决方案-它是配置和代码,但它可以工作 首先,如果您使用某种负载平衡器,最好在那里实现它。例如,NGINX具有速率限制功能: 其次,您应该考虑使用内置的IIS速率限制功能,称为动态IP限制:./P> 如果这两个都不够,因为您需要自定义逻辑,那么您可以始终在应用程序级别实现它。这可以通过多种方式实现 让我们从一些可重用的速率限制逻辑开始:
public interface IRateLimiter
{
bool ShouldLimit(string key);
HttpStatusCode LimitStatusCode { get; }
}
public interface IRateLimiterConfiguration
{
int Treshhold { get; set; }
TimeSpan TimePeriod { get; set; }
HttpStatusCode LimitStatusCode { get; set; }
}
public class RateLimiterConfiguration : System.Configuration.ConfigurationSection, IRateLimiterConfiguration
{
private const string TimePeriodConst = "timePeriod";
private const string LimitStatusCodeConst = "limitStatusCode";
private const string TreshholdConst = "treshhold";
private const string RateLimiterTypeConst = "rateLimiterType";
[ConfigurationProperty(TreshholdConst, IsRequired = true, DefaultValue = 10)]
public int Treshhold
{
get { return (int)this[TreshholdConst]; }
set { this[TreshholdConst] = value; }
}
[ConfigurationProperty(TimePeriodConst, IsRequired = true)]
[TypeConverter(typeof(TimeSpanConverter))]
public TimeSpan TimePeriod
{
get { return (TimeSpan)this[TimePeriodConst]; }
set { this[TimePeriodConst] = value; }
}
[ConfigurationProperty(LimitStatusCodeConst, IsRequired = false, DefaultValue = HttpStatusCode.Forbidden)]
public HttpStatusCode LimitStatusCode
{
get { return (HttpStatusCode)this[LimitStatusCodeConst]; }
set { this[LimitStatusCodeConst] = value; }
}
[ConfigurationProperty(RateLimiterTypeConst, IsRequired = true)]
[TypeConverter(typeof(TypeNameConverter))]
public Type RateLimiterType
{
get { return (Type)this[RateLimiterTypeConst]; }
set { this[RateLimiterTypeConst] = value; }
}
}
public class RateLimiter : IRateLimiter
{
private readonly IRateLimiterConfiguration _configuration;
private static readonly MemoryCache MemoryCache = MemoryCache.Default;
public RateLimiter(IRateLimiterConfiguration configuration)
{
_configuration = configuration;
}
public virtual bool ShouldLimit(string key)
{
if (!string.IsNullOrEmpty(key))
{
Counter counter = new Counter {Count = 1};
counter = MemoryCache.AddOrGetExisting(key, new Counter { Count = 1 }, DateTimeOffset.Now.Add(_configuration.TimePeriod)) as Counter ?? counter;
lock (counter.LockObject)
{
if (counter.Count < _configuration.Treshhold)
{
counter.Count++;
}
else
{
return true;
}
}
}
return false;
}
public HttpStatusCode LimitStatusCode
{
get { return _configuration.LimitStatusCode; }
}
private class Counter
{
public volatile int Count;
public readonly object LockObject = new object();
}
}
public class RateLimiterFactory
{
public IRateLimiter CreateRateLimiter()
{
var configuration = GetConfiguration();
return (IRateLimiter)Activator.CreateInstance(configuration.RateLimiterType, configuration);
}
public static RateLimiterConfiguration GetConfiguration()
{
return ConfigurationManager.GetSection("rateLimiter") as RateLimiterConfiguration ?? new RateLimiterConfiguration();
}
}
static class GetClientIpExtensions
{
private const string XForwardedForHeaderName = "X-Forwarded-For";
private const string HttpXForwardedForServerVariableName = "HTTP_X_FORWARDED_FOR";
private const string HttpRemoteAddressServerVariableName = "REMOTE_ADDR";
public static string GetClientIp(this Message message)
{
return GetClientIp(message.Properties);
}
public static string GetClientIp(this OperationContext context)
{
return GetClientIp(context.IncomingMessageProperties);
}
public static string GetClientIp(this MessageProperties messageProperties)
{
var endpointLoadBalancer = messageProperties[HttpRequestMessageProperty.Name] as HttpRequestMessageProperty;
if (endpointLoadBalancer != null && endpointLoadBalancer.Headers[XForwardedForHeaderName] != null)
{
return endpointLoadBalancer.Headers[XForwardedForHeaderName];
}
else
{
var endpointProperty = messageProperties[RemoteEndpointMessageProperty.Name] as RemoteEndpointMessageProperty;
return (endpointProperty == null) ? string.Empty : endpointProperty.Address;
}
}
public static string GetClientIp(this HttpRequest request)
{
string ipList = request.ServerVariables[HttpXForwardedForServerVariableName];
return !string.IsNullOrEmpty(ipList) ? ipList.Split(',')[0] : request.ServerVariables[HttpRemoteAddressServerVariableName];
}
}
或仅适用于任何传输级别的WCF实现:
public class RateLimiterDispatchMessageInspector : IDispatchMessageInspector
{
private readonly IRateLimiter _rateLimiter;
public RateLimiterDispatchMessageInspector(IRateLimiter rateLimiter)
{
_rateLimiter = rateLimiter;
}
public object AfterReceiveRequest(ref Message request, IClientChannel channel, InstanceContext instanceContext)
{
if (_rateLimiter.ShouldLimit(request.GetClientIp()))
{
request = null;
return _rateLimiter.LimitStatusCode;
}
return null;
}
public void BeforeSendReply(ref Message reply, object correlationState)
{
if (correlationState is HttpStatusCode)
{
HttpResponseMessageProperty responseProperty = new HttpResponseMessageProperty();
reply.Properties["httpResponse"] = responseProperty;
responseProperty.StatusCode = (HttpStatusCode)correlationState;
}
}
}
public class RateLimiterServiceBehavior : IServiceBehavior
{
public void Validate(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase) { }
public void AddBindingParameters(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase, Collection<ServiceEndpoint> endpoints, BindingParameterCollection bindingParameters) { }
public void ApplyDispatchBehavior(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
{
var rateLimiterFactory = new RateLimiterFactory();
foreach (ChannelDispatcher chDisp in serviceHostBase.ChannelDispatchers)
{
foreach (EndpointDispatcher epDisp in chDisp.Endpoints)
{
epDisp.DispatchRuntime.MessageInspectors.Add(new RateLimiterDispatchMessageInspector(rateLimiterFactory.CreateRateLimiter()));
}
}
}
}
public class RateLimiterBehaviorExtensionElement : BehaviorExtensionElement
{
protected override object CreateBehavior()
{
return new RateLimiterServiceBehavior();
}
public override Type BehaviorType
{
get { return typeof(RateLimiterServiceBehavior); }
}
}
公共类RateLimiterDispatchMessageInspector:IDispatchMessageInspector
{
私有只读IRateLimiter;
公共费率限制器DispatchMessageInspector(IRateLimiter费率限制器)
{
_比率限制器=比率限制器;
}
接收请求后的公共对象(ref消息请求、IClientChannel通道、InstanceContext InstanceContext)
{
if(_rateLimiter.ShouldLimit(request.GetClientIp()))
{
请求=null;
返回_rateLimiter.LimitStatusCode;
}
返回null;
}
SendReply之前的公共无效(参考消息回复,对象关联状态)
{
if(correlationState为HttpStatusCode)
{
HttpResponseMessageProperty responseProperty=新的HttpResponseMessageProperty();
回复.属性[“httpResponse”]=响应属性;
responseProperty.StatusCode=(HttpStatusCode)correlationState;
}
}
}
公共类费率限制服务行为:IServiceBehavior
{
公共无效验证(ServiceDescription ServiceDescription,ServiceHostBase ServiceHostBase){}
public void AddBindingParameters(ServiceDescription ServiceDescription,ServiceHostBase ServiceHostBase,集合终结点,BindingParameterCollection bindingParameters){}
公共无效ApplyDispatchBehavior(ServiceDescription ServiceDescription,ServiceHostBase ServiceHostBase)
{
var rateLimiterFactory=新rateLimiterFactory();
foreach(serviceHostBase.ChannelDispatchers中的ChannelDispatcher chDisp)
{
foreach(chDisp.Endpoints中的EndpointDispatcher epDisp)
{
添加(新的RateLimiterDispatchMessageInspector(rateLimiterFactory.CreateRateLimiter());
}
}
}
}
公共类RateLimiterBehaviorExtensionElement:BehaviorExtensionElement
{
受保护的重写对象CreateBehavior()
{
返回新的RateLimiterServiceBehavior();
}
公共重写类型BehaviorType
{
获取{返回类型(RateLimiterServiceBehavior);}
}
}
您也可以为ASP.NET MCV执行类似的操作筛选器。查看这里:。我认为没有一种可配置的方法来实现您想要的。您可以在wcf中获取客户端ip地址,并阻止特定ip超过呼叫限制。一次只执行一个呼叫的服务的单个实例?!这是我见过的“一个客户端能够阻止另一个客户端调用服务”的最佳实现。是的,但它不会阻止用户在一秒钟后返回1/1000的另一个请求。基本上,它只是对请求进行排队。您可以为每个客户端托管一个单例。当您获得一个新客户机时,只需托管另一个服务实例。大规模扩展战略。等等……从代码审查的角度来看,
TerminateRequest
是否有点暴力。等待Task.Delay
不是更有意义吗?Task.Delay将实现什么?它将“延迟”响应,从而在不终止请求的情况下进行排序。当然,您需要实现某种并发检查/排队逻辑……但这似乎是限制速率的正确方法。此外,您的TerminateRequest
会鼓励用户重复发出请求,这将消耗更多的资源…这就是限速的全部意义…其他标准限速器,如nginx或ISS,只是终止请求。s
public class RateLimiterDispatchMessageInspector : IDispatchMessageInspector
{
private readonly IRateLimiter _rateLimiter;
public RateLimiterDispatchMessageInspector(IRateLimiter rateLimiter)
{
_rateLimiter = rateLimiter;
}
public object AfterReceiveRequest(ref Message request, IClientChannel channel, InstanceContext instanceContext)
{
if (_rateLimiter.ShouldLimit(request.GetClientIp()))
{
request = null;
return _rateLimiter.LimitStatusCode;
}
return null;
}
public void BeforeSendReply(ref Message reply, object correlationState)
{
if (correlationState is HttpStatusCode)
{
HttpResponseMessageProperty responseProperty = new HttpResponseMessageProperty();
reply.Properties["httpResponse"] = responseProperty;
responseProperty.StatusCode = (HttpStatusCode)correlationState;
}
}
}
public class RateLimiterServiceBehavior : IServiceBehavior
{
public void Validate(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase) { }
public void AddBindingParameters(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase, Collection<ServiceEndpoint> endpoints, BindingParameterCollection bindingParameters) { }
public void ApplyDispatchBehavior(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
{
var rateLimiterFactory = new RateLimiterFactory();
foreach (ChannelDispatcher chDisp in serviceHostBase.ChannelDispatchers)
{
foreach (EndpointDispatcher epDisp in chDisp.Endpoints)
{
epDisp.DispatchRuntime.MessageInspectors.Add(new RateLimiterDispatchMessageInspector(rateLimiterFactory.CreateRateLimiter()));
}
}
}
}
public class RateLimiterBehaviorExtensionElement : BehaviorExtensionElement
{
protected override object CreateBehavior()
{
return new RateLimiterServiceBehavior();
}
public override Type BehaviorType
{
get { return typeof(RateLimiterServiceBehavior); }
}
}