Компания IBM
Опубликован: 14.12.2004 | Доступ: свободный | Студентов: 1528 / 138 | Оценка: 4.36 / 3.98 | Длительность: 16:32:00
ISBN: 978-5-9556-0031-4
Специальности: Системный архитектор
Лекция 10:

Основы программирования для WebSphere MQ

< Лекция 9 || Лекция 10: 1234 || Лекция 11 >

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

Использование механизма списков распространения (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\n", __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\n");
     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] == '\n')		                  	/* last char is a new-line     */
       {
         buffer[buflen-1]  = '\0';				/* 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\n", 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\n", 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\n", 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\n", 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\n", Reason);
     }
   }

   if (DisconnectRequired==True)
   {
     MQDISC(&Hcon, &CompCode, &Reason);          
     if (Reason != MQRC_NONE)
     {
       printf("MQDISC ended with reason code %d\n", 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:\n\n");
 printf("Distlist Qmgr QName1 [QName2 [QName3 [...]]]\n\n");
}

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\n"
            , 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Гбт
200 1 1
400 1 1
600 1 1
800 2 1
1000 3 2
1200 3 2

Таким образом, WebSphere MQ дает нам удобный механизм рассылки постоянно обновляющейся информации для 200, 400, 600 … клиентов. Верхняя граница числа очередей (клиентов) зависит от операционной системы и оперативной памяти компьютера. Число клиентов (очередей) ограничивается 1200 на Windows компьютере с памятью 512Мбт.

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

< Лекция 9 || Лекция 10: 1234 || Лекция 11 >