2011-02-07 3 views
0

Фон:WCF NetTCP с фоновой резьбой

У меня есть система, которая содержит службы WCF внутри службы Windows с привязкой NetTCP. Чтобы добавить новую службу в коллекцию, вы просто добавляете стандартные записи конфигурации WCF внутри < system.serviceModel -> services />, а затем добавляете строку внутри настраиваемого раздела конфигурации, в котором говорится, что инфраструктура хостинга необходима для инициализации службы. Каждая служба инициализируется собственным фоновым потоком и экземпляром AppDomain, чтобы все было изолировано.

Вот пример того, как услуги инициализируются:

Host 
    - ServerManager 
    - ServiceManager 
     - BaseServerHost 

Экземпляр ServerManager имеет коллекцию ServiceManagers, что каждый коррелируют к одному экземпляру службы, которая где стандарт WCF реализации лежит (ServiceHost.Open/Close и т. Д.). Экземпляр ServiceManager создает экземпляр (на основе конфигурации - он имеет стандартное определение сборки/типа) экземпляр службы с использованием базового класса BaseServerHost (аннотация). Каждая служба должна наследовать от этого для рамки, чтобы иметь возможность использовать ее. В процессе инициализации BaseServerHost предоставляет несколько событий, в частности событие UnhandledException, к которому присоединяется владелец ServiceManager. (Эта часть имеет решающее значение по отношению к вопросу ниже.)

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

Вопрос:

Проблемы я столкнулся это с фоновой резьбой. Большинство открытых методов на наших конечных точках выполняют значительную активность после стандартного вызова метода вставки/обновления/удаления, например отправки сообщений в другие системы. Чтобы повысить производительность (интерфейсный веб-интерфейс), мы позволяем начальному методу вставки/обновления/удаления выполнять свою задачу, а затем запускаем фоновый поток для обработки всех вещей, которые конечный пользователь не должен ждать завершить. Этот вариант отлично работает, пока что-то в этом фоновом потоке не будет обработано и не приведет всю службу Windows, и я понимаю, что это дизайн (и с ним все в порядке).

Основываясь на всех моих исследованиях, я обнаружил, что не существует возможности реализовать глобальную попытку/улов (минус использование взломанной конфигурации включения обработки ошибок 1.1), поэтому моей команде придется вернуться и получить эти в соответствующих местах. Что в стороне, то, что я нашел, находится на конечной стороне хоста WCF, кажется, находится в своем потоке на каждом вызове, и заставить этот поток говорить с «родителем» был кошмаром. С сервисной точки зрения здесь является расположение:

Endpoint (svc - inherits from BaseServerHost, mentioned above) 
    - Business Layer 
    - Data Layer 

Когда я ловлю исключение на фоне потока в бизнес-слоя I пузыря его к экземпляру Endpoint (который наследуется от BaseServerHost), который затем пытается стрелять UnhandledException BaseServerHost в событие для этой конкретной службы (к которой был прикреплен владелец ServiceManager, который его создал). К сожалению, обработчик событий больше не существует, поэтому он ничего не делает. Я пробовал много вещей, чтобы заставить это работать, и до сих пор все мои усилия были напрасны.

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

Host 
- ServerManager 
     - ServiceManager <===================== 
      - BaseServerHost    || 
       - Endpoint (svc)   || 
        - Business Layer <====== 
          - Data Layer 

Я пробовал статические классы не повезло, и даже дошел до того, что делает ServerManager статические и expoting его ранее внутреннюю коллекцию ServiceManagers (так что они могут быть отключение), но коллекция всегда пустой или нулевой слишком ,

Мысли о выполнении этой работы?

EDIT: После копания немного дальше я нашел пример того, как именно я это вижу. На стандартном веб-сайте ASP.NET на любой странице/обработчике и т. Д. Вы можете использовать свойство HttpContext.Current для доступа к текущему контексту для этого запроса. Именно так я хотел бы, чтобы это работало с «ServiceManager.Current», возвращая владелец ServiceManager для этой службы. Может быть, это помогает?

ответ

0

Может быть, вы должны смотреть на что-то делать с CallContext:

