【问题标题】:Restrict calls to WCF service per minute, per method, per IP限制每分钟、每方法、每 IP 对 WCF 服务的调用
【发布时间】:2015-11-29 06:27:33
【问题描述】:

我想将任何不同 IP 对 WCF 服务上特定方法的调用次数限制为每个时间范围 y x 次调用。

因此,例如,如果 IP 10.0.1.1 在特定分钟内调用方法 register 超过 5 次(称为分钟 x),则当它尝试在该分钟内第六次调用该方法时被阻止直到分钟 (x + 1)。

这是因为我系统上唯一的非令牌授权调用是 register 调用。我担心尝试用调用淹没此方法会使我的服务器在负载下挣扎。这个方法后面处理的比较多,设计的只是偶尔调用。

我已经考虑将ServiceThrottlingBehavior 添加到配置文件中,但这对于服务来说是全局的,而不是服务方法的本地。

是否有一种好的/标准化的方式来做到这一点,无论是通过编程还是在配置文件中?

【问题讨论】:

    标签: c# performance wcf configuration throttling


    【解决方案1】:

    实现此目的的一种方法是使用一个 ServiceBehavior,它添加一个实现 IInstanceContextInitializer 的实例。

    我的实现如下所示:

    public class PerOperationThrottle: IInstanceContextInitializer
    {
        static MemoryCache cache = new MemoryCache("foo", null);
    
        public void Initialize(InstanceContext instanceContext, Message message)
        {
            RemoteEndpointMessageProperty  ep = message.Properties[RemoteEndpointMessageProperty.Name] as RemoteEndpointMessageProperty;
            // which action do we want to throttle
            if (message.Headers.Action.EndsWith("register") &&
                ep != null && 
                ep.Address != null)
            {
                // get the IP address 
                var item = cache[ep.Address];
                if (item == null)
                {
                    // not found, so init
                    cache.Add(
                        ep.Address,
                        new Counter { Count = 0 },
                        new CacheItemPolicy
                        {
                            SlidingExpiration = new TimeSpan(0, 1, 0) // 1 minute
                        });
                }
                else
                {
                    // how many calls?
                    var count = (Counter)item;
                    if (count.Count > 5)
                    {
                        instanceContext.Abort();
                        // not sure if this the best way to break
                        throw new Exception("throttle");
                    }
                    // add one call
                    count.Count++;
                }
            }
        }
    }
    

    我使用了一个有点幼稚的 MemoryCache 实现,它为我的自定义 Counter 类的每个 IP 地址保存一个实例:

    public class Counter
    {
        public int Count;
    }
    

    要将PerOperationThrottle 的实例连接到服务,我有一个帮助类,它结合了IServiceBehaviorIEndpointBehavior 的实现:

    public class PerOperationThrottleBehaviorAttribute : Attribute, IServiceBehavior,IEndpointBehavior
    {
        public void ApplyDispatchBehavior(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
        {
            foreach(var ep in serviceDescription.Endpoints)
            {
                // add the EndpointBehavior
                ep.EndpointBehaviors.Add(this);
            }      
        }
    
        public void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher)
        {
            // our PerOperationThrottle gets created and wired
            endpointDispatcher.
                DispatchRuntime.
                InstanceContextInitializers.
                Add(new PerOperationThrottle());
        }
    
        public void AddBindingParameters(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase, System.Collections.ObjectModel.Collection<ServiceEndpoint> endpoints, BindingParameterCollection bindingParameters)
        {
        }
        public void AddBindingParameters(ServiceEndpoint endpoint, BindingParameterCollection bindingParameters)
        {
        }
        public void Validate(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
        {   
        }
        public void Validate(ServiceEndpoint endpoint)
        {   
        }
        public void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime)
        { 
        }
    }
    

    空方法属于接口,但不需要任何实现。不过请务必删除throw new NotImplementedException();

    最后我们用自定义属性PerOperationThrottleBehavior注解Service实现类

    [PerOperationThrottleBehavior]
    public class Service1 : IService1
    {
        public string register(int value)
        {
            return string.Format("You entered: {0}", value);
        }
    }
    

    如果register 操作在一分钟内被调用超过 5 次,服务会抛出异常。

    【讨论】:

    • “ServiceEndpoint”不包含 EndpointBehaviors 方法。你知道如何解决吗? @rene
    • @ozkank 你在 .Net 4.0 上吗?如果您愿意,请升级。
    • 是的,它在 .NET 4.0 上运行。我会试试看。你知道这个 .Net 4.0 的其他方法吗?我想我必须在这个框架上解决这个问题。:/
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2018-06-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多