Как сделать пакетные удаленные звонки в базу данных или сервис?

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

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

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

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 ответа

Вы царапаете поверхность массивно параллельного программирования. Вы должны думать ориентированным на параллель образом. Вы запускаете 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 должен сопоставить дочерние элементы с правильными ресурсами до выполнения пакета. Таким образом, число детей может быть уменьшено до гораздо меньшего количества вызовов RPC.

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

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

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

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

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

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

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

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


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

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

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

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

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

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

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

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

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

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

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

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

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

С уважением GJ

Другие вопросы по тегам