» √лавна€
eXcode.ru » —татьи » ƒругие » »нтеграци€ приложений на основе WebSphere MQ
» Ќовости
» ќпросы
» ‘айлы
» ∆урнал



ѕользователей: 0
√остей: 20





ќсновы программировани€ дл€ WebSphere MQ




ѕрограмма rewriter (модель "один к одному")

ѕерва€ программа будет достаточно проста€ и реализует так называемую модель "один к одному" или "точка-точка". Ёта программа предназначена дл€ чтени€ сообщений из очереди 1, записи их в очередь 2 и лог-файл на диске. Ёта программа имеет практическое значение. ƒостаточно часто необходимо иметь файл переданных сообщений за определенный период времени, чтобы быстро ответить на вопрос "Ѕыло ли передано сообщение с такими идентификационными параметрами в теле сообщени€:Е"? WebSphere MQ сохран€ет persistent сообщени€ на диске, но эти лог-файлы малопон€тны, предназначены дл€ восстановлени€ сообщений при сбо€х и достаточно быстро перезаписываютс€ менеджерами очередей при значение параметра logging = circular (по умолчанию) и больших потоках сообщений (logging = linear рекомендуетс€ только дл€ систем промышленной эксплуатации и в этом случае администратор WebSphere MQ должен заботитьс€ о том, чтобы лог-файлы не "замусорили" весь жесткий диск). ѕоэтому наша программа может быть достаточно полезной.

јвтору приходилось сталкиватьс€ с "плохим" стилем программировани€, когда параметры программы "зашиваютс€" в текст. ƒаже в учебных курсах этого следует избегать, несмотр€ на некоторое усложнение программ. ¬ наших программах мы будем использовать простые файлы инициализации, чтобы избежать этой ошибки. Ќазовем нашу программу rewriter.exe и файл инициализации rewriter.ini, в котором 1-€ строка Ц им€ очереди дл€ чтени€, 2-€ строка Ц им€ очереди дл€ записи, 3-€ строка Ц им€ лог-файла, как показано ниже.

QUEUE_INPUT
QUEUE_OUTPUT
C:TEMP
ewriter.log

–азрабатываема€ программа может быть представлена в следующей последовательности псевдокода:

    MQCONN
    MQOPEN	
--> цикл чтени€ сообщений
|   (на основе gmo.WaitInterval):  
|	 MQGET
|	 MQPUT
|-- конец цикла
    MQCLOSE
    MQDISC

Ќиже приводитс€ листинг программы rewriter.cpp дл€ Microsoft Visual C++ ver.6.0. Ќе забудьте добавить mqm.Lib в Project => Settings => Link и обратитьс€ к документации [15], [16], [17] в случае проблем с программированием.

/* Ћистинг программы rewriter */
/**********************************************************************/
 /* Program name: Rewriter                                            */
 /* Description: Rewriter C program  pass messages to output queue    */
 /* Function:                                                         */
 /* Rewriter is a sample C program to demonstrate the main MQI calls; */
 /* each message is copied from the input queue to the output         */
 /* queue, and sends a report to the log file                         */
