3

私は自分の会社のためにメール送信者のWebサービスを書いています。主な要件の1つは配信の保証であるため、永続的なQPidキューを使用して、JMSトランスポート上に薄いHTTPレイヤーがあります。

私が直面している問題の1つは、処理中のエラーの処理です。エラーが発生したときに単にトランザクションをロールバックすると、メッセージはキューの先頭に移動します。エラーが十分に蔓延している場合、誰かが手動で介入するまでキュー全体がロックされる可能性があります。その間にメッセージを処理できるように、ヘッドにポストバックすることでこれを回避したいと思います。

しかし、そこに私の問題があります。まず、AMQPにはメッセージを確認する代わりにアトミックに「拒否して再キューイング」するメカニズムがありますが、JMSにはこの機能の類似物がないようです。そのため、メッセージにアクセスする唯一の方法はキャストすることです。特定のJMS実装。さらに、CXFのJMSトランスポートには、トランスポートレベルで動作をオーバーライドまたは挿入する手段がないようです。つまり、バイトコードエージェントを作成するか、コードを変更して、必要な動作を取得するためだけに再コンパイルする必要があります。

この問題を回避するために、CXFメッセージからJMSメッセージを再構築し、それを再キューイングするだけのフォールトハンドラーをCXFに実装するというアイデアを試しました。しかし、障害が原因でオーバーライドできないロールバックが発生するため、トランザクションセッションを使用できません。その後、メッセージのコピーがヘッド(ロールバックから)とテール(ロールバックから)に表示されます。再キューから)。また、CLIENT_ACKNOWLEDGEを使用することはできません。これは、JMSトランスポートがメッセージを処理のために送信する前に確認応答するためです。つまり、サーバーが間違ったタイミングでダウンした場合、メッセージが失われる可能性があります。

したがって、基本的に、JMSトランスポートのデフォルトの動作を受け入れ続ける限り、データの整合性を損なうことなく、必要な動作(失敗したメッセージの再キューイング)を取得することは不可能のようです。

同僚は、JMSトランスポートを完全に避け、キューを直接呼び出すことを提案しました。その場合、サービスの実装は、メッセージをキューに入れるためだけに存在するスケルトンクラスになり、別のプロセスがメッセージリスナーを実装します。私にとって、このソリューションは、不可知論者のWebサービスの優雅さを失い、実装を基盤となるテクノロジーに結合することによってある程度のスケーラビリティを失うため、最適ではありません。

また、RabbitMQクライアントライブラリを使用してAMQP用のCXFトランスポートを作成することも検討しました。時間がかかりますが、これからも当社が使い続けられるものであり、CXFプロジェクトに貢献できるものかもしれません。とは言うものの、コードの記述、実行、テストに時間がかかるため、私はこのアイデアに夢中ではありません。

これがCXF用のbeans.xmlです。

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:util="http://www.springframework.org/schema/util"
    xmlns:context="http://www.springframework.org/schema/context"
    xmlns:jaxrs="http://cxf.apache.org/jaxrs"
    xmlns:jms="http://cxf.apache.org/transports/jms"
    xmlns:p="http://www.springframework.org/schema/p"
    xsi:schemaLocation="
    http://www.springframework.org/schema/beans     http://www.springframework.org/schema/beans/spring-beans.xsd
    http://www.springframework.org/schema/util      http://www.springframework.org/schema/util/spring-util.xsd
    http://www.springframework.org/schema/context   http://www.springframework.org/schema/context/spring-context.xsd
    http://cxf.apache.org/jaxrs                     http://cxf.apache.org/schemas/jaxrs.xsd
    http://cxf.apache.org/transports/jms            http://cxf.apache.org/schemas/configuration/jms.xsd">

    <import resource="classpath:META-INF/cxf/cxf.xml" />
    <import resource="classpath:META-INF/cxf/cxf-extension-soap.xml" />
    <import resource="classpath:META-INF/cxf/cxf-servlet.xml" />

    <context:component-scan base-package="com.edo" />

    <bean id="jmsConnectionFactory" class="org.apache.qpid.client.AMQConnectionFactory">
        <constructor-arg name="broker" value="tcp://localhost:5672"/>
        <constructor-arg name="username" value="guest"/>
        <constructor-arg name="password" value="guest"/>
        <constructor-arg name="clientName" value=""/>
        <constructor-arg name="virtualHost" value=""/>
    </bean>

    <bean id="jmsTemplate" class="org.springframework.jms.core.JmsTemplate" p:explicitQosEnabled="true" p:deliveryMode="1" p:timeToLive="5000" p:connectionFactory-ref="jmsConnectionFactory" p:sessionTransacted="false" p:sessionAcknowledgeModeName="CLIENT_ACKNOWLEDGE" />
    <bean id="jmsConfig" class="org.apache.cxf.transport.jms.JMSConfiguration" p:connectionFactory-ref="jmsConnectionFactory" p:wrapInSingleConnectionFactory="false" p:jmsTemplate-ref="jmsTemplate" p:timeToLive="500000" p:sessionTransacted="false" p:concurrentConsumers="1" p:maxSuspendedContinuations="0" p:maxConcurrentConsumers="1" />

    <jms:destination id="jms-destination-bean" name="{http://test.jms.jaxrs.edo.com/}HelloWorldImpl.jms-destination">
        <jms:address jndiConnectionFactoryName="ConnectionFactory" jmsDestinationName="TestQueue">
            <jms:JMSNamingProperty name="java.naming.factory.initial" value="org.apache.activemq.jndi.ActiveMQInitialContextFactory"/>
            <jms:JMSNamingProperty name="java.naming.provider.url" value="tcp://localhost:5672"/>
        </jms:address>
        <jms:jmsConfig-ref>jmsConfig</jms:jmsConfig-ref>
    </jms:destination>

    <jaxrs:server id="helloWorld" address="/HelloWorld" transportId="http://cxf.apache.org/transports/jms">
      <jaxrs:serviceBeans>
        <ref bean="helloWorldBean"/>
      </jaxrs:serviceBeans>
      <jaxrs:inInterceptors>
        <bean class="com.edo.jaxrs.jms.test.FlowControlInInterceptor" p:periodMs="1000" p:permitsPerPeriod="18" />
      </jaxrs:inInterceptors>
      <jaxrs:providers>
        <bean class="org.apache.cxf.jaxrs.provider.JSONProvider">
          <property name="produceMediaTypes" ref="jsonTypes"/>
          <property name="consumeMediaTypes" ref="jsonTypes"/>
        </bean>
      </jaxrs:providers>
    </jaxrs:server>

    <bean id="http-jms-config" class="com.edo.jaxrs.jms.test.HttpOverJmsConfig" 
        p:jmsFactory-ref="jmsConnectionFactory" 
        p:jmsDestinationName="TestQueue" />

    <util:list id="jsonTypes">
      <value>application/json</value>
      <value>application/jettison</value>
    </util:list>

</beans>

私が見逃している単純なものはありますか?それとも、問題を回避するためのより良い方法はありますか?

4

1 に答える 1

0

だから - 私は同僚のアドバイスを受けて、Web サービスに JMS トランスポートを使用していません。代わりに、Spring Integration 上に薄い Web サービス レイヤーを作成します。これにより、メッセージング層を不必要に公開することなく、必要な制御の粒度を得ることができます。

于 2011-12-13T15:34:38.497 に答える