WCF Channel и ChannelFactory Кэширование - PullRequest
19 голосов
/ 21 октября 2011

Поэтому я решил немного повысить производительность в своем приложении WCF и попытаться кешировать каналы и ChannelFactory.У меня есть два вопроса по поводу всего этого, которые мне нужно прояснить перед началом работы.

1) Должен ли ChannelFactory быть реализован как синглтон?

2) Я вроде какне знаете, как кешировать / повторно использовать отдельные каналы.У вас есть примеры того, как вы можете поделиться этим?

Вероятно, важно отметить, что моя служба WCF развертывается как отдельное приложение с единственной конечной точкой.

РЕДАКТИРОВАТЬ:

Спасибо за ответы.У меня все еще есть несколько вопросов ...

1) Думаю, я не совсем понимаю, где должно происходить кэширование.Я доставляю клиентский API, который использует этот код, в другой отдел нашей компании.Это кэширование происходит на клиенте?

2) Клиентский API будет использоваться как часть приложения Silverlight, это что-то меняет?В частности, какие механизмы кэширования доступны в таком сценарии?

3) Мне все еще неясно, как устроен метод GetChannelFactory.Если у меня есть только один сервис, должен ли когда-либо создаваться и кэшироваться только один ChannelFactory?

Я до сих пор не реализовал ни одной функции кэширования (потому что я совершенно не понимаю, как это должно быть сделано!), Но вотчто я имею для клиентского прокси до сих пор:

namespace MyCompany.MyProject.Proxies
{
    static readonly ChannelFactory<IMyService> channelFactory =
        new ChannelFactory<IMyService>("IMyService");

    public Response DoSomething(Request request)
    {
        var channel = channelFactory.CreateChannel();

        try
        {
            Response response = channel.DoSomethingWithService(request);
            ((ICommunicationObject)channel).Close();
            return response;
        }
        catch(Exception exception)
        {
            ((ICommenicationObject)channel).Abort();
        }
    }
}

Ответы [ 3 ]

20 голосов
/ 21 октября 2011

Используйте ChannelFactory для создания экземпляра фабрики, а затем кэшируйте этот экземпляр. Затем вы можете создавать каналы связи по необходимости / по желанию из кэшированного ресурса.

Есть ли у вас потребность в фабриках с несколькими каналами (т. Е. Есть ли несколько служб)? По моему опыту, именно здесь вы увидите наибольшее преимущество в производительности. Создание канала - довольно недорогая задача; все настраивается в начале, что требует времени.

Я бы не кэшировал отдельные каналы - я бы их создал, использовал для операции, а затем закрыл. Если вы кешируете их, они могут перестать работать, и канал выйдет из строя, тогда вам придется прервать его и в любом случае создать новый.

Не уверен, почему вы хотите использовать синглтон для реализации ChannelFactory, особенно если вы собираетесь его создать и кэшировать, и есть только одна конечная точка.

Я опубликую пример кода позже, когда у меня будет немного больше времени.

ОБНОВЛЕНИЕ: Примеры кода

Вот пример того, как я реализовал это для проекта на работе. Я использовал ChannelFactory<T>, так как приложение, которое я разрабатывал, представляет собой n-уровневое приложение с несколькими службами, и будут добавлены другие. Цель состояла в том, чтобы создать простой способ создания клиента один раз в течение жизни приложения, а затем создавать каналы связи по мере необходимости. Основы этой идеи не мои (я получил ее из статьи в Интернете), хотя я изменил реализацию для своих нужд.

В моем приложении есть статический вспомогательный класс, и в этом классе у меня есть словарь и метод для создания каналов связи из фабрики channelf.

Словарь выглядит следующим образом (объект - это значение, поскольку оно будет содержать разные фабрики каналов, по одному для каждой службы). Я поместил «Cache» в этом примере в качестве заполнителя - замените синтаксис тем механизмом кэширования, который вы используете.

public static Dictionary<string, object> OpenChannels
{
    get
    {
        if (Cache["OpenChannels"] == null)
        {
            Cache["OpenChannels"] = new Dictionary<string, object>();
        }

        return (Dictionary<string, object>)Cache["OpenChannels"];
    }
    set
    {
        Cache["OpenChannels"] = value;
    }
}

Далее - метод создания канала связи из заводского экземпляра. Метод проверяет, существует ли сначала фабрика - если нет, то создает ее, помещает в словарь и затем генерирует канал. В противном случае он просто генерирует канал из кэшированного экземпляра фабрики.