http://msdn.microsoft.com/en-us/library/system.runtime.remoting.messaging.callcontext.aspx

Вы можете использовать либо УстановитьДанные/GetData или LogicalSetData/LogicalGetData, в зависимости от того, хотите ли вы ServiceManager быть связаны с одной физической резьбой (SetData) или «логический» поток (LogicalSetData). С помощью LogicalSetData вы можете сделать тот же экземпляр ServiceManager доступным как в потоке, так и в потоках дочерних потоков этого потока. Попытайтесь опубликовать пару потенциально полезных ссылок позже, когда я смогу их найти.

Вот ссылка на "Virtual Singleton Pattern" на codeproject.

Вот ссылка на "Thread Singleton"

Вот ссылка на "Ambient Context"

Все эти идеи похожи. По существу, у вас есть объект со статическим текущим свойством (можно получить или получить/установить). Current помещает свое значение в (и получает его) из CallContext, используя либо SetData (чтобы связать значение «Current» с текущим потоком), либо LogicalSetData (чтобы связать значение «Current» с текущим потоком и передать значение любому «дочерние» потоки).

HttpContext реализован аналогичным образом.

System.Diagnostics.CorrelationManager - еще один хороший пример, реализованный аналогичным образом.

Я думаю, что статья Ambient Context довольно хорошо объясняет, что вы можете сделать с этой идеей.

Всякий раз, когда я dicsuss CallContext, я стараюсь также включить эту ссылку на эту entry from Jeffrey Richter's blog.

В конечном счете, я не уверен, если это поможет вам или нет. Одно было бы полезно, если бы у вас было многопоточное серверное приложение (возможно, каждый запрос выполняется потоком, и несколько запросов могут выполняться одновременно в разных потоках), у вас может быть ServiceManager для каждого потока. В этом случае у вас может быть статический метод Current на ServiceManager, который всегда возвращает правильный экземпляр ServiceManager для определенного потока, поскольку он хранит ServiceManager в CallContext.Что-то вроде этого:

public class ServiceManager 
{ 
    static string serviceManagerSlot = "ServiceManager"; 

    public static ServiceManager Current 
    { 
    get 
    { 
     ServiceManager sm = null; 
     object o = CallContext.GetData(serviceManagerSlot); 
     if (o == null) 
     { 
     o = new ServiceManager(); 
     CallContext.SetData(serviceManagerSlot, o); 
     } 
     sm = (ServiceManager)o; 
     return sm; 
    } 

    set 
    { 
     CallContext.SetData(serviceManagerSlot, value); 
    } 
    } 
} 

В самом начале процесса, вы можете настроить ServiceManager для использования в текущем потоке (или тока «логический» нить), а затем хранить в «Current» Свойство:

ServiceManager sm = new ServiceManager(thread specific properties?); 
ServiceManager.Current = sm; 

Теперь, когда вы возвращаете ServiceManager.Current в свой код, он будет правильным ServiceManager для потока, в котором вы выполняете текущий процесс.

Вся эта идея может быть не такой, какой вы хотите.

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

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

Удачи.

+0

Это определенно выглядело многообещающим с высокого уровня, и я надеюсь, что это именно то, как я его реализовал, что заставляет его не работать. Сразу после инициализации службы (BaseServerHost) я делаю это: CallContext.SetData («ApplicationName», ApplicationName); CallContext.SetData (ApplicationName + "ServiceManager", это); Когда я использую непосредственное окно справа, я могу увидеть экземпляр ServiceManager. Однако, когда я получаю исключение, CallContext.GetData («ApplicationName») имеет значение NULL. Надеюсь, вы можете найти некоторые ссылки, как это сделать, поскольку я должен что-то делать неправильно. – RubyHaus

+0

@digitall - я добавил дополнительную информацию и некоторые ссылки. Мне непонятно, что идея, о которой я думал, обязательно применима к тому, как работает ваша система. Надеюсь, в моем ответе будет достаточно информации, что вы сможете сказать, применимо ли то, о чем я говорю, или нет. – wageoghe

+0

Я не забыл об этом - меня вызвали в командировку, и я не смогу снова взглянуть на неделю или около того. В то время я буду копать немного дальше. – RubyHaus