Async Spring JMS + log4j через SSL

Опубликовал – 11.12.2011

Вступление.

Доброе время суток товарищи, это статья для тех, кто бы хотел использовать Java Message Service (JMS) в своих реализациях logging‘a. Прежде всего, эта модель пригодится тем, кто хочет собирать логи с нескольких серверов, и например, свапить их в DB для дальнейшего анализа. Я думаю не стоит объяснять, почему следует использовать не решение в лоб, т.е. заставить аппендер логера напрямую работать с базой. Но для тех кто в танке, поясню, что это не самая лучшая идея для высоконагруженного проекта по нескольким причинам, например насколько мой анализ показал, log4j работает синхронно, т.е. сервер будет регулярно висеть в ожидании коннекта к удаленной DB. Хотя я и не утверждаю, что мой способ лишен недостатков, потому что отправка MOM сообщения занимает около 3-5ms, если верить документации, но из двух зол выбираем меньшее.

Предполагается, что читатель уже имеет представление о технологии и хотя бы прочитал введение. Тут я предоставлю реализацию сперва сервера, а затем и клиента.

Общeе положениe.

И клиент и сервер должны общаться по одному протоколу, клиент должен знать местонахождения сервера, и ssl сертификат, поэтому пишем jmsCommon.xml, ее будем подключать и клиенту и серверу.

    <bean id="mappings"
          class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
        <property name="properties">
            <value>
                broker.url=ssl://localhost:61616
                jms.queue.server=queue.server
                jms.queue.client=queue.client
            </value>
        </property>
        <property name="systemPropertiesModeName" value="SYSTEM_PROPERTIES_MODE_OVERRIDE"/>
    </bean>

    <amq:queue id="serverDestination" physicalName="${jms.queue.server}"/>
    <amq:queue id="clientDestination" physicalName="${jms.queue.client}"/>

    <!-- a pooling based JMS provider -->
    <bean id="jmsFactory" class="org.apache.activemq.pool.PooledConnectionFactory"
          destroy-method="stop">
        <property name="connectionFactory">
            <bean class="org.apache.activemq.ActiveMQSslConnectionFactory">
                <property name="brokerURL" value="${broker.url}"/>
                <property name="keyStore" value="broker.ks"/>
                <property name="keyStorePassword" value="123456"/>
                <property name="trustStore" value="broker.ks"/>
                <property name="trustStorePassword" value="123456"/>
            </bean>
        </property>
    </bean>

    <!-- Spring JMS Template -->
    <bean id="myJmsTemplate" class="org.springframework.jms.core.JmsTemplate">
        <property name="connectionFactory">
            <ref local="jmsFactory"/>
        </property>
    </bean>

Тут все просто, мы создаем объект JmsTemplate, теперь мы можем отправлять сообщения разных видов, не задумываясь об обертках, за нас все уже написано. Про типы сообщений, которые можно отправлять через jms можно прочитать по ссылкам выше, мы же будем использовать ObjectMessage. Т.е. передать POJO объекты. Так же тут мы указываем где будет лежать ssl-сертификат broker.ks. Как сгенерировать такой сертификат, можно прочитать например тут.

Сервер.

Серверная часть должна импортировать весь jmsCommon.xml, и отличается от клиентской наличием брокера, который мы будем описывать в jmsBroker.xml.

    <amq:broker brokerName="ActiveMQBroker" useJmx="false" persistent="false">
        <amq:sslContext>
            <amq:sslContext keyStore="classpath:broker.ks" keyStorePassword="123456" trustStore="classpath:broker.ks"
                            trustStorePassword="123456"/>
        </amq:sslContext>
        <amq:transportConnectors>
            <amq:transportConnector uri="${broker.url}?needClientAuth=true"/>
        </amq:transportConnectors>
    </amq:broker>

Создаем слушателя:

public class LoggerMessageListener implements MessageListener {
    public void onMessage(Message message) {
        if (message instanceof ObjectMessage) {
            ObjectMessage objectMessage = (ObjectMessage) message;
            try {
                LoggingEvent loggingEvent = (LoggingEvent) objectMessage.getObject();
                System.out.println(loggingEvent.getTimeStamp() + " " + loggingEvent.getLevel() + " " + loggingEvent.getMessage());
            } catch (JMSException e) {
                //ignore
            }
        }
    }
}

