e79aa3c0ed
Former-commit-id: a2155e9bd80020e49e72e86c44da02a8ac0e57a4
480 lines
17 KiB
C#
480 lines
17 KiB
C#
//-----------------------------------------------------------------------------
|
|
// Copyright (c) Microsoft Corporation. All rights reserved.
|
|
//-----------------------------------------------------------------------------
|
|
|
|
namespace System.ServiceModel.Dispatcher
|
|
{
|
|
using System;
|
|
using System.Runtime;
|
|
using System.ServiceModel;
|
|
using System.ServiceModel.Diagnostics;
|
|
using System.ServiceModel.Diagnostics.Application;
|
|
|
|
interface ISessionThrottleNotification
|
|
{
|
|
void ThrottleAcquired();
|
|
}
|
|
|
|
public sealed class ServiceThrottle
|
|
{
|
|
internal const int DefaultMaxConcurrentCalls = 16;
|
|
internal const int DefaultMaxConcurrentSessions = 100;
|
|
internal static int DefaultMaxConcurrentCallsCpuCount = DefaultMaxConcurrentCalls * OSEnvironmentHelper.ProcessorCount;
|
|
internal static int DefaultMaxConcurrentSessionsCpuCount = DefaultMaxConcurrentSessions * OSEnvironmentHelper.ProcessorCount;
|
|
|
|
FlowThrottle calls;
|
|
FlowThrottle sessions;
|
|
QuotaThrottle dynamic;
|
|
FlowThrottle instanceContexts;
|
|
|
|
ServiceHostBase host;
|
|
ServicePerformanceCountersBase servicePerformanceCounters;
|
|
bool isActive;
|
|
object thisLock = new object();
|
|
|
|
internal ServiceThrottle(ServiceHostBase host)
|
|
{
|
|
if (!((host != null)))
|
|
{
|
|
Fx.Assert("ServiceThrottle.ServiceThrottle: (host != null)");
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("host");
|
|
}
|
|
this.host = host;
|
|
this.MaxConcurrentCalls = ServiceThrottle.DefaultMaxConcurrentCallsCpuCount;
|
|
this.MaxConcurrentSessions = ServiceThrottle.DefaultMaxConcurrentSessionsCpuCount;
|
|
|
|
this.isActive = true;
|
|
}
|
|
|
|
FlowThrottle Calls
|
|
{
|
|
get
|
|
{
|
|
if (this.calls == null)
|
|
{
|
|
lock (this.ThisLock)
|
|
{
|
|
if (this.calls == null)
|
|
{
|
|
FlowThrottle callsFt = new FlowThrottle(this.GotCall, ServiceThrottle.DefaultMaxConcurrentCallsCpuCount,
|
|
ServiceThrottle.MaxConcurrentCallsPropertyName, ServiceThrottle.MaxConcurrentCallsConfigName);
|
|
|
|
callsFt.SetRatio(this.RatioCallsToken);
|
|
|
|
this.calls = callsFt;
|
|
}
|
|
}
|
|
}
|
|
|
|
return this.calls;
|
|
}
|
|
}
|
|
|
|
FlowThrottle Sessions
|
|
{
|
|
get
|
|
{
|
|
if (this.sessions == null)
|
|
{
|
|
lock (this.ThisLock)
|
|
{
|
|
if (this.sessions == null)
|
|
{
|
|
FlowThrottle sessionsFt = new FlowThrottle(this.GotSession, ServiceThrottle.DefaultMaxConcurrentSessionsCpuCount,
|
|
ServiceThrottle.MaxConcurrentSessionsPropertyName, ServiceThrottle.MaxConcurrentSessionsConfigName);
|
|
|
|
sessionsFt.SetRatio(this.RatioSessionsToken);
|
|
|
|
this.sessions = sessionsFt;
|
|
}
|
|
}
|
|
}
|
|
|
|
return this.sessions;
|
|
}
|
|
}
|
|
|
|
QuotaThrottle Dynamic
|
|
{
|
|
get
|
|
{
|
|
if (this.dynamic == null)
|
|
{
|
|
lock (this.ThisLock)
|
|
{
|
|
if (this.dynamic == null)
|
|
{
|
|
QuotaThrottle dynamicQt = new QuotaThrottle(this.GotDynamic, new object());
|
|
dynamicQt.Owner = "ServiceHost";
|
|
|
|
this.dynamic = dynamicQt;
|
|
}
|
|
}
|
|
}
|
|
|
|
this.UpdateIsActive();
|
|
return this.dynamic;
|
|
}
|
|
}
|
|
|
|
internal int ManualFlowControlLimit
|
|
{
|
|
get { return this.Dynamic.Limit; }
|
|
set { this.Dynamic.SetLimit(value); }
|
|
}
|
|
|
|
const string MaxConcurrentCallsPropertyName = "MaxConcurrentCalls";
|
|
const string MaxConcurrentCallsConfigName = "maxConcurrentCalls";
|
|
public int MaxConcurrentCalls
|
|
{
|
|
get { return this.Calls.Capacity; }
|
|
set
|
|
{
|
|
this.ThrowIfClosedOrOpened(MaxConcurrentCallsPropertyName);
|
|
this.Calls.Capacity = value;
|
|
this.UpdateIsActive();
|
|
if (null != this.servicePerformanceCounters)
|
|
{
|
|
this.servicePerformanceCounters.SetThrottleBase((int)ServicePerformanceCounters.PerfCounters.CallsPercentMaxCallsBase, this.Calls.Capacity);
|
|
}
|
|
}
|
|
}
|
|
|
|
const string MaxConcurrentSessionsPropertyName = "MaxConcurrentSessions";
|
|
const string MaxConcurrentSessionsConfigName = "maxConcurrentSessions";
|
|
public int MaxConcurrentSessions
|
|
{
|
|
get { return this.Sessions.Capacity; }
|
|
set
|
|
{
|
|
this.ThrowIfClosedOrOpened(MaxConcurrentSessionsPropertyName);
|
|
this.Sessions.Capacity = value;
|
|
this.UpdateIsActive();
|
|
if (null != this.servicePerformanceCounters)
|
|
{
|
|
this.servicePerformanceCounters.SetThrottleBase((int)ServicePerformanceCounters.PerfCounters.SessionsPercentMaxSessionsBase, this.Sessions.Capacity);
|
|
}
|
|
}
|
|
}
|
|
|
|
const string MaxConcurrentInstancesPropertyName = "MaxConcurrentInstances";
|
|
const string MaxConcurrentInstancesConfigName = "maxConcurrentInstances";
|
|
public int MaxConcurrentInstances
|
|
{
|
|
get { return this.InstanceContexts.Capacity; }
|
|
set
|
|
{
|
|
this.ThrowIfClosedOrOpened(MaxConcurrentInstancesPropertyName);
|
|
this.InstanceContexts.Capacity = value;
|
|
this.UpdateIsActive();
|
|
if (null != this.servicePerformanceCounters)
|
|
{
|
|
this.servicePerformanceCounters.SetThrottleBase((int)ServicePerformanceCounters.PerfCounters.InstancesPercentMaxInstancesBase, this.InstanceContexts.Capacity);
|
|
}
|
|
}
|
|
}
|
|
|
|
FlowThrottle InstanceContexts
|
|
{
|
|
get
|
|
{
|
|
if (this.instanceContexts == null)
|
|
{
|
|
lock (this.ThisLock)
|
|
{
|
|
if (this.instanceContexts == null)
|
|
{
|
|
FlowThrottle instanceContextsFt = new FlowThrottle(this.GotInstanceContext, Int32.MaxValue,
|
|
ServiceThrottle.MaxConcurrentInstancesPropertyName, ServiceThrottle.MaxConcurrentInstancesConfigName);
|
|
instanceContextsFt.SetRatio(this.RatioInstancesToken);
|
|
|
|
if (this.servicePerformanceCounters != null)
|
|
{
|
|
InitializeInstancePerfCounterSettings(instanceContextsFt);
|
|
}
|
|
|
|
this.instanceContexts = instanceContextsFt;
|
|
}
|
|
}
|
|
}
|
|
|
|
return this.instanceContexts;
|
|
}
|
|
}
|
|
|
|
internal bool IsActive
|
|
{
|
|
get { return this.isActive; }
|
|
}
|
|
|
|
internal object ThisLock
|
|
{
|
|
get { return this.thisLock; }
|
|
}
|
|
|
|
internal void SetServicePerformanceCounters(ServicePerformanceCountersBase counters)
|
|
{
|
|
this.servicePerformanceCounters = counters;
|
|
//instance throttle is created through the behavior, set the perf counter callbacks if initialized
|
|
if (this.instanceContexts != null)
|
|
{
|
|
InitializeInstancePerfCounterSettings(this.instanceContexts);
|
|
}
|
|
|
|
//this.calls and this.sessions throttles are created by the constructor. Set the perf counter callbacks
|
|
InitializeCallsPerfCounterSettings();
|
|
InitializeSessionsPerfCounterSettings();
|
|
}
|
|
|
|
void InitializeInstancePerfCounterSettings(FlowThrottle instanceContextsFt)
|
|
{
|
|
Fx.Assert(instanceContextsFt != null, "Expect instanceContext to be initialized");
|
|
Fx.Assert(this.servicePerformanceCounters != null, "expect servicePerformanceCounters to be set");
|
|
instanceContextsFt.SetAcquired(this.AcquiredInstancesToken);
|
|
instanceContextsFt.SetReleased(this.ReleasedInstancesToken);
|
|
instanceContextsFt.SetRatio(this.RatioInstancesToken);
|
|
this.servicePerformanceCounters.SetThrottleBase((int)ServicePerformanceCounters.PerfCounters.InstancesPercentMaxInstancesBase, instanceContextsFt.Capacity);
|
|
}
|
|
|
|
void InitializeCallsPerfCounterSettings()
|
|
{
|
|
Fx.Assert(this.calls != null, "Expect calls to be initialized");
|
|
Fx.Assert(this.servicePerformanceCounters != null, "expect servicePerformanceCounters to be set");
|
|
this.calls.SetAcquired(this.AcquiredCallsToken);
|
|
this.calls.SetReleased(this.ReleasedCallsToken);
|
|
this.calls.SetRatio(this.RatioCallsToken);
|
|
this.servicePerformanceCounters.SetThrottleBase((int)ServicePerformanceCounters.PerfCounters.CallsPercentMaxCallsBase, this.calls.Capacity);
|
|
}
|
|
|
|
void InitializeSessionsPerfCounterSettings()
|
|
{
|
|
Fx.Assert(this.sessions != null, "Expect sessions to be initialized");
|
|
Fx.Assert(this.servicePerformanceCounters != null, "expect servicePerformanceCounters to be set");
|
|
this.sessions.SetAcquired(this.AcquiredSessionsToken);
|
|
this.sessions.SetReleased(this.ReleasedSessionsToken);
|
|
this.sessions.SetRatio(this.RatioSessionsToken);
|
|
this.servicePerformanceCounters.SetThrottleBase((int)ServicePerformanceCounters.PerfCounters.SessionsPercentMaxSessionsBase, this.sessions.Capacity);
|
|
}
|
|
|
|
bool PrivateAcquireCall(ChannelHandler channel)
|
|
{
|
|
return (this.calls == null) || this.calls.Acquire(channel);
|
|
}
|
|
|
|
bool PrivateAcquireSessionListenerHandler(ListenerHandler listener)
|
|
{
|
|
if ((this.sessions != null) && (listener.Channel != null) && (listener.Channel.Throttle == null))
|
|
{
|
|
listener.Channel.Throttle = this;
|
|
return this.sessions.Acquire(listener);
|
|
}
|
|
else
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
|
|
bool PrivateAcquireSession(ISessionThrottleNotification source)
|
|
{
|
|
return (this.sessions == null || this.sessions.Acquire(source));
|
|
}
|
|
|
|
bool PrivateAcquireDynamic(ChannelHandler channel)
|
|
{
|
|
return (this.dynamic == null) || this.dynamic.Acquire(channel);
|
|
}
|
|
|
|
bool PrivateAcquireInstanceContext(ChannelHandler channel)
|
|
{
|
|
if ((this.instanceContexts != null) && (channel.InstanceContext == null))
|
|
{
|
|
channel.InstanceContextServiceThrottle = this;
|
|
return this.instanceContexts.Acquire(channel);
|
|
}
|
|
else
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
|
|
internal bool AcquireCall(ChannelHandler channel)
|
|
{
|
|
lock (this.ThisLock)
|
|
{
|
|
return (this.PrivateAcquireCall(channel));
|
|
}
|
|
}
|
|
|
|
internal bool AcquireInstanceContextAndDynamic(ChannelHandler channel, bool acquireInstanceContextThrottle)
|
|
{
|
|
lock (this.ThisLock)
|
|
{
|
|
if (!acquireInstanceContextThrottle)
|
|
{
|
|
return this.PrivateAcquireDynamic(channel);
|
|
}
|
|
else
|
|
{
|
|
return (this.PrivateAcquireInstanceContext(channel) &&
|
|
this.PrivateAcquireDynamic(channel));
|
|
}
|
|
}
|
|
}
|
|
|
|
internal bool AcquireSession(ISessionThrottleNotification source)
|
|
{
|
|
lock (this.ThisLock)
|
|
{
|
|
return this.PrivateAcquireSession(source);
|
|
}
|
|
}
|
|
|
|
internal bool AcquireSession(ListenerHandler listener)
|
|
{
|
|
lock (this.ThisLock)
|
|
{
|
|
return this.PrivateAcquireSessionListenerHandler(listener);
|
|
}
|
|
}
|
|
|
|
void GotCall(object state)
|
|
{
|
|
ChannelHandler channel = (ChannelHandler)state;
|
|
|
|
lock (this.ThisLock)
|
|
{
|
|
channel.ThrottleAcquiredForCall();
|
|
}
|
|
}
|
|
|
|
void GotDynamic(object state)
|
|
{
|
|
((ChannelHandler)state).ThrottleAcquired();
|
|
}
|
|
|
|
void GotInstanceContext(object state)
|
|
{
|
|
ChannelHandler channel = (ChannelHandler)state;
|
|
|
|
lock (this.ThisLock)
|
|
{
|
|
if (this.PrivateAcquireDynamic(channel))
|
|
channel.ThrottleAcquired();
|
|
}
|
|
}
|
|
|
|
void GotSession(object state)
|
|
{
|
|
((ISessionThrottleNotification)state).ThrottleAcquired();
|
|
}
|
|
|
|
internal void DeactivateChannel()
|
|
{
|
|
if (this.isActive)
|
|
{
|
|
if (this.sessions != null)
|
|
this.sessions.Release();
|
|
}
|
|
}
|
|
|
|
internal void DeactivateCall()
|
|
{
|
|
if (this.isActive)
|
|
{
|
|
if (this.calls != null)
|
|
this.calls.Release();
|
|
}
|
|
}
|
|
|
|
internal void DeactivateInstanceContext()
|
|
{
|
|
if (this.isActive)
|
|
{
|
|
if (this.instanceContexts != null)
|
|
{
|
|
this.instanceContexts.Release();
|
|
}
|
|
}
|
|
}
|
|
|
|
internal int IncrementManualFlowControlLimit(int incrementBy)
|
|
{
|
|
return this.Dynamic.IncrementLimit(incrementBy);
|
|
}
|
|
|
|
void ThrowIfClosedOrOpened(string memberName)
|
|
{
|
|
if (this.host.State == CommunicationState.Opened)
|
|
{
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.SFxImmutableThrottle1, memberName)));
|
|
}
|
|
else
|
|
{
|
|
this.host.ThrowIfClosedOrOpened();
|
|
}
|
|
}
|
|
|
|
void UpdateIsActive()
|
|
{
|
|
this.isActive = ((this.dynamic != null) ||
|
|
((this.calls != null) && (this.calls.Capacity != Int32.MaxValue)) ||
|
|
((this.sessions != null) && (this.sessions.Capacity != Int32.MaxValue)) ||
|
|
((this.instanceContexts != null) && (this.instanceContexts.Capacity != Int32.MaxValue)));
|
|
}
|
|
|
|
internal void AcquiredCallsToken()
|
|
{
|
|
this.servicePerformanceCounters.IncrementThrottlePercent((int)ServicePerformanceCounters.PerfCounters.CallsPercentMaxCalls);
|
|
}
|
|
|
|
internal void ReleasedCallsToken()
|
|
{
|
|
this.servicePerformanceCounters.DecrementThrottlePercent((int)ServicePerformanceCounters.PerfCounters.CallsPercentMaxCalls);
|
|
}
|
|
|
|
internal void RatioCallsToken(int count)
|
|
{
|
|
if (TD.ConcurrentCallsRatioIsEnabled())
|
|
{
|
|
TD.ConcurrentCallsRatio(count, this.MaxConcurrentCalls);
|
|
}
|
|
}
|
|
|
|
internal void AcquiredInstancesToken()
|
|
{
|
|
this.servicePerformanceCounters.IncrementThrottlePercent((int)ServicePerformanceCounters.PerfCounters.InstancesPercentMaxInstances);
|
|
}
|
|
|
|
internal void ReleasedInstancesToken()
|
|
{
|
|
this.servicePerformanceCounters.DecrementThrottlePercent((int)ServicePerformanceCounters.PerfCounters.InstancesPercentMaxInstances);
|
|
}
|
|
|
|
internal void RatioInstancesToken(int count)
|
|
{
|
|
if (TD.ConcurrentInstancesRatioIsEnabled())
|
|
{
|
|
TD.ConcurrentInstancesRatio(count, this.MaxConcurrentInstances);
|
|
}
|
|
}
|
|
|
|
internal void AcquiredSessionsToken()
|
|
{
|
|
this.servicePerformanceCounters.IncrementThrottlePercent((int)ServicePerformanceCounters.PerfCounters.SessionsPercentMaxSessions);
|
|
}
|
|
|
|
internal void ReleasedSessionsToken()
|
|
{
|
|
this.servicePerformanceCounters.DecrementThrottlePercent((int)ServicePerformanceCounters.PerfCounters.SessionsPercentMaxSessions);
|
|
}
|
|
|
|
internal void RatioSessionsToken(int count)
|
|
{
|
|
if (TD.ConcurrentSessionsRatioIsEnabled())
|
|
{
|
|
TD.ConcurrentSessionsRatio(count, this.MaxConcurrentSessions);
|
|
}
|
|
}
|
|
}
|
|
}
|