Кэширование WCF Channel и ChannelFactory
Поэтому я решил немного повысить производительность в своем приложении WCF и попытаться кэшировать Channels и 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 ответа
Используйте 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% любых проблем, с которыми мы сталкивались, обычно были связаны с чем-то вне приложения (внешними клиентами или источниками данных, которые не находятся под нашим непосредственным контролем).
Дайте мне знать, если что-то не понятно или у вас есть дополнительные вопросы.
Вы всегда можете просто сделать свой ChannelFactory статичным для каждого контракта WCF...
Вы должны знать, что из.Net 3.5 объекты прокси объединяются по соображениям производительности фабрикой каналов. Вызов ICommunicationObject.Close()
Метод фактически возвращает объект в пул в надежде на его повторное использование.
Я хотел бы взглянуть на профилировщик, если вы хотите провести некоторую оптимизацию, если вы можете предотвратить только один IO-вызов в вашем коде, это может значительно перевесить любую оптимизацию, которую вы сделаете с фабрикой каналов. Не выбирайте область для оптимизации, используйте профилировщик, чтобы найти место для оптимизации. Например, если у вас есть база данных SQL, вы, вероятно, найдете в своих запросах какой-нибудь низко висящий плод, который увеличит производительность на несколько порядков, если они еще не были оптимизированы.
Создание канала стоит так дорого. на самом деле, 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 самостоятельно, если у вас есть другое требование. но этот класс инициатора не ограничен только одной конечной точкой. это мощный инструмент для всех конечных точек в вашем приложении. с надеждой. это хорошо работает для вас. КСТАТИ. это решение не от меня. я получил это из книги.