每个方法,每个IP

时间:2015-09-03 12:12:09

标签: c# performance wcf configuration throttling

我希望通过任何不同的IP将我的WCF服务上的特定方法的呼叫数量限制为每个时间范围 y x 呼叫。

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

这是因为我系统上唯一的非令牌授权呼叫是register呼叫。我担心尝试使用调用泛滥此方法会使我的服务器在负载下挣扎。这种方法背后有很多处理,它只是偶尔被调用。

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

是否有一个好的/标准化的方法来执行此操作,无论是以编程方式还是在配置文件中?

1 个答案:

答案 0 :(得分:3)

执行此操作的一种方法是使用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次,则服务会抛出异常。