Повторное использование соединений в Azure Relay Bus
Мы рассматриваем возможность подключения веб-сайтов Azure к локальным службам WCF. Мы хотим использовать для этого Azure Relay Bus.
Первоначальная настройка канала TCP требует затрат, в нашем случае около 3 секунд. Это понятно. Мы кешируем канал, поэтому следующий вызов идет быстрее. ( 250 мс) Канал кэшируется в хранилище ThreadLocal.
Когда несколько пользователей используют веб-сайт, для каждого нового используемого потока создается новый экземпляр канала, и мы должны снова заплатить цену за настройку канала TCP.
Мой вопрос:
Как мы можем предотвратить, чтобы реальный пользователь столкнулся с задержкой настройки канала? Это нормальная практика иметь канал TCP для каждого потока клиента?
Примечание. Локально мы используем службы автозапуска IIS или NT для "разогрева" наших служб WCF, но так ли это для веб-сайта, работающего в Azure? Тот факт, что каждый поток имеет свой собственный канал, не облегчает его.
Код показан ниже.
public static class NetTcpRelayClient<TChannel>
{
/// <summary>
/// A ThreadLocal instance of the channel, so that each thread can have an open TCP channel
/// </summary>
private static ThreadLocal<TChannel> staticChannel = new ThreadLocal<TChannel>();
/// <summary>
/// A shared instance of the ChannelFactory
/// </summary>
private static ChannelFactory<TChannel> cf = null;
/// <summary>
/// Creates the channel.
/// </summary>
/// <returns>The created channel</returns>
private static TChannel CreateChannel()
{
// get the url and access parameters from the configuration service
var address = ServiceSecurityInspector.GetNetTcpRelayBindingAddress(typeof(TContract));
string issuerName = ConfigurationManager.AppSettings["Shared.appsettings.AzureServiceBus.IssuerName"];
string issuerSecret = ConfigurationManager.AppSettings["Shared.appsettings.AzureServiceBus.IssuerSecret"];
// create a NetTcpRelayBinding,
if (cf == null)
{
cf = new ChannelFactory<TChannel>(new NetTcpRelayBinding(), new EndpointAddress(address));
cf.Endpoint.Behaviors.Add(new TransportClientEndpointBehavior
{
TokenProvider = TokenProvider.CreateSharedSecretTokenProvider(issuerName, issuerSecret)
});
}
TChannel channel = cf.CreateChannel();
// open the channel
IClientChannel clientChannel = channel as IClientChannel;
if (clientChannel != null)
{
clientChannel.Open();
}
return channel;
}
/// <summary>
/// Gets the channel for making a call over the relay bus.
/// Note that the channel is cached.
/// And that each thread has it's own channnel.
/// </summary>
/// <returns>The channel</returns>
public static TChannel GetChannel()
{
// check if we have a channel instance already
if (!staticChannel.IsValueCreated)
{
// no, create one
staticChannel.Value = CreateChannel();
}
else
{
// if the channel exists already
IClientChannel clientChannel = staticChannel as IClientChannel;
// check if it is open, if not, make a new one
if (clientChannel != null)
{
CommunicationState state = clientChannel.State;
// check its state
if (state == CommunicationState.Faulted)
{
// channel is in faulted state, close and recreate it
CloseChannel();
staticChannel.Value = CreateChannel();
}
else if ((state == CommunicationState.Closed) || (state == CommunicationState.Closing))
{
// channel is closed or closing, recreate it
staticChannel.Value = CreateChannel();
}
}
}
return staticChannel.Value;
}
/// <summary>
/// Closes the channel in a proper way
/// </summary>
private static void CloseChannel()
{
// always check if we still have a valid channel
if (staticChannel != null)
{
IClientChannel clientChannel = staticChannel as IClientChannel;
if (clientChannel != null)
{
// if the channel is open, we close it
if (clientChannel.State != CommunicationState.Closed)
{
clientChannel.Abort();
}
}
// and we set the static variable back to it's default ( = null )
staticChannel = null;
}
}
}