Несомненно, надо проверить мало того, что сообщение передает объект, так и сам класс объекта, я этого делать не стал, поскольку этот пример не может передавать ничего кроме данного класса, но будьте внимательны!
И инициализация:

    <import resource="jmsCommon.xml"/>
    <import resource="jmsBroker.xml"/>

    <bean id="centralLogger" class="com.example.jms.server.LoggerMessageListener"/>

    <jms:listener-container connection-factory="jmsFactory" concurrency="3-10">
        <jms:listener destination="${jms.queue.client}" ref="centralLogger"/>
    </jms:listener-container>

Клиент

Клиент самый интересный этап. Тут мы будем писать свой Appender к log4j он будет очень простой. Для начала посмотрим его код, затем инициализацию:

public class JMSAppender extends AppenderSkeleton {
    private JmsTemplate jmsTemplate;
    private Destination destination;

    @Override
    protected void append(LoggingEvent event) {
        jmsTemplate.convertAndSend(destination, event);
    }

    @Override
    public void close() {

    }

    @Override
    public boolean requiresLayout() {
        return false;
    }

    public void setJmsTemplate(JmsTemplate jmsTemplate) {
        this.jmsTemplate = jmsTemplate;
    }

    public void setDestination(Destination destination) {
        this.destination = destination;
    }
}

Достаточно просто, не так ли? И вот самый главный виновник торжества, jmsLoggerAppender.xml, подключив который любому клиенту, вы будете получать логи нужного вам уровня на стороне сервера:

    <import resource="jmsCommon.xml"/>

    <bean class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
        <property name="targetObject">
            <bean factory-method="getRootLogger" class="org.apache.log4j.Logger"/>
        </property>
        <property name="targetMethod">
            <value>addAppender</value>
        </property>
        <property name="arguments">
            <list>
                <bean init-method="activateOptions" class="com.example.jms.JMSAppender">
                    <property name="jmsTemplate" ref="myJmsTemplate" />
                    <property name="destination">
                        <bean class="org.apache.activemq.command.ActiveMQQueue">
                            <constructor-arg value="${jms.queue.client}" />
                        </bean>
                    </property>
                    <property name="layout">
                        <bean class="org.apache.log4j.PatternLayout">
                            <constructor-arg>
                                <value>%d, [%5p] [%t] [%c] - %m%n</value>
                            </constructor-arg>
                        </bean>
                    </property>
                    <property name="threshold">
                        <bean class="org.springframework.beans.factory.config.FieldRetrievingFactoryBean"
                              id="org.apache.log4j.Level.INFO"/>
                    </property>
                </bean>
            </list>
        </property>
    </bean>

Поздравляю, если вы дочитали до этого места, значит вы справились. Это действительно очень просто и полезно!
И на последок, немного зависимостей, которые я использовал. Но не все, разумеется зависимости логгера и спринга не входят в рамки данной статьи.

Maven dependencies:

            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-jms</artifactId>
                <version>3.0.5.RELEASE</version>
            </dependency>

            <dependency>
                <groupId>org.apache.activemq</groupId>
                <artifactId>activemq-pool</artifactId>
                <version>5.5.1</version>
            </dependency>

Успехов!

Рассказать друзьям:
  • Добавить ВКонтакте заметку об этой странице
  • Мой Мир
  • Facebook
  • Twitter
  • Яндекс.Закладки
  • В Живую Ленту Google
  • Сто закладок
Комментарии (3) - Async Spring JMS + log4j через SSL

Ответ

  1. Глеб:

    Для асинхронности в log4j посмотри:

    Thumb up Thumb down 0

  2. Глеб:

    Упс, вырезали xml
    Посмотри class=»org.apache.log4j.AsyncAppender»

    Thumb up Thumb down 0

  3. Вадим:

    Где пример передачи сообщения с клиентской страницы?
    Нихера не понятно

    Thumb up Thumb down 0

Ответить

Comments

Перед отправкой формы: