Как сделать пакетные удаленные звонки в базу данных или сервис? - PullRequest
3 голосов
/ 05 декабря 2009

Надеюсь, некоторые из вас могут дать некоторые советы по этому вопросу.

Я генерирую код, в котором мне нужно совершать звонки на удаленные ресурсы, такие как веб-службы или базы данных.

Рассмотрим этот кусок кода

class Parent{    
    IEnumerable<Child> Children;

    int SumChildren() {  
        // note the AsParallel
        return Children.AsParallel().Sum(c => c.RemoteCall()); 
    }   
}

class Child {        
    public int RemoteCall() {
        // call some webservice. I'd like to pool these calls 
        // without having to rewrite the rest of this code
    } 
}

Для 50 детей будет совершено 50 звонков в службу, 50 раз. В моих примерах из реальной жизни это легко может быть миллион звонков, доводя дело до конца.

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

Так что, когда он это делает, вызывающая задача блокируется. Затем очередь ожидает накопления X-вызовов и затем совершает 1 вызов удаленной службе со списком запросов.

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

Можно ли это сделать? Есть ли в TPL примитивы, которые позволят мне это сделать?

В некотором роде пахнет CCR, когда происходит множество событий, ожидающих завершения других действий.

Конечно, я мог бы переписать этот код, чтобы составить список запросов в классе Parent, а затем вызвать службу. Дело в том, что с моей настоящей проблемой генерируется весь этот код. Поэтому мне пришлось бы «заглянуть внутрь» реализации Child.RemoteCall, что сделало бы все это намного сложнее, чем сейчас. Кроме того, Child может быть прокси для удаленного объекта и т. Д. Было бы очень сложно, если это вообще возможно, я бы лучше изолировал эту сложность.

Надеюсь, это имеет смысл для кого-то, если не дай мне знать, я уточню.

Ответы [ 3 ]

3 голосов
/ 14 декабря 2009

Вы царапаете поверхность массивно параллельного программирования. Вам нужно мыслить ориентированным на параллелизм способом . Вы начинаете 51 задание, а не 50 заданий, которые вам нужно пакетировать. Дополнительная работа - та, которая управляет 50 работами. С точки зрения необходимых вам примитивов.

JOBHANDLE X= GetJobId();
//single job
AddJob(JOBHANLDE X,ChildJob y);
//container of jobs
AddJobs(JOBHANDLE x, ChildJobs Y);

BeginAsyncExecute(JOBHANDLE X);
WaitTillResult(JOBHANDLE X);

Вам нужен механизм в фоновом режиме, который определяет блокирующие примитивы (помимо тех, которые предоставляются ядром ОС) и управляет выполняемыми рабочими потоками и заданиями, что, судя по всему, обрабатывается технологией PLINQ. PLINQ также использует зеленые нити, что хорошо.

Вы упомянули, что у вас будет набор баз данных и веб-серверов. Следовательно, ваш процесс / функция Job должен сопоставить дочерние элементы с правильными ресурсами до выполнения пакета. Таким образом, 50 детей могут быть сокращены до гораздо меньшего количества вызовов RPC.

Итак, вы создаете свою серию заданий, а затем блокируете ее.

Получить более конкретную информацию будет сложно. но в свете обсуждения, пожалуйста, расскажите, с чем у вас проблемы.

1 голос
/ 05 декабря 2009

Так что, когда он это делает, вызывающая задача блокируется. Затем очередь ожидает X вызовов для накопления

Если очередь получает x вызовов (x = X. Если у вас есть только одна задача, которая хочет выполнить N * x вызовов, она застрянет.

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

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

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

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


... с моей настоящей проблемой генерируется весь этот код.

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

  1. Child.RemoteCall ()
  2. Parent.SumChildren ()
  3. Ничего из вышеперечисленного.

Если это не так, вам нужно что-то изменить, чтобы решить проблему. Создаются ли экземпляры Parent и Child с помощью AbstractFactory? Если это так, то может быть возможно вставить прокси для экземпляров Child, которые можно использовать для изменения нефункциональных аспектов их поведения.

0 голосов
/ 07 декабря 2009

(используя поле ответа для пробела)

Спасибо за мысли.

"Это сделало бы ...": Я имею дело с кодом, который генерируется из хранилища. Имея дело с примером этой проблемы, написанным вручную, разработчик может определить это и улучшить. При генерации кода довольно сложно выделить общий случай из набора примеров проблемы. Это может быть довольно сложным, поэтому моя стратегия раздваивать и побеждать. Если мне нужно заглянуть внутрь функции ребенка, который выходит за дверь.

«Я нашел эту ссылку ...»: Я посмотрел на Futures, но это скорее механизм разветвления, который можно распараллелить, когда есть свободный поток.

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

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

когда родитель может просто вызвать 50 дочерних элементов в параллельном режиме, и тогда эти отдельные задачи могут быть объединены вместе только потому, что они указывают на один и тот же удаленный ресурс, который был бы великолепен.

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

Мне кажется, я помню, что Джордж Хризантакопулос (парень, который создал CCR) сказал что-то, что заявление о возврате доходности - это то, к чему он привык. Я попытаюсь найти это интервью снова на 9 канале.

С уважением GJ

...