public static T GetFactoryChannel<T>(string address)
{

    string key = typeof(T.Name);

    if (!OpenChannels.ContainsKey(key))
    {
        ChannelFactory<T> factory = new ChannelFactory<T>();
        factory.Endpoint.Address = new EndpointAddress(new System.Uri(address));
        factory.Endpoint.Binding = new BasicHttpBinding();
        OpenChannels.Add(key, factory);
    }

    T channel = ((ChannelFactory<T>)OpenChannels[key]).CreateChannel();

    ((IClientChannel)channel).Open();

    return channel;
}

Я вычеркнул этот пример из того, что я использую на работе. В этом методе вы можете многое сделать - вы можете обрабатывать несколько привязок, назначать учетные данные для аутентификации и т. Д. Это практически ваш универсальный торговый центр для генерации клиента.

Наконец, когда я использую его в приложении, я обычно создаю канал, делаю свое дело и закрываю его (или отменяю его, если это необходимо). Например:

IMyServiceContract client;

try
{
    client = Helper.GetFactoryChannel<IMyServiceContract>("http://myserviceaddress");

    client.DoSomething();

    // This is another helper method that will safely close the channel, 
    // handling any exceptions that may occurr trying to close.
    // Shouldn't be any, but it doesn't hurt.
    Helper.CloseChannel(client);
}
catch (Exception ex)
{
    // Something went wrong; need to abort the channel
    // I also do logging of some sort here
    Helper.AbortChannel(client);
}

Надеюсь, приведенные выше примеры помогут вам продолжить. Я использую нечто похожее на это уже около года в производственной среде, и это работает очень хорошо. 99% любых проблем, с которыми мы сталкивались, обычно были связаны с чем-то вне приложения (внешними клиентами или источниками данных, которые не находятся под нашим непосредственным контролем).

Дайте мне знать, если что-то не понятно или у вас есть дополнительные вопросы.

5 голосов
/ 21 октября 2011

Вы всегда можете просто сделать свой ChannelFactory статическим для каждого контракта WCF ...

Вы должны знать, что из .Net 3.5 объекты прокси объединяются по соображениям производительности фабрикой каналов.Вызов метода ICommunicationObject.Close() фактически возвращает объект в пул в надежде на его повторное использование.

Я бы посмотрел на профилировщик, если вы хотите провести некоторую оптимизацию, если вы можете предотвратить только один вызов IOсделанный в вашем коде, он может значительно перевесить любую оптимизацию, которую вы проведете с фабрикой каналов.Не выбирайте область для оптимизации, используйте профилировщик, чтобы найти место для оптимизации.Например, если у вас есть база данных SQL, вы, вероятно, найдете в своих запросах какой-то низко висящий плод, который увеличит производительность на несколько порядков, если они еще не были оптимизированы.

3 голосов
/ 02 апреля 2012

Создание канала очень дорого обходится. на самом деле, WCF уже имеет механизм кэширования для ChannelFactory, если вы используете ClientBase в клиенте вместо чистого ChannelFactory. Но срок действия кэша истечет, если вы сделаете несколько обычных операций (пожалуйста, поищите в Google, если хотите). Что касается вопроса об ErOx, у меня есть другое решение, я думаю, что оно лучше. см. ниже:


namespace ChannelFactoryCacheDemo
{
    public static class ChannelFactoryInitiator
    {
        private static Hashtable channelFactories = new Hashtable();

        public static ChannelFactory Initiate(string endpointName)
        {
            ChannelFactory channelFactory = null;

            if (channelFactories.ContainsKey(endpointName))//already cached, get from the table
            {
                channelFactory = channelFactories[endpointName] as ChannelFactory;
            }
            else // not cached, create and cache then
            {
                channelFactory = new ChannelFactory(endpointName);
                lock (channelFactories.SyncRoot)
                {
                    channelFactories[endpointName] = channelFactory;
                }
            }
            return channelFactory;
        }
    }
    class AppWhereUseTheChannel
    {
        static void Main(string[] args)
        {
            ChannelFactory channelFactory = ChannelFactoryInitiator.Initiate("MyEndpoint");
        }
    }

    interface IMyContract { }
}

вы можете настроить логику и параметры метода Initiate самостоятельно, если у вас есть другое требование. но этот класс инициатора не ограничен только одной конечной точкой. это мощный инструмент для всех конечных точек в вашем приложении. с надеждой. это хорошо работает для вас. КСТАТИ. это решение не от меня. я получил это из книги.

...