我们正在研究将Azure网站与本地WCF服务相关联。我们想要使用Azure中继总线。
第一次设置TCP通道需要花费一些成本,在我们的情况下大约需要3秒。这是理解的。我们会缓存频道,因此下一个呼叫会更快。 (250ms)通道缓存在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;
}
}
}