私は自分の会社のためにメール送信者の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>
私が見逃している単純なものはありますか?それとも、問題を回避するためのより良い方法はありますか?