[英]Implementing native websphere MQ with CoD over Camel JMS component
我很難用Apache CAMEL來實現Websphere MQ(WMQ)連接器,該連接器可以無例外地處理MQ交付確認(CoD)報告,並且不會產生不需要的響應數據報形式的副作用。 最后,如果您習慣於編寫本機MQ客戶端,則可以按照我想要的方式進行操作,這是一種標准且通用的方法。 我在同一主題的帖子中記錄了該方法,但是我發現該解決方案充滿了復雜性,非常感謝任何建議或示例以使實現更簡潔,更優雅。
我了解到,該問題的根源是MQ設計請求-應答消息交換模式(MEP)的方式,JMS規范的方式以及其JMS組件中請求-應答MEP的CAMEL實現。 三種不同的哲學!
我願意通過遠程Websphere隊列管理器通過傳遞確認(CoD)報告來支持本機MQ消息交換,因此,除了事務和持久性(即無丟失,無重復)之外,還可以跟蹤何時消耗一條消息並在出現延遲時發出警報。
默認情況下,Websphere隊列管理器在隊列中的消息使用完成時會生成CoD報告。 因此,在沒有任何特定設置的情況下 ,當CAMEL端點使用該消息時,遠程MQ客戶端發送帶有CoD標志的數據報(然后是強制性的ReplyToQ)將作為第一個MQ報告從隊列管理器返回到隊列管理器,然后是第二個。由CAMEL明確返回的(意外)回復消息,其中包含CAMEL路由末尾在Exchange對象中留下的內容,因為鑒於存在JMSReplyTo字段,CAMEL假定了請求-應答EIP(由MQ ReplyToQ和ReplyToQMgr映射,兩者需要以支持CoD回流)。
沒有特定設置,CAMEL默認情況下也會在出站連接上假設一個請求-答復EIP / MEP。 然后,CAMEL JMS / MQ端點將等待1個響應。 當OUTbound消息是基於MQ的JMS時(因此帶有MQRFH2標頭),則工作正常。 當強制使用普通的原始MQ時,即按如下所示刪除MQRFH2標頭時,我無法使端點偵聽器與相關的傳入MQ報告相匹配,盡管跟蹤的值似乎都正確(強制使用24個字符的相關性ID,以便較長的CorrelId值被截斷或填充為空) MQ不能對關聯過濾器進行地理解析)。 有誰能夠解決這個問題?
詳細信息:盡管IBM JMS API接受傳遞特定的JMS屬性值WMQ_MESSAGE_BODY = {1 | 0} / WMQ_TARGET_CLIENT = {1 | 0}來控制生成的消息中JMS頭MQRFH2的存在,但是這些選項通過CAMEL無效。 必須使用CamelJmsDestinationName標頭( 如CAMEL JMS doc中所述 )為具有選項“ targetClient = 1”的目標提供IBM隊列URL,以便擺脫MQRFH2標頭。 但是,如果沒有此標頭,則CoD報告或MQ答復上的CAMEL關聯確實會失敗。
解決上述問題的方法確實是關於構建特定的CAMEL路由來處理由遠程方返回的CoD報告(以及相關的MQ答復)。 因此,CAMEL出站郵件必須強制為“ InOnly ” ExchangePattern ,因此不要等待任何答復。 但這將導致CAMEL抑制所有的ReplyTo字段。 然后,如果在出站MQ數據報上請求MQ CoD,則會發生CAMEL異常,其原因是MQException JMSCMQ0001: WebSphere MQ call failed with compcode '2' ('MQCC_FAILED') reason '2027' ('MQRC_MISSING_REPLY_TO_Q')
。
CAMEL記錄了一個URI選項“ disableReplyTo = true”,以禁用交換模式上的回復偵聽,但保留ReplyTo字段-顯然在入站和出站交換上。 但是,此選項在出站JMS交換上不起作用(據觀察,我可能是錯的),並且必須使用不太直觀的“ preserveMessageQos”選項。
歡迎為這些問題提供優雅的解決方案。
我所能得到的最好的記錄在下面,以Spring XML應用程序上下文為例,該上下文本身承載CAMEL上下文和路由。 它與IBM原生MQ JCA兼容資源適配器v7.5,CAMEL 2.15,Spring core 4.2一起使用。 我可以將其部署到Glassfish和Weblogic服務器上。
當然,在給定眾多變量的情況下,實際實現中會使用Java DSL。 這個基於CAMEL XML DSL的示例是獨立的,易於測試。
我們從Spring和Camel聲明開始:
<?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:camel="http://camel.apache.org/schema/spring"
xmlns:jee="http://www.springframework.org/schema/jee"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd
http://camel.apache.org/schema/spring http://camel.apache.org/schema/spring/camel-spring.xsd
http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-3.0.xsd">
CAMEL上下文遵循2條路由:從MQ到JMS和從JMS到MQ,在這里鏈接起來形成了簡化測試的橋梁。
<camel:camelContext id="mqBridgeCtxt">
<camel:route id="mq2jms" autoStartup="true">
怪異的:使用本地MQ資源適配器時,獲取(例如)3個偵聽器的唯一方法是強制執行3個連接(依次使用3個Camel:from語句),每個連接最多具有1個會話,否則會出現MQ錯誤: MQJCA1018: Only one session per connection is allowed
。 但是,如果改為使用MQ客戶端jar,則CAMEL JMS中的concurentConsumers選項可以正常工作。
<camel:from uri="wmq:queue:TEST.Q1?concurrentConsumers=1&disableReplyTo=true&
acknowledgementModeName=SESSION_TRANSACTED"/>
<camel:from uri="wmq:queue:TEST.Q1?concurrentConsumers=1&disableReplyTo=true&
acknowledgementModeName=SESSION_TRANSACTED"/>
<camel:from uri="wmq:queue:TEST.Q1?concurrentConsumers=1&disableReplyTo=true&
acknowledgementModeName=SESSION_TRANSACTED"/>
上面的disable disableReplyTo選項可確保CAMEL在我們測試MQ消息類型為1 = Request(-reply)或8 = datagram(單向!)之前不會產生回復。 該測試和答復的構造未在此處說明。
然后,在下一次發布到純JMS時,我們將EIP強制為InOnly,以與入站MQ模式保持一致。
<camel:setExchangePattern pattern="InOnly"/>
<!-- camel:process ref="reference to your MQ message processing bean fits here" / -->
<camel:to uri="ref:innerQueue" />
</camel:route>
接下來是jms到MQ路由:
<camel:route id="jms2mq" autoStartup="true">
<camel:from uri="ref:innerQueue" />
<!-- remove inner message headers and properties to test without inbound side effects! -->
<camel:removeHeaders pattern="*"/>
<camel:removeProperties pattern="*" />
<!-- camel:process ref="reference to your MQ message preparation bean fits here" / -->
現在出現了由遠程目標返回的MQ CoD報告的請求標志。 我們還將MQ消息強制為數據報類型(值8)。
<camel:setHeader headerName="JMS_IBM_Report_COD"><camel:simple resultType="java.lang.Integer">2048</camel:simple></camel:setHeader>
<camel:setHeader headerName="JMS_IBM_Report_Pass_Correl_ID"><camel:simple resultType="java.lang.Integer">64</camel:simple></camel:setHeader>
<camel:setHeader headerName="JMS_IBM_MsgType"><camel:simple resultType="java.lang.Integer">8</camel:simple></camel:setHeader>
可以通過ReplyTo uri選項指定ReplyTo隊列,也可以如下指定標題。
接下來,我們確實使用CamelJmsDestinationName標頭來強制禁止JMS MQ消息標頭MQRFH2 (使用targetClient MQ URL選項值1)。 換句話說,我們要發送普通的原始MQ二進制消息(即,僅MQMD消息描述符,后跟有效負載)。
<camel:setHeader headerName="JMSReplyTo"><camel:constant>TEST.REPLYTOQ</camel:constant></camel:setHeader>
<camel:setHeader headerName="CamelJmsDestinationName"><camel:constant>queue://MYQMGR/TEST.Q2?targetClient=1</camel:constant></camel:setHeader>
可以通過保留的JMS屬性控制更多MQMD字段,如下所示。 請參閱IBM文檔中的限制 。
<camel:setHeader headerName="JMS_IBM_Format"><camel:constant>MQSTR </camel:constant></camel:setHeader>
<camel:setHeader headerName="JMSCorrelationID"><camel:constant>_PLACEHOLDER_24_CHARS_ID_</camel:constant></camel:setHeader>
URI中的目標隊列被上面的CamelJmsDestinationName覆蓋,因此URI中的隊列名稱成為占位符。
URI選項preserveMessageQos是一種-如所觀察到的那樣-允許發送設置了ReplyTo數據的消息(以獲取MQ CoD報告),但是通過強制InOnly MEP阻止CAMEL實例化Reply消息偵聽器。
<camel:to uri="wmq:queue:PLACEHOLDER.Q.NAME?concurrentConsumers=1&
exchangePattern=InOnly&preserveMessageQos=true&
includeSentJMSMessageID=true" />
</camel:route>
</camel:camelContext>
必須根據您的情況調整以下內容。 它為本地JMS提供程序和Websphere MQ(通過本地IBM WMQ JCA資源適配器)提供了隊列工廠。 我們在這里在管理對象上使用JNDI查找。
<camel:endpoint id="innerQueue" uri="jmsloc:queue:transitQueue">
</camel:endpoint>
<jee:jndi-lookup id="mqQCFBean" jndi-name="jms/MYQMGR_QCF"/>
<jee:jndi-lookup id="jmsraQCFBean" jndi-name="jms/jmsra_QCF"/>
<bean id="jmsloc" class="org.apache.camel.component.jms.JmsComponent">
<property name="connectionFactory" ref="jmsraQCFBean" />
</bean>
<bean id="wmq" class="org.apache.camel.component.jms.JmsComponent">
<property name="connectionFactory" ref="mqQCFBean" />
</bean>
</beans>
或者,如果您使用MQ客戶端jar而不是資源適配器,則將聲明連接工廠bean,例如(代替上面的JNDI查找):
<bean id="mqCFBean" class="com.ibm.mq.jms.MQXAConnectionFactory">
<property name="hostName" value="${mqHost}"/>
<property name="port" value="${mqPort}"/>
<property name="queueManager" value="${mqQueueManager}"/>
<property name="channel" value="${mqChannel}"/>
<property name="transportType" value="1"/> <!-- This parameter is fixed and compulsory to work with pure MQI java libraries -->
<property name="appName" value="${connectionName}"/>
</bean>
<bean id="wmq" class="org.apache.camel.component.jms.JmsComponent">
<property name="connectionFactory" ref="mqCFBean"/>
<property name="transacted" value="true"/>
<property name="acknowledgementModeName" value="AUTO_ACKNOWLEDGE"/>
</bean>
歡迎評論和改進。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.