![]() |
|
||
|
|
|
Введение в JMS(Алексей Букавнев) Предисловие Данная статья предназначена, как это следует из названия, для первичного ознакомления с одной из ключевых технологий J2EE, Java Message Service (JMS). К сожалению, устоявшейся русской терминологии J2EE не существует. Да и та неустоявшаяся, что существует, далека от идеала. Мне, например, очень не нравится, когда Session Beanназывают Сессионным Бином. Ну уж если он "Сессионный", то почему "Бин", а не "Зерно"? По этой причине автор будет использовать англоязычную оригинальную терминологию. Тем более что за время своего проживания и работы в Штатах он, к сожалению, основательно подзабыл орфографию и пунктуацию русского языка. Статья нисколько не претендует на полноту освещения темы – для этого существует достаточно большое количество литературы, в том числе и доступной online. Недостатки традиционных распределенных
систем Традиционные распределенные системы, построенные на основе RMI, обладают рядом существенных недостатков. Назовем основные: Клиент, сделав запрос, должен ждать ответа от сервера. Эта синхронность является основным недостатком традиционных систем в том случае, когда процесс обработки запроса занимает относительно большой промежуток времени. Примитивтые псевдо-асинхронные механизмы типа callback не в счет. И клиент, и сервер должны быть стартованы в сети. Если клиент попытается сделать запрос к серверу, который не активен в данный момент, он получит сообщение об ошибке. Невозможно перенести серверное приложение с одного хоста на другой в runtime. Лучше дела обстоят с CORBA. Используя CORBA Event Service или его расширение CORBA Notification Service, вы можете осуществлять эффективное асинхронное взаимодействие внутри системы. Но есть одно "НО". Производители CORBA middleware, в том числе и два лидера (Borland и Iona), предоставляют лишь рудиментарный Event Service. Полноценный Event и Notification Services необходимо приобретать у third-party производителей. Например, великолепный продукт OpenFusion от Prism Technologies, в котором реализованы все CORBA-сервисы (за мой достаточно большой практический опыт разработки CORBA-систем мне доводилось работать лишь с четырьмя из них: Naming, Trading, Event, Notification). К тому же CORBA достаточна сложна и требует очень высокой квалификации разработчиков. Достаточно сказать, что с точки зрения CORBA-специалиста EJB является всего лишь Java-имплементацией CORBA Object Framework в спецификации CORBA 3 (к сожалению, еще не реализованной ни одним производителем). Message-Oriented Middleware (MOM) Выходом из данной ситуации может служить Messaging System. Существует множество определений этого термина. Например, следующий: Messaging System– это распределенная система, основанная на асинхронном обмене сообщениями(messages) между компонентами системы. Message-Oriented Middleware – это, грубо говоря, тот продукт, на основе которого и строится Messaging System. Примером может служить IBM MQSeries. В отличие от традиционных систем, в Messaging Systemприложения общаются не напрямую, а посредством MOM. Если один компонент системы хочет послать сообщение другому компоненту, он посылает данное сообщение MOM, а уж MOM затем пересылает его адресату.
Рис. 1 MOM позволяет избавиться от перечисленных в п.2 недостатков. Приложение, пославшее сообщение, не должно ждать ответа, и может продолжать свою текущую деятельность. Ни приложение, посылающее сообщение, ни адресат данного сообщения не обязаны бать активными в одно и то же время. Если адресат сообщения не активен, то MOM гарантирует, что сообщение будет доставлено как только адресат станет активным. Компоненты системы не связаны напрямую друг с другом (decoupled), а потому возможно перенесение компонентов с одного хоста на другой в runtime без ущерба для работоспособности системы. Модели обмена сообщениями Существует две "основных" модели обмена сообщениями:
Point-to-point модель применяется, когда одному или нескольким компонентам (так называемые senders) необходимо посать сообщение одному компоненту-адресату (receiver). Модель основана на понятии message queue. Sendersпосылают сообщения в queue, а Receiver читает сообщения из queue.
Рис. 2 Часто говорят, что в point-to-point модели есть один и только один receiver. Однако это не совсем верно. Может существовать несколько receivers, присоединенных к одной и той же queue. Но MOMдоставит сообщение только одному из них. Какому именно – зависит от реализации. Некоторые MOM доставляют сообщение первому зарегистрированному receiver, в то время как существуют реализации, осуществляющие round-robin. Publish-subscribe модель применима, когда одному или нескольким компонентам (publishers) необходимо послать сообщение одному или нескольким компонентам-адресатам (subscribers). Данная модель основана на понятии message topic. Publishers посылают сообщения в topic, и все subscribersданного topic получают эти сообщения.
Рис.3 Какую именно модель использовать – это зависит от того, какой вы видите вашу систему во-первых, и какой MOM-продукт вы используете во-вторых. Не все реализации поддерживают обе модели. Pub-sub модель кажется более элегантной, и у разработчика может возникнуть желание строить все Messaging Systems на основе этой модели. Но следует знать, что многие pub-subсистемы не гарантируют доставку сообщений в том порядке, в каком они были посланы (в отличие от point-to-point, где queueреализует принцип first-in/first-out). Задача сохранения порядка следования сообщений не тривиальна даже в случае одного topic. А уж если необходимо сохранение порядка доставки сообщений в случае множественных topics – то сложность возрастает экспоненциально. Поэтому в случае pub-sub модели следует по возможности избегать ситуаций, когда порядок следования сообщений важен (либо используя заголовки и раздел свойств сообщений для синхронизации – об этом будет сказано ниже). Java Message Service (JMS) Java Message Service (JMS) – это Java API (то есть набор интерфейсов и классов) для работы с Message-Oriented Middleware. Данный набор определен в пакете javax.jms в дереве пакетов J2EE. JMS реализован во множестве MOM-продуктов, среди которых наиболее известны iPlanet Message Queue, IBM MQSeries, Progress Software SonicMQ, Bea WebLogic Server, и, конечно, мой любимый Prism Technologies OpenFusion, который к тому же позволяет интегрировать J2EE и CORBA. Существуют и freeware имплементации. JMS поддерживает обе "основных" модели обмена сообщениями. Однако спецификация не требует от производителя реализовывать обе модели. Впрочем, большинство JMS-продуктов на рынке реализуют как point-to-point, так и pub-sub. Важно отметить, что обмен сообщениями в JMS может быть частью транзакции, а значит сообщение не будет потеряно в случае возникновения ошибки. Далее мы рассмотрим использование "основных" моделей в случае JMS, но прежде следует ввести определенную в спецификации терминологию. ConnectionFactory – это, как нетрудно догадаться из названия, обьект, ответственный за создание JMS Connection. Администратор МОМ создает данный обьект и связывает его с деревом JNDI, так что клиент JMS может получить доступ к ConnectionFactory используя стандартный JNDI lookup-механизм. В случае point-to-point модели используется javax.jms.QueueConnectionFactory, в случае pub-sub модели – javax.jms.TopicConnectionFactory. Connection – абстрактное представление реального соединения между клиентом JMS и MOM. В случае point-to-point модели используется javax.jms.QueueConnection, в случае pub-sub модели – javax.jms.TopicConnection. Session – обьект, создаваемый JMS Connection и используемый клиентами для посылки и принятия сообщений. В случае point-to-point используется javax.jms.QueueSession, в случае pub-sub – javax.jms.TopicSession. Фактически, это главная "рабочая лошадка" JMS. Destination – это либо queue, либо topic – в зависимости от используемой модели: javax.jms.Queue или javax.jms.Topic. Как и ConnectionFactory, destination связывается с деревом JNDI. MessageProducer – обьект, который, собственно, и посылает сообщения. В случае point-to-point модели это javax.jms.QueueSender, в случае pub-sub – javax.jms.TopicPublisher. MessageConsumer – обьект, принимающий сообщения. В случае point-to-point модели это javax.jms.QueueReceiver, в случае pub-sub – javax.jms.TopicSubscriber. Message– сообщение. О типах сообщений будет сказано ниже. Использование Point - to - Point модели в
JMS Итак, приступим к созданию простейших sender и receiver.
Sender создан. Приступим к созданию receiver. Существует два пути получения сообщений. Первый – синхронное затребование сообщений из queue, используя метод receive() интерфейса javax.jms.QueueReceiver. Второй – асинхронное получение сообщений как только они становятся доступны – используя интерфейс javax.jms.MessageListener. Многие шаги в создании receiverаналогичны таковым для sender – эту часть оставим без комментариев. Первый пример receiver – с использованием receive().
Как видно из примеров, JMS API достаточно прост. В следующем разделе будет представлена pub-sub модель. Изменив point-to-point код всего в нескольких местах, Вы получите работающее pub-sub приложение. Использование Pub-Sub модели в JMS Pub-Sub publisher :
Как видите, изменения в коде незначительны. Pub-Sub subscriber: Как и в случае point-to-point, существует два способа получения сообщения: синхронный, с использованием метода receive() интерфейса TopicSubscriber, либо асинхронный, с использованием интерфейса MessageListener. Приведу лишь список необходимых изменений в обоих случаях. Итак, в синхронном варианте, получив (либо создав) Topic, создайте consumer:
После активизации connection, вы можете получать сообщения:
В случае асинхронного получения сообщений, как и прежде, реализуйте метод onMessage интерфейса MessageListener, и зарегистрируйте новый Listener с помощью метода setMessageListener интерфейса TopicSubscriber. Запустив несколько subscribers, вы увидите, что все они получают сообщения, посланные publishers. Говоря о pub-sub модели, важно отметить следующее. В отличие от point-to-point случая, где сообщение, посланное в queue, остается там до тех пор, пока receiver не получит это сообщение, pub-sub по умолчанию такой возможностью (durability) не обладает. Иными словами, сообщения, посланные в topic будут доставлены только тем subscribers, которые активны – то есть доступны на данный момент. Но не отчаивайтесь! Выход, конечно же есть! Все, что вам нужно сделать – создать durable subscriber:
Второй аргумент метода – это уникальное имя данного subscriber. Теперь можно быть уверенным, что сообщения, посланные в topic, данный subscriber получит, даже если он не активен в момент посылки сообщения – когда станет активным. Типы сообщений В JMX определены следующие стандартные типы сообщений: StreamMessage – сообщение, содержащее сериализованный поток обьектов MapMessage – сообщение, содержащее пары "ключ-значение" TextMessage – сообщение, содержащее строку ObjectMessage– сообщение, содержащее сериализованный обьект ByteMessage – сообщение, содержащее поток байтов Кроме того, некоторые имплементации (например, OpenFusion и WebLogic) предоставляют еще один "почти стандартный" тип: XMLMessage – расширение TextMessage, используется для доставки XMLсообщений Все типы сообщений являются подклассами javax.jms.Message. Заголовки и разделы свойств
сообщений Сообщения соостоят из трех частей: заголовка сообщения, раздела свойств и собственно тела сообщения. Заголовок сообщения содержит дополнительную информацию, которую разработчик может использовать в своем приложении. JMS предоставляет get и set методы для каждого поля заголовка. Некоторые из них устанавливаютя автоматически, другие могут быть использованы разработчиком приложения. JMSDestination – содержит имя destination, в который посылается сообщение. JMSDeliveryMode – определяет, является ли сообщение сохраняемым или нет. Если оно сохраняемо, то будет сохранено MOM в базе данных (или в файле), а потому оно переживет "гибель" системы, в отличие от несохраняемого сообщения. JMSExpiration – определяет, когда сообщение устареет и будет удалено из системы. По умолчанию сообщение не устаревает никогда. JMSPriority – как и следует из названия, определяет приоритет сообщения (от 0 до 9). По умолчанию равно 4. JMSMessageID – уникальный идентификатор сообщения JMSTimestamp – содержит информацию, когда именно MOM приняла сообщение от producer. JMSCorrelationID – может быть использовано разработчиком для согласования сообщений: например, если вы хотите переслать ряд сообщений, обьединенных в одну логическую группу (такую как набор товаров в заказе, при этом в каждое сообщение о товаре вы можете добавить в данное поле заголовка номер заказа). JMSReplyTo – может быть использовано разработчиком для того, чтобы consumer знал, кому (то есть в какой destination) при желании отсылать ответ. JMSType – поле может быть использовано разработчиком для того, чтобы дать приложению информацию, как обращаться с данным сообщением. Тип здесь понимается как application-specific type, а не тот, что использован выше в разделе "типы сообщений". JMSRedelivered – устанавливается, если сообщение не было доставлено с первой попытки – например, в случае, когда consumer не подтвердил получение сообщения (если он должен был подтвердить, конечно) Раздел свойств содержит пары "ключ-значение", которые могут быть использованы для пересылки определенных данных между producer и consumer. В качестве значений могут быть использованы примитивные типы (boolean, byte, float, double, short, int, long), а так же строки (java.lang.String). Устанавливаются и читаются они с помощью соответствующих set и getметодов. Например, для установки integer-свойства с ключом "MyProperty" и значением равным 100:
Для чтения:
Доставка сообщений Сообщение может быть сохраняемым или несохраняемым. Если сообщение сохраняемо, то MOM сохранит его в базе данных или файле. Такое сообщение переживет "гибель" MOM. Какой тип сообщения выбрать – зависит от того, что вы ожидаете от своей Messaging System: большей надежности (в случае сохраняемых сообщений), либо большей производительности (в случае несохраняемых). Вы можете установить режим доставки сообщения в момент его посылки(по умолчанию режим будет таким, каким его установил администратор при создании ConnectionFactory). Например:
Подтверждение получения сообщения JMS поддерживает три "основных" модели подтверждения получения сообщения. AUTO_ACKNOWLEDGE – в случае синхронного получения сообщений, подтверждение получения будет произведено автоматически, когда метод receive() возвратит значение не вызвав никакой исключительной ситуации. В случае асинхронного получения сообщений, подтверждение получения будет произведено, когда метод onMessage() вернет значение. DUPS_OK_ACKNOWLEDGE – работа по подтверждению получения сообщения перекладывается на Session. Сообщения будут вновь доставлены в случае возникновения ошибки или "гибели" системы. CLIENT_ACKNOWLEDGE – клиент должен вызвать метод acknowledge() интерфейса javax.jms.Message для того, чтобы явно подтвердить получение сообщения. При вызове данного метода будет подтверждено получение текущего и всех предидущих полученных сообщений. Какой тип подтверждения сообщения выбрать – опять-таки решать вам на основе анализа требований к вашей системе. AUTO_ACKNOWLEDGE – самый простой тип. Там где не требуется очень высокая производительность – этот тип вполне уместен. DUPS_OK_ACKNOWLEDGE используйте там, где нужна высокая производительность, однако помните, что минус в данном случае – все неподтвержденные сообщения будут передоставлены вновь. CLIENT_ACKNOWLEDGE – дает разработчику полный контроль над подтверждением получения сообщения. Посылка сообщений как часть
транзакции Очень часто возникает необходимость посылать сообщения в контексте транзакции. Например, получив сообщение из queue 1, consumerпосылает это сообщение в другую queue 2. В этот момент происходит исключительная ситуация. И Вы, естественно, хотите, чтобы система вернулась в первоначальное состояние, то есть сообщение вновь оказалось в queue 1. Одно из главных преимуществ JMS в том, что посылка сообщения может быть частью транзакции. Существует два подхода в использовании транзакций совместно с JMS. Один применим только внутри JMS, другой может быть использован для включения не только JMS но и, например, JDBC - запроса, в состав той же транзакции. Первый подход заключается в использовании transacted sessions. Как этого добиться? Очень просто – поставить флаг transacted при создании Вашей session в значение true:
Важно отменить, что в случае transacted session второй аргумент – тип подтверждения получения – хоть и присутствует, но игнорируется JMS. И подтверждение получения будет произведено, когда транзакция будет завершена методом commit() обьекта session, который должен быть вызван клиентом, получающим сообщение. Интерфейс javax.jms.Session включает два метода: commit() для подтверждения транзакции и rollback() для отката к первоначальному состоянию. В данном случае используется chained transaction model. Что это означает? Только то, что в случае успешного завершения транзакции, JMSавтоматически создает новую транзакцию. И producer, и consumer могут использовать transacted session. Когда producer использует transacted session, посланные сообщения накапливаются в буфере до тех пор, пока producer не вызовет либо commit(), либо rollback(). В случае вызова commit(), сообщения будут доступны для доставки, в случае вызова rollback(), JMS"очистит" буфер. В случае, когда consumer использует transacted session, этот sessionконтролирует подтверждение доставки сообщения. В случае вызова commit() производится подтверждение получения сообщений в контексте данной транзакции, в случае вызова rollback() JMS вернет все сообщения в соответствующий destination. Второй подход заключается в использовании JTA-транзакций совместно с JMS. Этот подход позволяет разработчику включать в единую транзакцию как посылку сообщений, так и запрос к базе данных или EJB. Достигается это стандартным способом – использованием javax.transaction.UserTransaction. Например,
При этом важно отметить, что JTA не может быть использован совместно с transacted session. Либо пироги, либо пряники. Если Вы попытаетесь совместить несовместимое, то увидите, что transacted session будет игнорировать вызовы commit() или rollback() интерфейса UserTransaction. Как и в случае transacted session, тип подтверждения получения сообщения будет игнорироваться. Когда будет вызван метод commit(), все посланные сообщения будут доступны для доставки. Будет произведено подтверждение для всех полученых сообщений. Message-Driven Beans Несколько слов о новом типе EJB в спецификации 2.0. Новая спецификация EJB 2.0 (реализованная на данный момент только в Weblogic 6.x и, кажется, JBoss 2.x), вводит 3 революционных изменения по онтошению к EJB 1.1
В предыдущей спецификации, при необходимости интергрировать JMS и EJB приходилось идти на различные ухищрения (Weblogic 5.1 для облегчения страданий разработчиков даже предоставлял для этой цели Startup класс). Вызвано это тем, что доступ непосредственно к EJB имеет только контейнер, для клиента же единственный выход – remote interface. Message Driven Bean (MDB) – это долгожданный плод любви EJB и JMS. Главная отличительная черта MDB – это то, что клиент не общается с ним посредством remote interface (ни Object-, ни Home-интерфейса MDB не имеет). Единственный способ общения – посылка сообщений. MDB – это просто MessageListener, и ничего больше: класс реализует javax.ejb.MessageDrivenBean и javax.jms.MessageListener. Первый из этих интерфейсов имеет всего два метода: setMessageDrivenContext и ejbRemove. Второй интерфейс вообще куцый – как Вы помните, он имеет лишь один метод onMessage. Спецификация требует также создания метода ejbCreate без параметров. Раз клиент не общается непосредственно с MDB, то он его и не создает. Контейнер сам решит, когда и сколько ему требуется MDB для обработки сообщений из данного destination. Главный недостаток MDB – он может принимать сообщения только из одного destination. Заключение В заключение приведу несколько ссылок на доступные в сети ресурсы по данной тематике. http://java.sun.com/products/jms/ http://www.jguru.com/ faq/home.jsp?topic=JMS http://www.theserverside.com/ home/index.jsp http://www.devx.com/upload/free/features/ javapro/2001/01jan01/ jm0101/jm0101.asp http://www.devx.com/upload/free/features/ entdev/2000/06jun00/rg0006/rg0006.asp http://www.prismtechnologies.com/English/ Downloads/DownloadsFrame.html http://e-docs.bea.com/wls/ docs61/jms/index.html http://www-4.ibm.com/ software/ts/mqseries/api/mqjava.html http://www.iplanet.com/products/ iplanet_message_queue/home_message_queue.html http://www.talarian.com/ products/jms/index.shtml http://www.fiorano.com/ products/products.htm http://www.objectweb.org/joram/ Об
авторе |
| Справка | Условия | |
| В начало | Логин | Комментарий к колонке | Поиск | Почта |