Дополнительные особенности программирования для WebSphere MQ
Channel-exit программы
Channel - exits программы выполняются канальными агентами. Эти программы первыми и последними встречают и провожают сообщения. Именно поэтому их чаще всего используют для шифрования, проверки идентификации и аутентификации сообщений в целях безопасности.
Иногда возникает потребность применять Channel- exits программы, например, для того чтобы проследить прохождение сообщений и выполнить их логирование, выявить возникшие проблемы. Допустим, что стандартное ПО этого не делает, "вклиниваться" в него не представляется возможным, так как все входящие сообщения считывает из очереди программа-обработчик. В этом случае можно написать простую Channel- exits программу в виде библиотечной функции для UNIX или как DLL (Dynamic Linl Library) для Windows. Программе следует присвоить имя, к примеру, mqexit.dll с точкой входа MsgExit. Для работы программы, ее надо прописать в том канале, через который идут сообщения:
С помощью программы RUNMQSC это может быть сделано командой:
alter chl(mychannel) chltype(mychannel_type) msgexit(' mqexit(MsgExit)') msgdata('\mydir\mqexit.ini')
Программа mqexit.dll и настроечный файл mqexit.ini помещаются в стандартной директории (mydir ) для Channel exits программ (для Windows это C:\Program Files\IBM\WebSphere MQ\Exits\, для UNIX HP_UX это /var/mqm/exits/, аналогичные пути существуют для других платформ).
Настроечный файл mqexit.ini может содержать имя лог файла, имя буферного файла для записи входящего сообщения; имя очереди, в которую перекладывается содержимое буферного файла; имя менеджера, ведь менеджеров может быть несколько на одном сервере.
В учебных целях стоит упростить задачу, не использовать mqexit.ini-файл и записать имя лог-файла непосредственно в Message Exit Data (например, C:\TEMP\mqexit.log ), а с сообщениями пусть разбирается прежняя программа-обработчик, читающая входящие сообщения из очереди. При этом предполагается, что используется менеджер очередей по умолчанию. В этом случае программа mqexit.dll для Windows выглядит следующим образом.
/* Листинг программы mqexit.dll */ /************************************************************************/ /* Program name: mqexit */ /* Description: This is a sample message exit function record all messages */ /* This function will work on any channels, e.g. send, receive, requester, etc. */ /* This function logs all channel activities into the Channel Log file. */ /* Please see Intercommunication documentation to specify the Channel Message Exit */ /* Authors: Vladimir Makushkin, Moscow, Russia */ /* email: vmakushkin@mail.ru */ /* Disclaimer: this program has been tested under HP_UX and Windows to the */ /* authors' satisfaction. You may use this program at your own risk. Author are not */ /* responsible for any unexpected results generated by this program. */ /************************************************************************/ /* standard headers */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <errno.h> #include <locale.h> #include <sys/types.h> #include <sys/timeb.h> #include <time.h> /* MQSeries headers */ #include <cmqc.h> #include <cmqxc.h> char timebuf??(130??); char Channel_Logfile??(129??); char MsgBufFile??(129??); char ini_filename??(129??); FILE * OST = 0; #define DBGPRINTF(x) { \ OST=fopen(Channel_Logfile,"a+"); \ if(OST) fprintf x; \ fclose(OST); \ } void writer (long *, char *); /* prototype for file writer function */ void getINI( void ); void MQENTRY MQStart(void) {;} void MQENTRY MsgExit( PMQCXP pChannelExitParams, PMQCD pChannelDefinition, PMQLONG pDataLength, PMQLONG pAgentBufferLength, PMQBYTE AgentBuffer, PMQLONG pExitBufferLength, PMQPTR pExitBufferAddr) { short i; struct timeb t; char millisec??(25??); time_t clock = time( (time_t*) NULL); struct tm *tmptr = localtime(&clock); strcpy(timebuf, asctime(tmptr)); ftime( &t ); memset( millisec, 0, sizeof( millisec ) ); sprintf( millisec, " Time:%ld.%d", t.time, t.millitm ); strcat( timebuf, millisec ); if ( strlen(pChannelExitParams->ExitData) ) { memset( ini_filename, 0, sizeof( ini_filename ) ); strncpy( ini_filename, pChannelExitParams->ExitData, MQ_EXIT_DATA_LENGTH ); /*------------------------*/ /* remove space from data */ /*------------------------*/ for ( i = MQ_EXIT_DATA_LENGTH - 1; i > 0; i-- ) { if ( ini_filename??(i??) != ' ' && ini_filename??(i??) ) break; ini_filename??(i??) = 0; } } getINI(); void writer(long *lngth, char *bf) { long msgsize; int n; FILE *fp; getINI(); fp = fopen(MsgBufFile, "ab"); msgsize = *lngth; n = fwrite(&msgsize, sizeof(msgsize), 1, fp); /* put record length */ n = fwrite(bf, msgsize, 1, fp); /* and message buffer */ fclose(fp); } /*--------------------------------------------------------- getINI() - read information from INI file -----------------------------------------------------------*/ void getINI( void ) { FILE *ini_fp; char *ptr, *name_ptr; char field_val??(129??), Ini_s??(129??), fn??(129??); short len; memset( Ini_s, 0, sizeof( Ini_s ) ); memset( field_val, 0, sizeof( field_val ) ); memset( MsgBufFile, 0, sizeof ( MsgBufFile ) ); memset( Channel_Logfile, 0, sizeof( Channel_Logfile ) ); if ( strlen( ini_filename ) ) /* if Channel Exit defined INI filename */ strcpy( fn, ini_filename ); /* use it */ else /* otherwise use default INI filename */ strcpy( fn, INI_FILENAME ); if ( (ini_fp = fopen( fn, "r" )) == NULL ) { fprintf(stderr, "\nUnable to open %s. Error #: %d", fn, errno ); return; } /*-----------------------------*/ /* read the INI file until eof */ /*-----------------------------*/ while ( (ptr = fgets( Ini_s, sizeof(Ini_s) - 1, ini_fp )) != NULL ) { len = strlen( Ini_s ); if ( Ini_s??(len -1??) == LINEFEED ) Ini_s??(len-1??) = 0; /* null out '\n' */ if ( (name_ptr = strchr( Ini_s, EQUAL )) == NULL ) /* no '=' found */ continue; ptr = name_ptr + 1; *name_ptr = 0; /* set null for s[] */ strcpy( field_val, ptr ); fclose( ini_fp ); return; }Листинг 11.1. Листинг программы mqexit.c
В качестве комментария к программе надо отметить, что обязательным является соблюдение трех рекомендаций для Windows из документа [ 8 ] - Intercommunication.
- Начало программы следует определить точками входа MQStart и ChannelExit программы mqexit.c:
#include <cmqc.h> #include <cmqxc.h> void MQStart() {;} /* dummy entry point - for consistency only */ void MQENTRY ChannelExit ( PMQCXP pChannelExitParms, PMQCD pChannelDefinition, PMQLONG pDataLength, PMQLONG pAgentBufferLength, PMQVOID pAgentBuffer, PMQLONG pExitBufferLength, PMQPTR pExitBufferAddr) { ... Insert code here }
- При компиляции добавить в проект как исходный файл MQMVX.LIB. В настройках проекта для генерации C/C++ кода изменить выпадающее меню с "Use Run-Time Library" на "Multithreaded" для "Multithreaded using DLL".
- Добавить в проект свой .DEF файл ( mqexit.def ):
LIBRARY mqexit DESCRIPTION 'Provides Retry and Channel exits' HEAPSIZE 4096 STACKSIZE 8192 EXPORTS ChannelExit
И после этого проходящие по каналу сообщения начинают записываться в файл, определенный в Message Exit Data, включая заголовок и данные сообщения. Скорость прохождения сообщений с использованием Channel exits программ уменьшится за счет команд записи на диск. Читателю предлагается исследовать самостоятельно во сколько раз изменится скорость работы программы и найти пути её повышения.
В заключение лекции следует отметить, что остались механизмы, которые не нашли отражение в этой книге и могли бы представлять интерес для читателя-программиста, а именно:
- Разработка программ на основе интерфейсов JMS и AMI [ 21 ] ;
- PCF - команды (Programmable Command Format) и работа с ними;
- встроенные средства мониторинга событий WebSphere MQ.
С этими вопросами читателю предлагается ознакомиться самостоятельно по документации.
Ключевым моментом интеграции приложений является использование WebSphere Business Integration Message Broker , сокращенно WBI Message Broker или просто WBI Broker (ранее - IBM MQSeries Integrator ) для управления и преобразования потоков сообщений. Этот продукт компании IBM будет рассмотрен далее.