3

我们正在研究将 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;
        }
    }
}
4

0 回答 0