| Россия |
Основы программирования для 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
Ниже приводится листинг программы 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), которая будет рассмотрена подробно в следующей лекции.
