Warning: file_get_contents(/data/phpspider/zhask/data//catemap/7/wcf/4.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 如何限制每个客户端的WCF服务_C#_Wcf - Fatal编程技术网

C# 如何限制每个客户端的WCF服务

C# 如何限制每个客户端的WCF服务,c#,wcf,C#,Wcf,我正在开发一项服务,它将在互联网上仅向少数选定的客户公开。但是,我不希望一个客户端能够频繁调用该服务,从而阻止另一个客户端调用该服务,或在合理的时间内得到响应。我意识到WCF内置了许多节流配置设置,但我知道这些仅适用于整个服务 是否有任何内置机制使我能够配置服务,使单个客户端只能执行(例如)10个并发调用或类似操作 这个问题与这里的另一个问题有关: 我仍在努力确定是否需要,以及如何识别个人客户 您在这里寻找的短语是。而且,没有内置的方式来限制WCF服务的费率。正如您所说,您可以随意使用WCF功

我正在开发一项服务,它将在互联网上仅向少数选定的客户公开。但是,我不希望一个客户端能够频繁调用该服务,从而阻止另一个客户端调用该服务,或在合理的时间内得到响应。我意识到WCF内置了许多节流配置设置,但我知道这些仅适用于整个服务

是否有任何内置机制使我能够配置服务,使单个客户端只能执行(例如)10个并发调用或类似操作

这个问题与这里的另一个问题有关:


我仍在努力确定是否需要,以及如何识别个人客户

您在这里寻找的短语是。而且,没有内置的方式来限制WCF服务的费率。正如您所说,您可以随意使用WCF功能集,但这是一个服务级别设置,而不是每个客户端

为了实现速率限制,一般的指导似乎是使用内存中的集合(或类似于redis的扩展场景)对传入的用户字符串或IP地址执行快速查找。然后,您可以围绕该信息定义一些限制算法


更多信息和。

您可以如下更改配置:

 ConcurrencyMode:=ConcurrencyMode.Single
 InstanceContextMode:=InstanceContextMode.Single
然后,在代码中设置两个服务级别变量:

  • 一个字符串变量,用于保存最后一个请求者的ID
  • 访问次数的一个整数变量
对于每个入站用户ID==到上次保存的用户的请求,递增+1整数变量。请求10后,向用户返回拒绝。如果用户不同,请重置变量并处理请求


它不是一个配置解决方案-它是配置和代码,但它可以工作

首先,如果您使用某种负载平衡器,最好在那里实现它。例如,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); }
    }
}