/**********************************************************************/
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 #include <time.h>
 #include <signal.h>
 #include <io.h>
     /*  includes for MQI  */
 #include <cmqc.h>
 char queue1[48] = "";
 char queue2[48] = "";
 char logfilename[48] = "";
 char logfilename2[48] = "";
 char buf[48];  
 int  queuenamelen;
 time_t tmr;
 FILE * fp; 
 FILE *fptr;	
 
 void cntrl_c_handler(int sig);

   /*   Declare MQI structures needed                                */
   MQOD    odG = {MQOD_DEFAULT};    /* Object Descriptor for GET     */
   MQOD    odP = {MQOD_DEFAULT};    /* Object Descriptor for PUT     */
   MQOD    odI = {MQOD_DEFAULT};    /* Object Descriptor for InitQ   */
   MQOD    odR = {MQOD_DEFAULT};    /* Object Descriptor for report  */
   MQMD     md = {MQMD_DEFAULT};    /* Message Descriptor            */
   MQGMO   gmo = {MQGMO_DEFAULT};   /* get message options           */
   MQPMO   pmo = {MQPMO_DEFAULT};   /* put message options           */     
   MQTMC2   *trig;                  /* trigger message structure     */
   MQCHAR48 QManager;               /* queue manager name            */
   MQHCONN  Hcon;                   /* connection handle             */
   MQHOBJ   Hobj;                   /* object handle, server queue   */
   MQHOBJ   Hinq;                   /* handle for MQINQ              */
   MQHOBJ   Hout;                   /* handle for MQPUT              */
   MQLONG   O_options;              /* MQOPEN options                */
   MQLONG   C_options;              /* MQCLOSE options               */
   MQLONG   CompCode;               /* completion code               */
   MQLONG   Reason;                 /* reason code                   */
   MQLONG   CReason;                /* reason code (MQCONN)          */   
   MQBYTE   buffer[8001];            /* message buffer               */
   MQLONG   buflen;                 /* buffer length                 */
   MQLONG   messlen;                /* message length received       */
   MQLONG   Select[1];              /* attribute selectors           */
   MQLONG   SelectValue[1];         /* value attribute selectors           */
   MQLONG  char_count;

   int main(int argc, char **argv)
 {     
    strcpy(QManager, "");	/*   –аботаем с менеджером очередей по умолчанию  */
   if ( (fptr=fopen ("rewriter.ini","r" )) == NULL )
      {printf("Cannot open rewriter.ini file" ); exit(1);	}
   else{			/*   ќткрываем ini-файл и присваиваем значени€ переменным  */
      fgets(queue1, 48, fptr);
      queuenamelen = strlen(queue1) - 1;				
      queue1[queuenamelen] = ′ ′;
      fgets(queue2, 48, fptr);			
      queuenamelen = strlen(queue2) - 1;						
      queue2[queuenamelen] = ′ ′;
      fgets(logfilename, 48, fptr);
      queuenamelen = strlen(logfilename) - 1;				
      logfilename[queuenamelen] = ′ ′;
   		tmr = time(NULL);
      strcpy ( buf, ctime(&tmr));
      buf[strlen(buf)-1]=0;		// переход на новую строку
      strncat (logfilename, buf,10);	

      strcpy(odG.ObjectName, queue1);
      strcpy(odP.ObjectName, queue2);		
      fclose (fptr);	
      }	   	
   
   MQCONN(QManager, &Hcon, &CompCode, &CReason);     								   
   if (CompCode == MQCC_FAILED)
   {
     printf("MQCONN ended with reason code %ld
", CReason);
     exit(CReason);
   } 
   O_options = MQOO_INPUT_SHARED + MQOO_FAIL_IF_QUIESCING; 
   MQOPEN(Hcon, &odG, O_options, &Hobj, &CompCode, &Reason);	/* открываем очередь дл€ чтени€ - &odG   */
   if (Reason != MQRC_NONE) { printf("MQOPEN (input) ended with reason code %ld
", Reason); }
   if (CompCode == MQCC_FAILED) { exit(Reason);  }


   O_options = MQOO_OUTPUT + MQOO_FAIL_IF_QUIESCING;    
    MQOPEN(Hcon, &odP, O_options, &Hout, &CompCode, &Reason);  	/* открываем очередь дл€ записи - &odP   */	
   if (Reason != MQRC_NONE) { printf("MQOPEN (output) ended with reason code %ld
", Reason);	}
   if (CompCode == MQCC_FAILED) { exit(Reason); }   

   
   fp = fopen (logfilename,"a");						
   if ( fp==NULL ){ printf("Cannot open log file %s
", logfilename); }		
   printf("Rewriter(C) sending messages from %s to %s and to log-file %s 
",odG.ObjectName, odP.ObjectName, logfilename);	
         
   /*****************************************************************************/   
   /*   „итаем сообщени€ из QUEUE_INPUT и пишем в QUEUE_OUTPUT      */
   /*   до тех пор пока не встретим сообщение об ошибке                        */   
   /*****************************************************************************/
   buflen = sizeof(buffer) - 1;
   while (CompCode == MQCC_OK)
   {
     gmo.Options = MQGMO_ACCEPT_TRUNCATED_MSG  + MQGMO_WAIT;    
     gmo.WaitInterval = 3000;			/* ќжидаем новые сообщени€ 3 секунды           */         
     //gmo.WaitInterval = MQWI_UNLIMITED;  
     memcpy(md.MsgId, MQMI_NONE, sizeof(md.MsgId));
     memcpy(md.CorrelId, MQMI_NONE, sizeof(md.CorrelId));

   MQGET(Hcon, Hobj, &md, &gmo, buflen, buffer, &messlen, &CompCode, &Reason);   	
   if ((CompCode == MQCC_OK)  ||  (CompCode == MQCC_WARNING))
     {
      buffer[messlen] = ′′;  /* заносим символ конец строки в буфер с прочитанным сообщением */       
      buflen = messlen;	
        MQPUT(Hcon, Hout, &md, &pmo, buflen, buffer, &CompCode, &Reason); 
      if ((CompCode == MQCC_OK)  ||  (CompCode == MQCC_WARNING))
         {			
      		tmr = time(NULL);
         strcpy ( buf, ctime(&tmr));
         buf[strlen(buf)-1]=0;		// переход к новой строке			
         Reason = fprintf( fp, "%s: %s
", buf, buffer );		
         }
     }				/* конец обработки входного сообщени€         */
   }				/* конец цикла чтени€/записи сообщений функци€ми   MQGET, MQPUT */	 

   fclose (fp);	
   C_options = 0;                   /* нет никаких опций при закрытии */
   MQCLOSE(Hcon, &Hobj, C_options, &CompCode, &Reason);     /* закрываем очередь дл€ чтени€   */
   if (Reason != MQRC_NONE)	
      {printf("MQCLOSE (input) ended with reason code %ld
", Reason); }

   MQCLOSE(Hcon, &Hout, C_options, &CompCode, &Reason);    	/* закрываем очередь дл€ записи   */
   if (Reason != MQRC_NONE)
      {printf("MQCLOSE (output) ended with reason code %ld
", Reason); }
   
   if (CReason != MQRC_ALREADY_CONNECTED)
   {
     MQDISC(&Hcon,  &CompCode, &Reason);     									 
     if (Reason != MQRC_NONE){ printf("MQDISC ended with reason code %ld
", Reason); }
   }

   return(0);
 }
 
Ћистинг 9.1. Rewriter C program pass messages to output queue

¬ данной версии мы выходим из цикла программы по опции gmo.WaitInterval = 3000, когда ожидаем сообщение в очереди в течении 3 сек, а его там нет (опци€ gmo.WaitInterval работает быстрее, чем если бы мы опрашивали очередь по собственному временному циклу). ƒругой вариант программы может быть таким. «адаем gmo.WaitInterval = MQWI_UNLIMITED; что соответствует gmo.WaitInterval= -1. ѕрограмма будет крутитьс€ "бесконечно" до тех пор, пока мы не остановим еЄ принудительно, например, нажатием клавиш CNTRL_C (стандартный останов). ¬ этом случае нужно добавить обработчик прерываний по нажатию CNTRL_C потому, что при таком выходе объекты очереди останутс€ не закрытыми и идентификаторы объектов окажутс€ "зависшими" в виртуальной пам€ти компьютера. ј это может привести к тому, что при повторном запуске программы эти "зависшие" идентификаторы будут мешать нормальному функционированию программы. ¬о втором варианте открытие и закрытие лог-файла необходимо также делать в обработчике прерываний или после каждой команды MQPUT, в противном случае лог-файл не будет формироватьс€. —ледует отметить, что размер массива buffer ограничивает длину сообщени€ 8 б и при по€влении сообщений большей длины следует увеличить размер буфера.

ѕрограмма rewriter.exe работает достаточно быстро и сравнительные скорости работы данного алгоритма при длине сообщени€ 1 б на компьютере INTEL Pentium 1.8√гц приведены в таблице ниже.

“аблица 9.1.
язык программытип очередиNot PersistentPersistent
—++1000 сооб/сек400 сооб/сек
Visual Basic 6.0200 сооб/сек140 сооб/сек

”величение длины сообщени€ не ведет к пропорциональному уменьшению скорости. Ёти исследовани€ читатель может проделать самосто€тельно. –еальные приложени€, работающие с базами данных, имеют скорость обработки сообщений в 3-4 раза меньше.

¬озвраща€сь к вопросу о стил€х программировани€, следует отметить, что обработка кода ошибки €вл€етс€ об€зательным атрибутом качественного программировани€ и об этом не следует забывать. ¬ нашей программе даетс€ предупредительное сообщение и делаетс€ выход из программы. ≈сли этого не сделать, то проста€ описка в rewriter.ini файле приведет к зависанию программы и мучительному поиску причин такого зависани€, не говор€ о других более сложных ситуаци€х, например, когда очередь открыта эксклюзивно другим приложением.

ƒл€ версии программы gmo.WaitInterval = MQWI_UNLIMITED полезно сделать вывод на экран передаваемых сообщений, чтобы наблюдать динамику работы созданного интерфейса. “аких улучшений может быть достаточно много и мы рассмотрим две достаточно полезные модификации.

  1. ѕрограмма rewriter может вызыватьс€ как MQSeries-триггер. ƒл€ этого параметры можно задать следующим образом. ¬ходна€ очередь Ц это очередь, на которой определен триггеринг. ¬ыходна€ очередь Ц это User Data в триггерном процессе и им€ лог файла Ц это Environment Date в триггерном процессе. ¬ этом случае код в начале программы будет такой.

    /*  од дл€ вызова rewriter.exe 
       как MQSeries-триггер */
       int main(int argc,
                char **argv)
     {   if (argc > 1) 
         {trig = (MQTMC2*)argv[1];     
         strncpy(odG.ObjectName,
                 trig->QName,
                 MQ_Q_NAME_LENGTH);
         strncpy(queue1,
                 trig->QName,
                 MQ_Q_NAME_LENGTH);
         strncpy(QManager,
                 trig->QMgrName,
                 MQ_Q_MGR_NAME_LENGTH);     
         strncpy(odP.ObjectName,
                 trig->UserData,
           MQ_PROCESS_USER_DATA_LENGTH);
         strncpy(queue2, trig->UserData,
           MQ_PROCESS_USER_DATA_LENGTH); 
         strncpy(logfilename2,
                 trig->EnvData, 48);
     }
    

    ¬озможна€ модификаци€ этого варианта - программа rewriter может вызыватьс€ с передачей параметров через командную строку и эту модификацию читатель может проделать самосто€тельно.

  2. ѕрограмма rewriter может быть модифицирована в программу разветвитель mqsplitter.exe: чтение сообщений из очереди 1 и запись их в очередь 2, в очередь 3 и лог-файл на диске.

ћожно сделать программу mqsplitter.exe на разных €зыках, например, на Visual Basic 6.0 с интерфейсом, показанным на рис.9.1, и сравнить производительность программ на разных €зыках, реализующих один и тот же алгоритм. “ака€ задача будет хорошим лабораторным практикумом.

»нтерфейс программы mqsplitter на VB6
увеличить изображение
–ис. 9.1. »нтерфейс программы mqsplitter на VB6

ћодификацию программы mqsplitter.exe читателю предлагаетс€ сделать самосто€тельно и одновременно проверить идею создани€ "вечного двигател€". ƒл€ создани€ "вечного двигател€" понадобитьс€ изменить исходный mqsplitter.ini файл следующим образом:

QUEUE_INPUT
QUEUE_OUTPUT1
QUEUE_OUTPUT2
C:TEMP mqsplitter.log
->
QUEUE_INPUT
QUEUE_OUTPUT1
QUEUE_ INPUT
C:TEMP mqsplitter.log

≈сли в очереди QUEUE_INPUT будет хот€ бы одно сообщение, то ваша программа будет посылать сообщени€ в очередь QUEUE_OUTPUT1 до тех пор, пока не будет остановлена. ћожно заложить в ini-файл программы п€тый параметр: врем€ опроса очереди, измер€емое в миллисекундах. “ака€ программа окажетс€ весьма полезной при тестировании интерфейсов.

“ехнологические вопросы интеграции приложений

«адачи интеграции приложений возникают достаточно часто в современных корпоративных системах. ¬ качестве примера можно привести такую задачу. ¬ московском динамично развивающемс€ банке прин€то решение о переходе с наиболее попул€рной в –оссии банковской системы компании Ђƒиасофтї на западную банковскую систему T24 компании TEMENOS, получившую широкое распространение в мире и в –оссии за последние годы. ѕричины, побудившие к этому переходу, могут быть следующие:

  • Ќеобходимость иметь западную банковскую отчетность.
  • ¬озможность работы клиентов через »нтернет (интернет-банкинг).
  • √ибкость и адаптивность к изменени€м в области банковского законодательства.
  • ѕередовые программно-аппаратные решени€ и наилучшие показатели по критерию цена/(качество + производительность).

¬ этой задаче дл€ нас интересна технологи€ такого перехода. —овершенно очевидно, что переход от одной автоматизированной банковской системы (јЅ—) к другой не может пройти за один день или даже за один мес€ц. Ётот переход будет идти несколько мес€цев, а возможно, один или два года. ¬ первую очередь это зависит от используемых технологий и системных интеграторов.  роме этого, должен быть обучен персонал, а сам переход должен быть тщательно протестирован и осуществл€тьс€ по подсистемам. ѕроект перехода на новую јЅ— составл€етс€ по подсистемам, в каждой из которых выдел€ютс€ свои группы задач.

¬ качестве конкретной задачи рассмотрим создание интерфейса по передаче клиентов из системы Ђƒиасофтї (јЅ—1) в систему T24 (јЅ—2). јЅ—1 функционирует под Windows на основе базы данных SQL Server. ¬ јЅ—2 предполагаетс€ функционирование под UNIX (HP_UX) на основе базы данных ORACLE. »звестна структуры данных (таблицы client) в јЅ—1 и јЅ—2. “ребуетс€ создать интерфейс: клиенты јЅ—1 => WebSphere MQ => клиенты јЅ—2. WebSphere MQ идеально подходит дл€ решени€ такого класса задач по межплатформенной передаче данных, €вл€€сь мировым лидером среди транспортных систем.

—уществует разные варианты решени€ поставленной задачи:

  1. —оздать две программы обработчика, работающих на платформах јЅ—1 и јЅ—2 дл€ отправки и приема сообщений через WebSphere MQ.
  2. »спользовать WebSphere Business Integration Message Broker (сокращенно WebSphere BI Broker) или, иначе называемый, WebSphere MQ Integrator.
  3. »спользовать заложенные в T24 средства интеграции с WebSphere MQ.

ѕервый путь €сен в технической реализации. — одной стороны при каждом обновлении таблицы client в јЅ—1 программа-обработчик срабатывает как триггер базы данных и помещает результаты оператора update в очередь, они приход€т на јЅ—2, где сво€ программа-обработчик срабатывает как триггер очереди и помещает сообщение в таблицу client јЅ—2. WebSphere MQ гарантирует доставку сообщений. –азработчикам приложений (программ-обработчиков) остаетс€ позаботитьс€ 1) о преобразовании форматов јЅ—1 в јЅ—2 в одной из программ-обработчиков, например, на платформе Windows; 2) о надежности такой передачи с учетом механизмов транзакционности в WebSphere MQ и в базах данных одновременно. ¬едь в банковских системах ничего не должно и не может пропасть! ”крупненна€ блок-схема программы-обработчика в јЅ—2 дл€ такого надежного транзакционного взаимодействи€ выгл€дит следующим образом.

   Ѕлок 1 MQCONN;  MQOPEN;
   Ѕлок 2 MQBEGIN;  MQGET;
   Ѕлок 3 Begin Tran
   Ѕлок 4 UPDATE CLIENT SET ...
      WHERE ...
   Ѕлок 5 If Error = 0  then
     Commit Tran
       else
     Rollback Tran;
   Ѕлок 6 If Error = 0  then
     MQCMIT  else MQBACK;
   Ѕлок 7 MQCLOSE;  MQDISC;

¬ этой программе транзакци€ WebSphere MQ €вл€етс€ внешней по отношению к транзакции базы данных. ¬ программе-обработчике дл€ јЅ—1 наоборот транзакци€ WebSphere MQ будет внутренней по отношению к транзакции базы данных. “аким образом, реализаци€ интерфейса по первому варианту не вызывает технических проблем и в следующем разделе мы рассмотрим пример на программирование транзакций дл€ WebSphere MQ. ќстаетс€ отметить один важный момент. ѕервый вариант не перспективен при создании нескольких дес€тков интерфейсов и более. ¬о-первых, затраты на разработку возрастут по сравнению со вторым и третьим вариантами, когда используютс€ специализированные средства. ¬о-вторых, сопровождение нескольких дес€тков разных программ, написанных разными программистами, становитьс€ весьма серьезной задачей, а их модификаци€ после увольнени€ авторов программ или прекращени€ с ними договорных отношений может оказатьс€ неразрешимой проблемой.   сожалению, жизнь такова, что программисты увольн€ютс€, программы интерфейсов живут по несколько лет и их требуетс€ модифицировать. ѕоэтому нужно очень серьезно подумать в самом начале интеграционного проекта, какой выбрать путь дл€ реализации интерфейсов. ≈сли будет создано несколько интерфейсов по варианту 1, то переделывать их по варианту 2 Ц задача малопри€тна€ и неблагодарна€.

“ем не менее, при небольшом числе интерфейсов рекомендуетс€ вариант 1, как наиболее экономический, к рассмотрению которого мы и переходим. WebSphere BI Broker будет рассмотрен в лекции 12.   сожалению, рассмотрение средств интеграции с WebSphere MQ в системе T24 (вариант 3) выходит за пределы данного курса лекций.

ѕрограммирование транзакций

—ообщени€ WebSphere MQ могут быть четырех типов:

  • Datagram - простое сообщение, не требующее ответа;
  • Request - сообщение-запрос, которое ожидает сообщение-ответ (reply message);
  • Reply - сообщение-ответ на сообщение-запрос;
  • Report - сообщение, которое описывает такое событие, как по€вление ошибки.

Ќаша очередна€ задача: на сервере 1 прочитать сообщение из входной очереди, положить еЄ в очередь дл€ отправки на сервер 2 как сообщение-запрос и дождатьс€ прихода сообщени€-ответа, как это показано на рис.9.2. ¬се это необходимо оформить в виде транзакции, дл€ которой будет осуществл€тьс€ откат в случае неполучени€ сообщени€-ответа в течении 10 сек. Ёта задача может использоватьс€ в практических цел€х при нестабильной работе каналов, например выделенных. Ќаше приложение при откате транзакции может попытатьс€ перенаправить сообщений из входной очереди Ц но это уже друга€ задача.

—труктура объектов WebSphere MQ
–ис. 9.2. —труктура объектов WebSphere MQ

»так, последовательность псевдокода представл€етс€ следующим образом (обратите внимание на блок 5 и опции MQMD):

   Ѕлок 1 MQCONN
   Ѕлок 2 MQOPEN
   Ѕлок 3 MQBEGIN
   Ѕлок 4 MQGET (Input_queue)
   Ѕлок  5 MQPUT (Output_queue, 
       MQMD.MsgType = MQMT_REQUEST,
       MQMD.ReplyToQ = Reply_queue)
   Ѕлок 6 MQGET (Reply_queue)
   Ѕлок 7 If Reply time < 10 sec
          then
            MQCMIT  else MQBACK;
   Ѕлок 8 MQCLOSE
   Ѕлок 9 MQDISC

Ќазовем нашу программу transmit.exe и файл инициализации transmit.ini, в котором 1-€ строка Ц им€ очереди дл€ чтени€, 2-€ строка Ц им€ очереди дл€ записи, 3-€ строка Ц им€ очереди дл€ ответа, 4-€ строка Ц врем€ ожидани€ ответа Reply_time = 3000мсек, как показано ниже.

QUEUE_INPUT
QUEUE_OUTPUT
QUEUE_REPLY
3000

“ип очереди Output_queue Ц remote queue и эта очередь настроена дл€ отправки сообщений на сервер 2. Ќа сервере 2 также выполнены соответствующие настройки и при нормальной работе каналов транзакци€ будет совершатьс€ успешно. ќтметим также, что сообщение-ответ формируетс€ на сервере 2 средствами другого приложени€ на этом сервере. ¬ случае остановки любого канала, которую мы произведем дл€ отладки программы, будет происходить откат транзакции. ¬ данной версии в начале программы производитс€ извлечение параметров из ini-файла. “акую программу полезно также иметь в виде триггера и читателю предлагаетс€ самосто€тельно модифицировать программу дл€ считывани€ параметров триггера из очереди, на которую он навешиваетс€.

Ќиже приводитс€ листинг программы transmit.cpp дл€ Microsoft Visual C++ ver.6.0. ƒл€ каждого сообщени€ MsgId и the CorrelId создаютс€ как уникальные (MSGID= MQMI_NONE и CORRELID= MQCI_NONE) и об этом подробнее в лекции 11.

/* Ћистинг программы transmit */
#include <stdio.h>
 #include <stdlib.h>
 #include <string.h> 
 #include <time.h>
 #include <io.h>     
 #include <cmqc.h>
 char queue_input[48] = "";
 char queue_output[48] = ""; 
 char queue_reply[48] = ""; 
 char reply_time[48] = ""; 
 char buf[48];  
 time_t tmr;
 int queuenamelen;
 FILE *fptr;	

 int main(int argc, char **argv)
 {
   MQOD    odG = {MQOD_DEFAULT};    
   MQOD    odP = {MQOD_DEFAULT};    
   MQOD    odR = {MQOD_DEFAULT};    
   MQOD    odI = {MQOD_DEFAULT};    
   MQMD    md  = {MQMD_DEFAULT};    
   MQBO	   mbo = {MQBO_DEFAULT};
   MQGMO   gmo = {MQGMO_DEFAULT};   
   MQPMO   pmo = {MQPMO_DEFAULT};         
   MQCHAR48 QManager;               
   MQHCONN  Hcon;                   
   MQHOBJ   Hobj;                   
   MQHOBJ   Hout;               
   MQHOBJ   Hrep;               
   MQLONG   O_options;              
   MQLONG   C_options;              
   MQLONG   CompCode;               
   MQLONG   Reason;                 
   MQLONG   CReason;                
   MQBYTE   buffer[8001];           
   MQLONG   buflen;                 
   MQLONG   replylen;   
   MQLONG   messlen;                
 static  MQBYTE24 LastMsgId;				   
  
   if ( (fptr=fopen ("transmit.ini","r" )) == NULL )
      {printf("Cannot open transmit.ini file" ); 
       exit(1);	
      }
   else{
      fgets(queue_input, 48, fptr);			
      queuenamelen = strlen(queue_input) - 1;						
      queue_input[queuenamelen] = ′ ′;		
      strcpy(odG.ObjectName, queue_input);

      fgets(queue_output, 48, fptr);			
      queuenamelen = strlen(queue_output) - 1;						
      queue_output[queuenamelen] = ′ ′;		
      strcpy(odP.ObjectName, queue_output);		

      fgets(queue_reply, 48, fptr);			
      queuenamelen = strlen(queue_reply) - 1;						
      queue_reply[queuenamelen] = ′ ′;		
      strcpy(odR.ObjectName, queue_reply);

      fgets(reply_time, 48, fptr);			
      queuenamelen = strlen(reply_time) - 1;						
      reply_time[queuenamelen] = ′ ′;		
      fclose (fptr);
      }	   
   strcpy(QManager, "");	/*   –аботаем с менеджером очередей по умолчанию  */
   MQCONN(QManager, &Hcon, &CompCode, &CReason);              								  
   if (CompCode == MQCC_FAILED)
   {
     printf("MQCONN to %s ended with reason code %ld
", QManager, CReason);
     exit(CReason);
   }
   
   O_options = MQOO_BROWSE + MQOO_INPUT_SHARED ;
   MQOPEN(Hcon, &odG, O_options, &Hobj, &CompCode, &Reason);             
   if (Reason != MQRC_NONE) { printf("MQOPEN %s ended with reason code %ld
", queue_input, Reason); }
   if (CompCode == MQCC_FAILED) { exit(Reason);  }

   O_options = MQOO_BROWSE + MQOO_INPUT_SHARED ;
   MQOPEN(Hcon, &odR, O_options, &Hrep, &CompCode, &Reason);             
   if (Reason != MQRC_NONE) { printf("MQOPEN %s ended with reason code %ld
", queue_reply, Reason); }
   if (CompCode == MQCC_FAILED) { exit(Reason);  }
   
   O_options = MQOO_OUTPUT ;
   MQOPEN(Hcon, &odP, O_options, &Hout, &CompCode, &Reason);             
   if (Reason != MQRC_NONE) { printf("MQOPEN %s ended with reason code %ld
", queue_output, Reason); 	}
   if (CompCode == MQCC_FAILED) { exit(Reason);  }
    
   while (CompCode == MQCC_OK)
   {	 
    buflen = sizeof(buffer) - 1;    
     memcpy(md.MsgId, MQMI_NONE, sizeof(md.MsgId));
     memcpy(md.CorrelId, MQCI_NONE, sizeof(md.CorrelId));	 
    gmo.Options = MQGMO_ACCEPT_TRUNCATED_MSG + MQGMO_WAIT + MQGMO_SYNCPOINT;   				 
    gmo.WaitInterval = 3000 ;	 

   MQBEGIN (Hcon, &mbo, &CompCode, &Reason);
    MQGET(Hcon, Hobj, &md, &gmo, buflen, buffer, &messlen, &CompCode, &Reason);      	
   //if (Reason != MQRC_NONE) { printf("MQGET from %s ended with reason code %ld
", queue_input, Reason);	}

   if ((CompCode == MQCC_OK)  ||  (CompCode == MQCC_WARNING))
         {				
         buffer[messlen] = ′′;  /* заносим символ конец строки в буфер с прочитанным сообщением */       
         buflen = messlen;	
            md.MsgType  = MQMT_REQUEST; 
             md.Report = MQRO_EXCEPTION_WITH_DATA;
            strncpy(md.ReplyToQ, queue_reply, MQ_Q_NAME_LENGTH);
            memcpy(md.Format, MQFMT_STRING, MQ_FORMAT_LENGTH);

         MQPUT(Hcon, Hout, &md, &pmo, buflen, buffer,  &CompCode, &Reason);      
      if (Reason != MQRC_NONE) 
   { 
   printf("MQPUT  to %s ended ended unsuccessfully with reason code %ld CompCode %ld
", queue_output, Reason, CompCode );	
   MQBACK( Hcon, &CompCode, &Reason ) ;	
   CompCode = MQCC_FAILED ;
   }		
        else
       {

       while (CompCode != MQCC_FAILED)
       {
         /** осуществл€етс€ проверка queue_reply **/
      gmo.Options = MQGMO_ACCEPT_TRUNCATED_MSG + MQGMO_WAIT ;   				 
      gmo.WaitInterval = 3000 ;	 
      memcpy(md.MsgId, MQMI_NONE, sizeof(md.MsgId));
      memcpy(md.CorrelId, MQCI_NONE, sizeof(md.CorrelId));	 
    MQGET(Hcon, Hrep, &md, &gmo, buflen, buffer, &replylen, &CompCode, &Reason);      	
         if (CompCode != MQCC_FAILED)
         {       
         if (md.MsgType == MQMT_REPLY)     /* report feedback     */
         {	printf("Transaction % s=> %s successfully: %s
", queue_input, queue_output, buffer);
            MQCMIT( Hcon, &CompCode, &Reason ) ;	
         }
         else
         {
            printf("Transaction % s=> %s successfully, REPLY message  not deliver, reason code %ld CompCode %ld
", queue_input, queue_output, queue_reply, Reason, CompCode );				
            MQBACK( Hcon, &CompCode, &Reason ) ;	
            CompCode = MQCC_FAILED ;
         }
         }

        if (Reason == MQRC_NO_MSG_AVAILABLE)
        {
           printf("Transaction % s=> %s UNsuccessfully, REPLY message  not deliver
", queue_input, queue_output );	
           MQBACK( Hcon, &CompCode, &Reason ) ;	
           CompCode = MQCC_FAILED ;
        }
               }
          }	
     }		
  }    
   C_options = 0;              
   MQCLOSE(Hcon, &Hobj, C_options, &CompCode, &Reason);    
   if (Reason != MQRC_NONE){printf("MQCLOSE %s ended with reason code %ld
", queue_input, Reason); }
   MQCLOSE(Hcon, &Hout, C_options, &CompCode, &Reason);    
   if (Reason != MQRC_NONE){printf("MQCLOSE %s ended with reason code %ld
", queue_output, Reason); }	
   MQCLOSE(Hcon, &Hrep, C_options, &CompCode, &Reason);    
   if (Reason != MQRC_NONE){printf("MQCLOSE %s ended with reason code %ld
", queue_reply, Reason); }	

   MQDISC(&Hcon, &CompCode, &Reason);     
   if (Reason != MQRC_NONE){ printf("MQDISC ended with reason code %ld
", Reason); }
   return(0);
 }
Ћистинг 9.2. ѕрограмма transmit.cpp дл€ Microsoft Visual C++ ver.6.0.

ѕо тексту программы следует дать комментарии. Ќаличие опции

gmo.Options = MQGMO_SYNCPOINT;

подразумевает, что команда MQBEGIN может не указыватьс€. ќператоры

md.MsgType  = MQMT_REQUEST; 
strncpy(md.ReplyToQ,
        queue_reply,
        MQ_Q_NAME_LENGTH);

определ€ют тип сообщени€ REQUEST и очередь ответа, заданную в QUEUE_REPLY.

Ќа очередь QUEUE_OUTPUT (или на удаленную очередь на другом менеджере) должна быть навешена программа-триггер, который возвращает сообщени€ типа Reply. ≈сли Reply-сообщение поступает в очередь QUEUE_REPLY, то транзакци€ завершаетс€ успешно, в противном случае производитс€ откат транзакции и сообщение восстанавливаетс€ в очереди QUEUE_INPUT. Reply-сообщение должно иметь идентификатор CorrelId такой же, как и MsgId исходного сообщени€. ¬ данной версии программы в цел€х упрощени€ отладки не провер€етс€ это условие и читателю предлагаетс€ самосто€тельно дописать этот фрагмент кода после отладки текущей версии программы. –абота с MsgId и CorrelId будет рассмотрена подробнее в лекции 11.

ѕрограмму-триггер, котора€ "навешиваетс€" на очередь QUEUE_OUTPUT (или на удаленную очередь) дл€ формировани€ Reply-сообщени€ (md.MsgType = MQMT_REPLY;), читателю также предлагаетс€ сделать самосто€тельно.

Ќа данном примере мы познакомились с WebSphere MQ транзакци€ми, €вл€ющимис€ основой создани€ надежных программ дл€ передачи сообщений. ≈сли сообщение приходит на сервер в очередь, то программа опроса очереди открывает внешнюю транзакцию дл€ работы с WebSphere MQ и передает управление подпрограмме записи сообщени€ в базу данных, котора€ открывает внутреннюю транзакцию дл€ работы с базой данных (Ѕƒ). ≈сли сообщение уходит из базы данных, то открываетс€ внешн€€ транзакци€ работы с Ѕƒ, далее открываетс€ внутренн€€ транзакцию дл€ работы с WebSphere MQ и идет помещение сообщени€ в очередь, из которой это сообщение "улетает" на другой сервер. «авершение транзакций и откат транзакций обоих типов осуществл€етс€ взаимосв€занно. Ёто и есть правильный стиль интеграции приложений на основе WebSphere MQ.

—писки распространени€ ( модель "один ко многим" )

»спользование механизма списков распространени€ (Distribution List) или так называемой модели "один ко многим" требуетс€, например, в случае рассылки большому количеству клиентов посто€нно мен€ющейс€ информации (котировки акций, курсы валют, новости и т.п.). Ётот механизм позвол€ет одной командой MQOPEN открыть множество очередей и одной командой MQPUT положить сообщени€ в эти очереди. ѕосле открыти€ очередей возвращаетс€ один уникальный идентификатор объекта и MQPUT помещает сообщени€ во все эти очереди, использу€ этот единственный идентификатор.

¬ версии WebSphere MQ 5.1 и выше object descriptor (MQOD) содержит пол€, которые используютс€ дл€ списков распространени€. ѕоле Object Descriptor RecsPresent содержит число Object Records (MQORs) и если оно больше чем 0, то это означает, что должен быть использован список распространени€.

–ассмотрим этот механизм на примере задачи, когда WebSphere MQ server помещает сообщени€ в N очередей, как показано на рис.9.3. Ёти сообщени€ могут дальше уходить через remote queue или их может забирать WebSphere MQ client с заданной периодичностью. Ќазовем нашу программу distlist.exe, файл с текстом сообщени€ distlist.dat и файл инициализации distlist.ini, в котором 1-€ строка Ц им€ менеджера, 2-€ и последующие строки Ц имена очередей, как показано ниже.

QM_ ALFA
Queue_ Moscow
Queue_ Kiev
Queue_ Alma-Ata	
Queue_ SPetersburg 
Queue_ Novosibirsk
Queue_ Saratov

//last string must be blank

ћеханизм Distribution List дл€  WebSphere MQ
–ис. 9.3. ћеханизм Distribution List дл€ WebSphere MQ

Ќиже приводитс€ листинг программы distlist.cpp дл€ Microsoft Visual C++ ver.6.0.

/* Ћистинг программы distlist                                       */
/* Program name: Distlist                                           */
/* Description: Distlist C program  pass messages to output queues  */
/* by Distribution list  for indicated Queue Manager                */
/* distlist.ini file give list of queue and distlist.dat give file  */
/* of message which copied to the output queue                      */

 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h> 
 #include <io.h>
 #include <time.h>
 #include <cmqc.h>
 char queue[1000][48] ; 
 char buf[48];  
 int  queuenamelen; 
 time_t tmr;
 FILE * fp; 
 FILE *fptr;	

static void print_usage(void);
static void print_responses( char * comment, PMQRR  pRR, MQLONG NumQueues, PMQOR  pOR);

 int main(int argc, char **argv)
 {
   typedef enum {False, True} Bool;
   MQOD     od = {MQOD_DEFAULT};    /* Object Descriptor             */
   MQMD     md = {MQMD_DEFAULT};    /* Message Descriptor            */
   MQPMO   pmo = {MQPMO_DEFAULT};   /* put message options           */
   MQHCONN  Hcon;                   /* connection handle             */
   MQHOBJ   Hobj;                   /* object handle                 */
   MQLONG   O_options;              /* MQOPEN options                */
   MQLONG   C_options;              /* MQCLOSE options               */
   MQLONG   CompCode;               /* completion code               */
   MQLONG   OpenCode;               /* MQOPEN completion code        */
   MQLONG   Reason;                 /* reason code                   */
   MQCHAR48 QManager;               /* queue manager name            */
   MQLONG   buflen;                 /* buffer length                 */
   char     buffer[101];            /* message buffer                */   
   MQLONG   Index ;                 /* Index into list of queues     */
   MQLONG   NumQueues ;             /* Number of queues              */ 
   PMQRR    pRR=NULL;               /* Pointer to response records   */
   PMQOR    pOR=NULL;               /* Pointer to object records     */
   Bool     DisconnectRequired=False;/* Already connected switch     */
   Bool     Connected=False;        /* Connect succeeded switch     */
      
   typedef struct 
   {
    MQBYTE24 MsgId;
    MQBYTE24 CorrelId;
   } PutMsgRec, *pPutMsgRec; 
   pPutMsgRec pPMR=NULL;            /* Pointer to put msg records    */    
   MQLONG PutMsgRecFields=MQPMRF_MSG_ID | MQPMRF_CORREL_ID;     

                           /* Open ini file and setting value  */
   	if ( (fptr=fopen ("distlist.ini","r" )) == NULL )
      {printf("Cannot open distlist.ini file" ); 
       print_usage();	
       exit(1);	}
   else{					
      fgets(QManager, 48, fptr);		
      queuenamelen = strlen(QManager) - 1;	
      QManager[queuenamelen] = ′ ′;
      NumQueues = 0;
   while (queuenamelen != 0)
      {	
      fgets(queue[NumQueues], 48, fptr);	
      queuenamelen = strlen(queue[NumQueues]) - 1;				
      queue[NumQueues][queuenamelen] = ′ ′;
      NumQueues++;
      }
   }
   fclose (fptr);	
   --NumQueues;		    /* NumQueues - Number of Queue name   */
   /*   Allocate response records, object records and put message records */   
   pRR  = (PMQRR)malloc( NumQueues * sizeof(MQRR));
   pOR  = (PMQOR)malloc( NumQueues * sizeof(MQOR));
   pPMR = (pPutMsgRec)malloc( NumQueues * sizeof(PutMsgRec));
   
   if((NULL == pRR) || (NULL == pOR) || (NULL == pPMR))
   {
     printf("%s(%d) malloc failed
", __FILE__, __LINE__);
     exit(4);
   }

   /*   Use parameters as the name of the target queues              */
   
   for( Index = 0 ; Index < NumQueues ; Index ++)
   {
     strncpy( (pOR+Index)->ObjectName, queue[Index], (size_t)MQ_Q_NAME_LENGTH);		
     strncpy( (pOR+Index)->ObjectQMgrName, QManager, (size_t)MQ_Q_MGR_NAME_LENGTH);
   }   

   for( Index = 0 ; Index < NumQueues ; Index ++)
   {
     MQCONN((pOR+Index)->ObjectQMgrName, &Hcon, &((pRR+Index)->CompCode), &((pRR+Index)->Reason)); 
     if ((pRR+Index)->CompCode == MQCC_FAILED)
     {
       continue;
     }
     if ((pRR+Index)->CompCode == MQCC_OK)
     {
       DisconnectRequired = True ;
     }
     Connected = True;
     break ;
   }
     /* Print any non zero responses                                   */
      print_responses("MQCONN", pRR, Index, pOR);

      /* Print If failed to connect to  queue manager then exit.        */   
   if( False == Connected  )
   {
     printf("Unable to connect to queue manager
");
     exit(3) ;
   } 
   
   if ( (fp=fopen ("distlist.dat","r" )) == NULL )
      {printf("Cannot open distlist.dat file" ); exit(2);	}
   else{					
      fgets(buffer, 100, fptr);			
      buflen = (MQLONG)strlen(buffer);               /* length without null         */
       if (buffer[buflen-1] == ′
′)		                  	/* last char is a new-line     */
       {
         buffer[buflen-1]  = ′′;				/* replace new-line with null  */
         --buflen;					/* reduce buffer length        */
       }
   }
      fclose (fp);	

   		tmr = time(NULL);
      strcpy ( buf, ctime(&tmr));
      buf[strlen(buf)-5]=0;	
      printf("Distlist start  send message to list queue %s
", buf);	

   /*   Open the target message queue for output                     */   
   od.Version = MQOD_VERSION_2 ;
   od.RecsPresent = NumQueues ;      
   od.ObjectRecPtr = pOR;            
   od.ResponseRecPtr = pRR ;
   O_options = MQOO_OUTPUT + MQOO_FAIL_IF_QUIESCING; 

   MQOPEN(Hcon, &od,  O_options,  &Hobj,  &OpenCode,  &Reason); 
   if (Reason == MQRC_MULTIPLE_REASONS)
   {
     print_responses("MQOPEN", pRR, NumQueues, pOR);
   }
   else
   {
     if (Reason != MQRC_NONE)
     {
       printf("MQOPEN returned CompCode=%d, Reason=%d
", OpenCode, Reason);
     }
   }
      
   /*   Read message from the file 
   /*   Loop until null line or end of file, or there is a failure   */      
   CompCode = OpenCode;        /* use MQOPEN result for initial test */   
   		
   pmo.Version = MQPMO_VERSION_2 ;
   pmo.RecsPresent = NumQueues ;
   pmo.PutMsgRecPtr = pPMR ;
   pmo.PutMsgRecFields = PutMsgRecFields ;
   pmo.ResponseRecPtr = pRR ;  
   
           /*   Put  buffer to the message queue                       */
     if (buflen > 0)
     {
       for( Index = 0 ; Index < NumQueues ; Index ++)
       {
         memcpy( (pPMR+Index)->MsgId, MQMI_NONE, sizeof((pPMR+Index)->MsgId));
         memcpy( (pPMR+Index)->CorrelId, MQCI_NONE, sizeof((pPMR+Index)->CorrelId));
       }
       memcpy(md.Format, MQFMT_STRING, (size_t)MQ_FORMAT_LENGTH);

       MQPUT(Hcon, Hobj, &md, &pmo, buflen, buffer, &CompCode, &Reason); 
   
       if (Reason == MQRC_MULTIPLE_REASONS)
       {
         print_responses("MQPUT", pRR, NumQueues, pOR);
       }
       else
       {
         if (Reason != MQRC_NONE)
         {
           printf("MQPUT  returned CompCode=%d, Reason=%d
", OpenCode, Reason);
         }
       }
      tmr = time(NULL);
      strcpy ( buf, ctime(&tmr));
      buf[strlen(buf)-5]=0;		// strip new line
      printf("Distlist finish send message to list queue %s
", buf);	

     }
     else   /* satisfy end condition when empty line is read */
       CompCode = MQCC_FAILED;
   //}
   
   if (OpenCode != MQCC_FAILED)
   {
     C_options = 0;
     MQCLOSE(Hcon, &Hobj, C_options, &CompCode, &Reason); 
     if (Reason != MQRC_NONE)
     {
       printf("MQCLOSE ended with reason code %d
", Reason);
     }
   }

   if (DisconnectRequired==True)
   {
     MQDISC(&Hcon, &CompCode, &Reason);          
     if (Reason != MQRC_NONE)
     {
       printf("MQDISC ended with reason code %d
", Reason);
     }
   }

   
   if( NULL != pOR )
   {
     free( pOR ) ;
   }
   if( NULL != pRR )
   {
     free( pRR ) ;
   }
   if( NULL != pPMR )
   {
     free( pPMR ) ;
   }
   return(0);
 }

static void print_usage(void)
{
 printf("Distlist correct usage is:

");
 printf("Distlist Qmgr QName1 [QName2 [QName3 [...]]]

");
}

static void print_responses( char * comment, PMQRR  pRR, MQLONG NumQueues, PMQOR  pOR)
{
  MQLONG Index;
  for( Index = 0 ; Index < NumQueues ; Index ++ )
  {
    if( MQCC_OK != (pRR+Index)->CompCode )
    {
      printf("%s for %.48s( %.48s) returned CompCode=%d, Reason=%d
"
            , comment
            , (pOR+Index)->ObjectName
            , (pOR+Index)->ObjectQMgrName
            , (pRR+Index)->CompCode
            , (pRR+Index)->Reason);
    }
  } 
}
Ћистинг 9.3. ѕрограмма distlist.cpp дл€ Microsoft Visual C++ ver.6.0.

¬ завершение раздела можно сказать, что врем€ работы механизма Distribution List дл€ WebSphere MQ с 200, 400, 600 и т.д. очеред€ми не зависит от производительности компьютера и не сильно зависит от количества очередей (дл€ Notpersistent queue, persistent queue не целесообразно использовать дл€ данной задачи ). Ёто нагл€дно видно из следующей таблицы, отражающей врем€ работы distlist (сек) в зависимости от оперативной пам€ти (ќѕ) компьютера: 512ћбт и 1√бт.

 оличество очередей¬рем€ работы distlist (сек) при ќѕ 512ћбт¬рем€ работы distlist (сек) при ќѕ 1√бт
20011
40011
60011
80021
100032
120032

“аким образом, WebSphere MQ дает нам удобный механизм рассылки посто€нно обновл€ющейс€ информации дл€ 200, 400, 600 Е клиентов. ¬ерхн€€ граница числа очередей (клиентов) зависит от операционной системы и оперативной пам€ти компьютера. „исло клиентов (очередей) ограничиваетс€ 1200 на Windows компьютере с пам€тью 512ћбт.

«адачи, решаемые с помощью механизмов списков распространени€ (Distribution List), могут быть успешно решены с помощью модели публикаци€/подписка (Publish/Subscribe), котора€ будет рассмотрена подробно в следующей лекции.

  началу статьи





ƒобавил: MadvEXƒата публикации: 2006-02-28 01:20:54
–ейтинг статьи:3.00 [√олосов 5] ол-во просмотров: 174854

 омментарии читателей

¬сего комментариев: 29506

2019-11-12 05:23:00
averena24rap

[url=http://averena24.ru]санкнижка продление[/url] - подробнее на нашем сайте [url=https://averena24.ru]averena24.ru[/url]
Ћична€ [url=https://averena24.ru]медицинска€ книжка[/url] ([url=https://averena24.ru]санитарна€ книжка[/url]) - официальный документ строгой отчетности. ¬ санитарной книжке отражаютс€ все данные о результатах периодических осмотров, сдачи анализов и прививках, наличи€ инфекционных заболеваний, а также о прохождении курсов по гигиеническому воспитанию и аттестации.

2019-11-12 04:01:06
GeraldHib
<a href=https://vk.com/vischki>вышка тура улт 060</a> Ц это передвижные приспособлени€, предназначенные дл€ высотных работ. »спользуютс€ при возведении промышленных и гражданских сооружений, реставрации пам€тников архитектуры, отделке помещений. ѕомимо строительной области, они нашли применение в обслуживании вентил€ционных систем и рекламных щитов, осуществлении клининговых меропри€тий (мойки витражных и стекл€нных элементов многоэтажек), замене светильников. »х часто арендуют дл€ шоу, киносъемок, телевидени€ с целью размещени€ оборудовани€. ќни представл€ют собой сборную жесткую металлическую конструкцию в форме башни. Ќаклонные лестницы обеспечивают доступ к горизонтальным настилам, количество которых регулируетс€.
<a href=https://vk.com/wall-187610116_3>вышка тура 7 метров</a>на колесах могут комплектоватьс€ несколькими рабочими площадками, но дл€ работы обычно используютс€ не более двух. –абочий уровень (<a href=https://vk.com/wall-187610116_6>вышка тура 7.4 м</a>;) представл€ет собой несущие ригели со специальными зацепами, на которые кладут дощатые щиты. ƒл€ обеспечени€ максимальной безопасности, при установке более 4.0 м., прикрепл€ют к стене анкерными креплени€ми. ѕо желанию заказчика, комплектуютс€ дополнительными щитами-настилами. ћонтаж и демонтаж вышки туры не занимает много времени и труда, так как она состоит из элементов с малым весом, которые легко поднимаютс€ на самый верх и устойчивой колесной базой. —оедин€тс€ они двум€ способами, либо вставл€ютс€ труба в трубу, например рамы, либо соедин€ютс€ флажковыми замками, что не требует никаких инструментов и специальных навыков.
¬ нашей компании ¬ы можете выбрать или <a href=https://vk.com/wall-187610116_7>вышка тура купить спб</a> с бесплатной доставкой рассрочкой.

2019-11-12 03:50:37
KeithErary
[больничный лист][купить официально больничный лист]

[url=https://dr-ryadom24.top]больничный лист официально/ѕодробности по ссылкеЕ[/url]

2019-11-12 00:01:27
gekllokjwer
<a href="http://xn----8sbaic5bmmlo1af.xn--p1ai/user/Arnoldweava/">√Ґ√†√¶√≠√Ѓ</a>
<a href="http://bbs.gm2004.com/home.php?mod=space&uid=110860">√Ґ√†√¶√≠√Ѓ</a>
<a href="http://www.mir-c.ru/forum/user/71566/">√Ґ√†√¶√≠√Ѓ</a>
<a href="https://www.posturiradio.net/user/Davidlox/">√Ґ√†√¶√≠√Ѓ</a>
<a href="http://terra-bashkiria.ru/forum/index.php?PAGE_NAME=profile_view&UID=25982">√Ґ√†√¶√≠√Ѓ</a>
<a href="https://partsukraine.com/forum/?PAGE_NAME=profile_view&UID=19798">√Ґ√†√¶√≠√Ѓ</a>
<a href="https://profmeter.com.ua/communication/forum/index.php?PAGE_NAME=profile_view&UID=34586">√Ґ√†√¶√≠√Ѓ</a>
<a href="http://smtp.pbprog.ru/forum/?PAGE_NAME=profile_view&UID=239585">√Ґ√†√¶√≠√Ѓ</a>
<a href="http://www.rybalka-profi.ru/forum/index.php/user/23793/">√Ґ√†√¶√≠√Ѓ</a>
<a href="http://practic.childpsy.ru/phorum/index.php?PAGE_NAME=profile_view&UID=680832">√Ґ√†√¶√≠√Ѓ</a>
<a href="http://pbprog.kz/forum/?PAGE_NAME=profile_view&UID=244518">√Ґ√†√¶√≠√Ѓ</a>
<a href="https://voda21.ru/forum/user/4430/">√Ґ√†√¶√≠√Ѓ</a>
<a href="http://ftp.best5.ru/support/forum/index.php?PAGE_NAME=profile_view&UID=77822">√Ґ√†√¶√≠√Ѓ</a>
<a href="https://bestnet.ru/support/forum/index.php?PAGE_NAME=profile_view&UID=77822">√Ґ√†√¶√≠√Ѓ</a>
<a href="http://cmb.argonathrpg.eu/index.php?action=profile;u=6332">√Ґ√†√¶√≠√Ѓ</a>
<a href="http://crashphoto.ru/user/ThomasAnoni/">√Ґ√†√¶√≠√Ѓ</a>
<a href="https://www.bahismaster.com/member.php?49232-Stephennoize">√Ґ√†√¶√≠√Ѓ</a>
<a href="https://www.aborigen-tour.ru/communication/forum/index.php?PAGE_NAME=profile_view&UID=16840">√Ґ√†√¶√≠√Ѓ</a>

2019-11-11 17:28:48
gekllokjwer
<a href="https://www.bangphaehospital.net/forum/index.php?action=profile;u=117243">√Ґ√†√¶√≠√Ѓ</a>
<a href="http://cxshw.com/home.php?mod=space&uid=176665">√Ґ√†√¶√≠√Ѓ</a>
<a href="http://fastfoodfanatics.com/member.php?action=profile&uid=3556">√Ґ√†√¶√≠√Ѓ</a>
<a href="http://www.eltaltd.ru/forum/user/157874/">√Ґ√†√¶√≠√Ѓ</a>
<a href="http://www.0594bbs.cn/space-uid-8266.html">√Ґ√†√¶√≠√Ѓ</a>
<a href="https://intrarent.ru/forum/index.php?PAGE_NAME=profile_view&UID=15749">√Ґ√†√¶√≠√Ѓ</a>
<a href="http://egotranslating.com/ego-club/forum/user/15879/">√Ґ√†√¶√≠√Ѓ</a>
<a href="http://xtczjw.com/home.php?mod=space&uid=123293">√Ґ√†√¶√≠√Ѓ</a>
<a href="http://visitusadba.ru/forum/user/5611/">√Ґ√†√¶√≠√Ѓ</a>
<a href="http://www.6986986.com/space-uid-70181.html">√Ґ√†√¶√≠√Ѓ</a>
<a href="http://www.nanajch.com/home.php?mod=space&uid=32217">√Ґ√†√¶√≠√Ѓ</a>
<a href="http://xn--80addh1bakhjcf3k.xn--p1ai/forum/index.php?PAGE_NAME=profile_view&UID=239585">√Ґ√†√¶√≠√Ѓ</a>
<a href="http://www.66hyb.com/space-uid-111273.html">√Ґ√†√¶√≠√Ѓ</a>
<a href="http://www.meridianlogistic.ru/communication/forum/user/191411/">√Ґ√†√¶√≠√Ѓ</a>
<a href="https://www.teko.biz/support/forum/?PAGE_NAME=profile_view&UID=63271">√Ґ√†√¶√≠√Ѓ</a>
<a href="http://j733151.myjino.ru/user/Walterder/">√Ґ√†√¶√≠√Ѓ</a>
<a href="https://shopwizzu.com/forum/?PAGE_NAME=profile_view&UID=6725">√Ґ√†√¶√≠√Ѓ</a>
<a href="https://www.semenatver.ru/forum/user/19047/">√Ґ√†√¶√≠√Ѓ</a>

2019-11-11 15:55:06
holodBow

[url=http://klimatik-nn.ru]ремонт холодильников атлант[/url] - [url=http://klimatik-nn.ru]klimatik-nn.ru[/url]

2019-11-11 15:25:29
gekllokjwer
<a href="http://51cardu.com/home.php?mod=space&uid=164067">√Ґ√†√¶√≠√Ѓ</a>
<a href="http://market.pbprog.ru/forum/index.php?PAGE_NAME=profile_view&UID=241311">√Ґ√†√¶√≠√Ѓ</a>
<a href="http://sosnovoborsk.ru/forum/user/51512/">√Ґ√†√¶√≠√Ѓ</a>
<a href="http://keyandi.com/space-username-Michaelcluri.html">√Ґ√†√¶√≠√Ѓ</a>
<a href="http://bashkiria.travel/forum/index.php?PAGE_NAME=profile_view&UID=26215">√Ґ√†√¶√≠√Ѓ</a>
<a href="http://p4.minzdravrso.ru/about/forum/user/13554/">√Ґ√†√¶√≠√Ѓ</a>
<a href="http://www.galeevmm.ru/forum/memberlist.php?mode=viewprofile&u=27478">√Ґ√†√¶√≠√Ѓ</a>
<a href="http://ruslan.siter.org.kz/communication/forum/user/10460/">√Ґ√†√¶√≠√Ѓ</a>
<a href="http://v.totobang.cn/home.php?mod=space&uid=315027">√Ґ√†√¶√≠√Ѓ</a>
<a href="http://umu.bip-ip.by/forum/user/31881/">√Ґ√†√¶√≠√Ѓ</a>
<a href="https://teikalamata.gr/member.php/22140-DavidGaimi">√Ґ√†√¶√≠√Ѓ</a>
<a href="http://xn--80ag7ag.online/forum/user/40979/">√Ґ√†√¶√≠√Ѓ</a>
<a href="http://ww.xn--80afhpoahcbjlch1a1d7d.xn--p1ai/forum/index.php?PAGE_NAME=profile_view&UID=241017">√Ґ√†√¶√≠√Ѓ</a>
<a href="http://www.d3scene.com/forum/members/matthewwap.html">√Ґ√†√¶√≠√Ѓ</a>
<a href="http://aocph.com/memberlist.php?mode=viewprofile&u=1931">√Ґ√†√¶√≠√Ѓ</a>
<a href="http://xn--80aaiunencbyldg5b5bzc9c.xn--p1ai/about/forum/user/191635/">√Ґ√†√¶√≠√Ѓ</a>
<a href="http://allied-social.ca/forum/memberlist.php?mode=viewprofile&u=30176">√Ґ√†√¶√≠√Ѓ</a>
<a href="https://unaegean.gr/member.php/22019-Percyduavy">√Ґ√†√¶√≠√Ѓ</a>

2019-11-11 13:30:09
gekllokjwer
<a href="https://escortfistged.info/gana-eskort-soprovozhdenie.html">√Г√†√≠√† √љ√±√™√Ѓ√∞√≤ √±√Ѓ√ѓ√∞√Ѓ√Ґ√Ѓ√¶√§√•√≠√®√•</a>
<a href="https://modelsseherd.ru/siktivkar-krasivie-devushki-eskort.html">√С√ї√™√≤√ї√Ґ√™√†√∞ √™√∞√†√±√®√Ґ√ї√• √§√•√Ґ√≥√Є√™√® √љ√±√™√Ѓ√∞√≤</a>
<a href="https://modelsgyalked.info/vip-eskort-florentsiya-italiya-soprovozhdenie-muzhchin.html">VIP-√љ√±√™√Ѓ√∞√≤ √Ф√Ђ√Ѓ√∞√•√≠√ґ√®√њ, √И√≤√†√Ђ√®√њ | √С√Ѓ√ѓ√∞√Ѓ√Ґ√Ѓ√¶√§√•√≠√®√• √ђ√≥√¶√Ј√®√≠</a>
<a href="https://modelsgrgrew.info/norvegiya-krasivie-devushki-eskort.html">√Н√Ѓ√∞√Ґ√•√£√®√њ √™√∞√†√±√®√Ґ√ї√• √§√•√Ґ√≥√Є√™√® √љ√±√™√Ѓ√∞√≤</a>
<a href="https://escorteoleft.com/uslugi-zhenskogo-eskorta-dlya-muzhchin-ambalangoda-shri-lanka.html">√У√±√Ђ√≥√£√® √¶√•√≠√±√™√Ѓ√£√Ѓ √љ√±√™√Ѓ√∞√≤√† √§√Ђ√њ √ђ√≥√¶√Ј√®√≠ √А√ђ√°√†√Ђ√†√≠√£√Ѓ√§√†, √Ш√∞√®-√Л√†√≠√™√†</a>
<a href="https://escortdumet.ru/elitniy-vip-eskort-pakistan.html">√Э√Ђ√®√≤√≠√ї√© VIP √љ√±√™√Ѓ√∞√≤ √П√†√™√®√±√≤√†√≠</a>
<a href="https://escortztcoved.info/escort-moscow-szao.html">Escort Moscow SZAO</a>
<a href="https://modelsdqwote.ru/elitnie-znakomstva-s-modelyami-belgorod.html">√Э√Ђ√®√≤√≠√ї√• √І√≠√†√™√Ѓ√ђ√±√≤√Ґ√† √± √ђ√Ѓ√§√•√Ђ√њ√ђ√® √Б√•√Ђ√£√Ѓ√∞√Ѓ√§</a>
<a href="https://escortqptold.com/agentstvo-eskort-uslug-dlya-delovih-lyudey-kosgoda-shri-lanka.html">√А√£√•√≠√≤√±√≤√Ґ√Ѓ √љ√±√™√Ѓ√∞√≤ √≥√±√Ђ√≥√£ √§√Ђ√њ √§√•√Ђ√Ѓ√Ґ√ї√µ √Ђ√Њ√§√•√© √К√Ѓ√±√£√Ѓ√§√†, √Ш√∞√®-√Л√†√≠√™√†</a>
<a href="https://escorteufelt.com/eskort-agentstvo-soligorsk.html">√Э√±√™√Ѓ√∞√≤ √†√£√•√≠√≤√±√≤√Ґ√Ѓ √С√Ѓ√Ђ√®√£√Ѓ√∞√±√™</a>
<a href="https://esortkzhoped.info/escort-agency-agency-ust-kamenogorsk-kazakhstan-vip-escort.html">Escort Agency Agency Ust-Kamenogorsk, Kazakhstan - VIP Escort</a>
<a href="https://modelspogot.com/elite-girls-for-escort-vip-models-murcia-spain.html">Elite girls for escort | VIP models Murcia, Spain</a>
<a href="https://modelsdiilded.ru/uslugi-zhenskogo-eskorta-dlya-muzhchin-bali-changu.html">√У√±√Ђ√≥√£√® √¶√•√≠√±√™√Ѓ√£√Ѓ √љ√±√™√Ѓ√∞√≤√† √§√Ђ√њ √ђ√≥√¶√Ј√®√≠ √Б√†√Ђ√®-√Ч√†√≠√£√≥</a>
<a href="https://escortjtrased.info/escort-agency-hungary.html">Escort Agency Hungary</a>
<a href="https://modelsheapared.info/elitnie-eskort-uslugi-bad-valtersdorf.html">√Э√Ђ√®√≤√≠√ї√• √љ√±√™√Ѓ√∞√≤ √≥√±√Ђ√≥√£√® √Б√†√§-√В√†√Ђ√Љ√≤√•√∞√±√§√Ѓ√∞√і</a>
<a href="https://escortguwon.info/elitnie-prostitutki-smolenskaya-oblast-vip-devushki-smolenskaya-oblast-anketi-i-eskort-ot-elitnih.html">√Э√Ђ√®√≤√≠√ї√• √ѓ√∞√Ѓ√±√≤√®√≤√≥√≤√™√® √С√ђ√Ѓ√Ђ√•√≠√±√™√†√њ √Ѓ√°√Ђ√†√±√≤√Љ - Vip √§√•√Ґ√≥√Є√™√® √С√ђ√Ѓ√Ђ√•√≠√±√™√†√њ √Ѓ√°√Ђ√†√±√≤√Љ, √†√≠√™√•√≤√ї √® √љ√±√™√Ѓ√∞√≤ √Ѓ√≤ √љ√Ђ√®√≤√≠√ї√µ</a>
<a href="https://escortzosurted.info/elite-girls-moscow-cao.html">Elite girls Moscow CAO</a>
<a href="https://modelsresaid.com/escort-agency-agency-zhanaozen-kazakhstan-vip-escort.html">Escort Agency Agency Zhanaozen, Kazakhstan - VIP Escort</a>

2019-11-11 11:23:25
gekllokjwer
<a href="https://modelsertried.com/agentstvo-eskort-uslug-volgograd-vip-soprovozhdenie.html">√А√£√•√≠√≤√±√≤√Ґ√Ѓ √љ√±√™√Ѓ√∞√≤ √≥√±√Ђ√≥√£ √В√Ѓ√Ђ√£√Ѓ√£√∞√†√§ - VIP √±√Ѓ√ѓ√∞√Ѓ√Ґ√Ѓ√¶√§√•√≠√®√•</a>
<a href="https://escortfrrled.info/vip-modeli-bobruysk-belarus.html">VIP √ђ√Ѓ√§√•√Ђ√® √Б√Ѓ√°√∞√≥√©√±√™, √Б√•√Ђ√†√∞√≥√±√Љ</a>
<a href="https://escortdtlost.ru/elitnie-modeli-soldeu-i-el-tarter.html">√Э√Ђ√®√≤√≠√ї√• √ђ√Ѓ√§√•√Ђ√® √С√Ѓ√Ђ√Љ√§√•√≥ √® √Э√Ђ√Љ-√Т√†√∞√≤√•√∞</a>
<a href="https://modelszecaht.info/escort-services-of-charming-girls-china-jinan.html">Escort services of charming girls China Jinan</a>
<a href="https://modelseynedd.com/elitnie-devushki-vetnam.html">√Э√Ђ√®√≤√≠√ї√• √§√•√Ґ√≥√Є√™√® √В√Љ√•√≤√≠√†√ђ</a>
<a href="https://modelsfufowged.info/rostov-na-donu-eskort-soprovozhdenie.html">√Р√Ѓ√±√≤√Ѓ√Ґ-√≠√†-√Д√Ѓ√≠√≥ √љ√±√™√Ѓ√∞√≤ √±√Ѓ√ѓ√∞√Ѓ√Ґ√Ѓ√¶√§√•√≠√®√•</a>
<a href="https://modelsethad.com/naples-italy-beautiful-escort-girls.html">Naples, Italy beautiful escort girls</a>
<a href="https://modelszpagreed.info/elite-prostitutes-rostov-region.html">Elite prostitutes Rostov region</a>
<a href="https://escortiosaw.com/eskort-uslugi-ocharovatelnih-devushek-mayrhofen-tsillertal.html">√Э√±√™√Ѓ√∞√≤ √≥√±√Ђ√≥√£√® √Ѓ√Ј√†√∞√Ѓ√Ґ√†√≤√•√Ђ√Љ√≠√ї√µ √§√•√Ґ√≥√Є√•√™ √М√†√©√∞√µ√Ѓ√і√•√≠ & √Ц√®√Ђ√Ђ√•√∞√≤√†√Ђ√Љ</a>
<a href="https://modelsertook.com/elite-prostitutes-albania-vip-escort-from-model-girls-albania-vip-profiles-of-elite-courtesans.html">Elite prostitutes Albania - VIP escort from model girls Albania, VIP profiles of elite courtesans</a>
<a href="https://modelsjrkiled.info/elitnie-eskort-uslugi-tverskaya-oblast.html">√Э√Ђ√®√≤√≠√ї√• √љ√±√™√Ѓ√∞√≤ √≥√±√Ђ√≥√£√® √Т√Ґ√•√∞√±√™√†√њ √Ѓ√°√Ђ√†√±√≤√Љ</a>
<a href="https://modelsjypased.info/villeurbanne-france-escort-agency.html">Villeurbanne, France escort agency</a>
<a href="https://escortetsked.com/elitnie-prostitutki-kartahena-ispaniya-vip-eskort-ot-devushek-modeley-kartahena-ispaniya-vip-anket.html">√Э√Ђ√®√≤√≠√ї√• √ѓ√∞√Ѓ√±√≤√®√≤√≥√≤√™√® √К√†√∞√≤√†√µ√•√≠√†, √И√±√ѓ√†√≠√®√њ - Vip √љ√±√™√Ѓ√∞√≤ √Ѓ√≤ √§√•√Ґ√≥√Є√•√™ √ђ√Ѓ√§√•√Ђ√•√© √К√†√∞√≤√†√µ√•√≠√†, √И√±√ѓ√†√≠√®√њ, √Ґ√®√ѓ √†√≠√™√•√≤</a>
<a href="https://escortetsked.com/elitnoe-eskort-agentstvo-sochi.html">√Э√Ђ√®√≤√≠√Ѓ√• √љ√±√™√Ѓ√∞√≤ √†√£√•√≠√≤√±√≤√Ґ√Ѓ √С√Ѓ√Ј√®</a>
<a href="https://modelsqigave.com/beruvela-shri-lanka-vip-eskort-servis.html">√Б√•√∞√≥√Ґ√•√Ђ√†, √Ш√∞√®-√Л√†√≠√™√† VIP √Э√С√К√О√Р√Т-√С√Е√Р√В√И√С</a>
<a href="https://modelsjrkiled.info/elitnie-prostitutki-kitay-dunguan.html">√Э√Ђ√®√≤√≠√ї√• √ѓ√∞√Ѓ√±√≤√®√≤√≥√≤√™√® √К√®√≤√†√© √Д√≥√≠√£√≥√†√≠√Љ</a>
<a href="https://escortjourned.info/escort-girls-magnitogorsk.html">Escort girls Magnitogorsk</a>
<a href="https://odelsfechrged.info/vip-devushki-soldeu-i-el-tarter.html">Vip √§√•√Ґ√≥√Є√™√® √С√Ѓ√Ђ√Љ√§√•√≥ √® √Э√Ђ√Љ-√Т√†√∞√≤√•√∞</a>

2019-11-11 09:48:47
Georgewonry
<a href=https://www.xn--propeciapreoportugal-e1b.nu>propecia pre√Іo</a>
To give up smoking cigarettes, you really should take into consideration undergoing hypnotherapy. Throughout a period, the specialist will place you right into a calming, dreamlike state and explain to you not to smoke cigarettes. Also, the individual may possibly replicate a number of reasons that you should give up allowing you to have this in your head whenever you awaken.
<a href=https://www.priligybelgique.nu>priligy belgique</a>
If you have been eating a lot of unhealthy food these days, you should attempt food preparation a proper food or having a greens. This will aid to recharge your body to enable you to feel good whenever you wake up every morning. Reducing the fat articles in the body may help your worries.
<a href=https://www.xn--levitragnriquesuisse-i2bb.nu>levitra générique</a>
Even though it may be beneficial to have some fruits in your diet, make certain you have the fresh fruit from consuming true fresh fruit and not from consuming fruit juices. Most fruit drinks include a great deal of added glucose so it will be less healthy as having fruit.
<a href=https://www.cialisschweiz.ch>cialis generika</a>
¬аше им€: *
“екст записи: *
»м€:

ѕароль:



–егистраци€

 акой марки ваш мобильник?
Nokia
40% (146)
Samsung
8% (29)
Siemens
16% (59)
Motorola
13% (49)
Sony Ericsson
13% (49)
LG
1% (4)
Pantech
0% (0)
Alcatel
2% (6)
ƒругой
3% (10)
Ќет у мен€ мобилы
4% (15)

ѕроголосовало: 367
¬о врем€ раскопок в јмерике были найдены кусочки медной проволоки. ѕосле долгих исследований было вы€снено, что это - остатки древней кабельной сети, которой пользовались индейцы. ¬о врем€ раскопок в √ермании были найдены кусочки стекла. ѕосле долгих исследований было вы€снено, что это - остатки оптоволоконной сети древних нибелунгов. ¬ –оссии долго копали, копали, копали... и не нашли ничего. “ак было вы€снено, что древние слав€не пользовались спутниковой св€зью.
–ейтинг: 6.7/10 (3)
ѕосмотреть все анекдоты