MQ配置和編程最佳實踐

MQ配置和編程最佳實踐

對於MQ的使用,主要會涉及到MQ系統本身的配置和MQ應用程序的開發兩方面的工作。為了幫助大家更好地使用MQ,本文將就MQ配置和編程中的一些注意事項和技巧與大家探討,並希望與大家分享這方面的一些最佳實踐(Best Practice)。

第一部分:有關MQ對象配置的最佳實踐

對於MQ系統配置,我們要規劃MQ通訊網路,確定系統的拓撲結構,確定各種對象的屬性和命名規則並創建所需的各種對象等,首先,我們談一談在系統建設之初,如何設計和定義MQ的各種對象。

1、有關隊列管理器:

創建隊列管理器時,應考慮的因素主要有:

1) 隊列管理器的日誌類型以及日誌文件的大小和個數,要根據用戶數據量的大小、各個隊列上的消息總容量,來計算日誌的總容量,以免在系統運行過程中出現日誌寫滿的情況;

2) 應該為隊列管理器指定和建立死信隊列;

3) 對最多打開句柄數MAXHANDS(預設為256,如果您需要多於256個應用程序同時連接隊列管理器,應增大該值),最大消息長度MAXMSGL,最多的未提交的消息個數MAXUMSGS屬性(預設為10000,如果您使用了消息分段或分組,某個大消息的分段個數超過了10000,應增大該值)的考慮;

4) 創建完隊列管理器之後,應修改隊列管理器的配置文件,考慮有關TCP和通道有關的參數的配置,舉例如下:

TCP:

KeepAlive=Yes

Channels:

AdoptNewMCA=ALL

PipeLineLength=2

MaxActiveChannels=200

2、有關隊列:

對於隊列的屬性,應該考慮的因素主要有:

1) 永久性和非永久性設置:尤其要注意的是DEFPSIST屬性的預設值為No,若要保證消息的安全可靠,必須將其設置為Yes;

2) 對於本地隊列和傳輸隊列,要考慮隊列的最大深度MAXDEPTH(預設為5000,應根據實際情況計算該值),隊列中每個消息的最大位元組數MAXMSGL的配置。

3、有關通道:

對於通道的屬性,應該考慮的因素主要有:

1) 確定通道的運行方式採用長連接的方式還是觸發的方式,通常,對於需要頻繁啟動的通道,不適宜採用觸發的方式。若採用觸發方式啟動通道,觸發類型應為FIRST;

2) 對於發送類型的通道,要考慮通道的斷開間隔(DISCINT)、短重試次數(SHORTRTY)、短重試間隔(SHORTTMR)、長重試次數(LONGRTY)、長重試間隔(LONGTMR)、批處理大小(BATCHSZ)的配置。

 

第二部分:有關MQ程序開發的最佳實踐

通常大家在使用MQ時,一般在系統設計之初只考慮MQ的系統配置,而很少提前考慮應用程序編寫的指導原則,往往是邊寫邊想,邊想邊寫,邊寫邊改,使得開發效率和質量都比較低,為了使大家更快、更好地開發出MQ應用程序,在該部分我們給出與MQ程序開發相關的一些最佳實踐,供大家參考。這裡我們無法做到面面俱到,並且由於每個用戶的具體需求不同,每條原則都不能一概而論,但是從普遍意義上可以作為您的參考。

為了更好地掌握和了解MQ編程的技巧,我們首先要熟悉MQ的消息通訊模式,這將幫助你更好地理解MQ編程的最佳實踐。通常,MQ有兩種通訊模式,即數據報(Datagram) 方式和請求/應答(Request/Reply) 方式:其中,Datagram方式通常又被稱為”Send And Forget”(發送/忽略),是最簡單的通訊模式,應用程序只需在創建完消息之後,利用MQ的API將消息發送到隊列中,它充分利用了MQ確保消息傳輸,並且傳一次且僅傳一次(once and once only)的優勢,發送端應用程序無需關心消息何時被處理。

Request/Reply(請求/應答)方式相對複雜一些,在消息發出之後,你需要等待對方的處理結果,在這種情況下,你通常需要考慮其他一些問題,如:

等待應答的時間是多少?
如果沒有收到應答,是否再次發出請求?
應答發出之前是否會有資料庫操作或其他交易被執行?
本次請求/應答過程的會話(session)信息是否需要被保留?

通常,我們要根據用戶的需求來決定採用何種通訊模式,不同的通訊模式之下對應用程序的考慮將會有所不同,使用的MQ API的參數和選項也將不同。

為了使你的MQ應用能夠更加健壯,並且具有更強的可維護性,我們要學會靈活高效地使用MQ的一些特性以及相關的API選項,從而提到應用程序的質量、靈活性、可靠性和性能。這裡,我們將給出一些較典型的建議。

  1. 在每一個MQ API調用之後,必須檢查完成碼(completion code)和原因碼(reason code),對於非零的返回碼,必須進行相應的處理,必要時,最好將返回碼記錄錯誤日誌,從而在應用程序出現運行故障時便於檢查和處理。

在MQ中,所有的API調用都會得到其完成碼和原因碼,完成碼有MQCC_OK、MQCC_WARNING、MQCC_FAILED等,MQCC_OK表示調用成功,當得到MQCC_WARNING、MQCC_FAILED返回碼時,表示API執行不完全成功,你需要進一步檢查原因碼,通過不同的原因碼分析失敗原因。如:原因碼2033代表隊列已空,沒有消息可取;2080代表消息的實際長度超過了你在程序代碼中設置的緩衝區長度等等。

  1. MQCONN, MQOPENMQCLOSE, MQDISC的使用

由於MQCONN, MQOPEN,MQCLOSE, MQDISC相對於MQGET和MQPUT來說是比較消耗資源的幾個函數,在一個應用中,即使你需要對某個隊列進行多次讀寫操作,也不要對每一次讀寫都調用一次MQCONN, MQOPEN,MQCLOSE, MQDISC函數,正確的做法應該是,調用一次MQCONN, MQOPEN就可以對隊列進行多次讀寫操作,另外,一定別忘記對稱地使用MQCLOSE, MQDISC函數將相關資源釋放掉。

  1. 讀取消息時,等待時間間隔的設置

對隊列的讀取操作,可以有兩種方式,即輪巡方式和觸發方式(利用MQ的觸發功能動態調起應用程序)。在採用輪巡方式讀取隊列時,在MQGET時,我們需要設置消息讀取選項MQGMO_WAIT,同時指定等待時間間隔。不少用戶將時間間隔設定為MQWI_UNLIMITED以實現輪巡的目的,這樣做的弊端在於在沒有消息到達時應用程序陷入無限的等待,無法接收來自外部系統的相關信號,MQCLOSE,MQDISC調用也無法被執行,因此,我們建議避免採用這種方式,推薦的做法是設置特定的等待時間間隔,然後再循環發出MQGET調用。

  1. 如果在同步點控制之下使用MQGET,在所有MQGET調用之後,必須檢查消息的回滾次數(Backout Count)

如果某個消息是在同步點控制之下讀取的,並且由於某種原因消息被回滾,消息描述符中(Message Descriptor)的BackoutCount欄位的值將被加1,你需要判斷該數值,如果它大於某個閾值,你需要使用其它手段來處理該消息。否則,在某些情況下會導致讀取消息-消息被回滾-再讀取消息-消息再被回滾的死循環。

例如:你使用了觸發機制設定當隊列中消息到達時,觸發某個應用程序,該應用程序在MQ XA Resource Manager的控制之下,讀取消息,並且利用其數據對資料庫進行更新,假設資料庫出現問題,無法成功進行資料庫操作,消息將被回滾;這時,又滿足了觸發條件,又會觸發起該應用程序,周而復始,陷入死循環。在這種情況下,你必須在程序中加入對BackoutCount的判斷。

  1. 當處理backout消息時,可以使用隊列的BOTHRESH BOQNAME屬性。

如4中所言,如果某個消息是在同步點控制之下讀取的,並且由於某種原因消息被回滾,消息描述符中的BackoutCount欄位的值將被加1,你需要判斷該數值,如果它大於某個閾值,你需要使用其它手段來處理該消息。在處理該消息的應用中,你可以將其與設定的閾值做比較,這時,閾值會被寫死在程序中,為了提高其靈活性,你可以使用隊列的BOTHRESH 和 BOQNAME屬性。這樣,你可以在例外處理中,利用MQINQ查詢得到閾值的大小,如果超出,可以將消息轉發到BOQNAME指定的隊列中,繼而對該隊列進行相應的處理。這種方法大大增強了應用程序的靈活性。

  1. 在使用MQOPEN, MQPUT MQGET調用時,要使用FAIL_IF_ QUIESCING的選項。

MQ系統本身和使用它提供的服務的用戶應用程序之間是互相獨立的,必要時,我們可能要停止MQ系統,這時,我們不但希望新的應用不能連接,並且希望所有已連接的應用能夠立即停止。為了使所有的應用程序能夠快速得知MQ系統正在停止的信號,在上述MQ API中,必須設置FAIL_IF_ QUIESCING的選項。

如果不設置FAIL_IF_ QUIESCING的選項,當MQ系統停止時,所有應用將繼續運行,這樣會影響MQ系統的停止,從而導致MQ停止需要很長時間,同時可能導致我們必須手工殺掉那些沒有設置該選項的應用程序。

  1. 消息描述符的不同欄位的使用方法

在MQ的消息描述符MQMD中包含了很多欄位,這些欄位大部分是一些保留欄位,例如:MsgID表示消息的唯一標識,如果你不指定,MQ系統會為你自動產生一個,並保證其唯一性;PutAppType, PutAppName, PutDate, PutTime是系統自動產生的,表示哪個應用何時將消息發送到隊列中;再如:GroupId, MsgSeqNumber, Offset, MsgFlags是與消息分段和消息分組相關的控制信息;除了這些系統自動產生無法更改的欄位和有特殊用途的欄位之外,如果您想選擇某些欄位為己所用,將其設定為自己應用程序中某個有意義的標識,你可以使用CorrelId和Feedback欄位,但是,按照慣例,CorrelId常常被用於在請求/應答通訊模式中來表示請求消息和應答消息之間的關聯;因此,我們可以靈活使用Feedback欄位,利用該欄位來進行一些應用程序控制。當我們接收到某個消息之後,我們可以檢查Feedback欄位,根據Feedback欄位值進行相應的處理。

  1. 消息永久性屬性的確定

永久性消息保證了消息在系統和網路等故障下的安全可靠,但是同時從性能角度來講會比非永久性消息要差,因此,要從不同的角度進行權衡和分析,然後決定消息的永久性屬性。當對性能要求非常高,可靠性要求相對不高時,可以首先考慮採用非永久性消息,在消息決不允許有任何丟失,並且在丟失之後又無法重新發送時,要使用永久性消息。

  1. 當指定消息的永久性和非永久性屬性時,最好利用應用程序顯式地指定,不要使用“defined as queue”的方法來指定。

消息的永久性和非永久性是消息本身的屬性,多數情況下,只有消息的原始發出者才了解丟失消息將產生的重大影響,因此,消息的原始發出者應在應用程序中顯式地指定消息的永久性,如果將其定義為依賴於隊列的該屬性,就會比較被動,當隊列的永久性屬性(DEPSIST)被意外地設為NO時,就會有丟失的風險。

  1. 應用程序可以將請求消息的永久性屬性為No,即對於請求消息使用非永久性消息。

一般情況下,請求消息丟失對應用系統不會產生嚴重的影響,如果出現請求消息丟失的情況,我們可以重新發送,因此,不必將請求消息設置為永久性消息。把請求消息設置為非永久性消息的另外一個好處是,系統不需要對非永久性消息記錄日誌,從而減少I/O操作,提高系統的性能。

  1. 在異種操作系統平台上使用MQ傳輸消息時,將消息格式設置為MQFMT_STRING

MQ的一大優勢之一,是對built-in(內置的)消息格式,可以實現不同操作系統平台間、不同系統字符集之間的數據轉換,如開發平台ASCII碼和主機EBCDIC碼之間的轉換。為了實現該數據轉換,MQ必須獲知本身和對方MQ系統的隊列管理器的CCSID和Encoding以及消息的格式。一般而言,CCSID和Encoding會被自動設置和處理,不需要應用程序關心,但是,消息的格式必須由應用程序指定,對於MQ內置支持的消息格式,MQ可以自動轉換,這些消息格式由MQFMT_*來指定。鑒於應用程序數據都可以用MQFMT_STRING來表示,並且MQFMT_STRING是MQ內置支持可以轉換的格式之一,你可以使用它來表示你的消息格式,同時,對於數字型消息,你需要使用atoi, itoa等函數實現數字型和字元型之間的轉換。

  1. 對大消息的處理

MQ支持單條消息的最大長度為100M,隊列管理器、隊列、通道支持的最大消息的預設值為4M,即使如此,我們卻應該根據不同的網路類型和帶寬,具體地確定不同情況下單條消息的合適大小。例如:如果在撥號網路或網路帶寬較窄的情況下,我們將單條消息的大小設置得太大,就會影響傳輸效率,在這種情況下,一定要使用MQ的分段功能將消息進行分段處理,確保每一個物理消息的大小適當,MQ會自動維護整個邏輯消息的完整性,並且可以在接收端一次性將其取出。

  1. 如何使用MQ的請求/應答通訊模式來處理同步的消息處理模式。

大家知道,MQ的非同步處理模式是非常強健的,同時它也支持同步的消息處理,例如MQ的client-server通訊就是一種典型的同步工作模式,對於server-server通訊,我們也可以實現同步工作模式,這裡就涉及到如何巧妙地使用MQ提供的消息生命周期的功能。這時,對於請求消息和應答消息我們最好為其設置生命周期。

典型的應用案例如下:假設系統A向系統B發出請求,調用B上的某個交易,這裡,我們首先要設置請求消息的生命周期,並且在消息到期時將消息丟棄,如果在消息發出之前消息過期,它就會在進入通道之前,被MQ系統丟棄;如果當消息到達目的地之後,在被對方應用程序取走之前消息過期,它也將被MQ系統丟棄。系統B上的交易便不會被調起執行。對於應答消息,我們也設置它的生命周期,與請求消息不同的是,我們設置在消息到期時將其轉發到另外一個特定的隊列中,這時,如果系統B上的交易執行完之後,會產生應答消息,如果由於通訊等原因,該應答消息在到達系統A時應用程序設置的Timeout時間已經超出,應用程序必然認為系統B上的交易沒有被執行,也不會處理該應答消息,這樣,應答消息便會過期,當它過期時,根據我們的設置,它會自動被MQ系統轉發到特定隊列中,我們另設專門的應用程序對此進行沖正處理。

  1. 對消息類型的設置。

通常情況下,我們不要求用戶一定去設置消息的類型,設置消息類型的方法是在消息描述符MQMD的MsgType欄位,消息類型有datagram, request, reply, report等若干種。但是為了更好地對消息進行管理,我們必要時要設定消息類型,從而對不同的消息進行處理。例如,當reply消息和report消息都被發送到同一個應答隊列時,我們可以根據消息類型的不同對其進行不同的處理。

  1. 對消息轉換的設置。

通常,當需要進行消息轉換時,我們有兩種方法來進行設置:一種方法是在調用MQGET時設置MQGMO_CONVERT的讀取消息選項;第二種方法是將發送端通道的convert屬性設置為yes。對比二者,我們推薦使用第一種方法,它的優勢在於:它避免了消息通道代理程序對消息的轉換,從而提高了通道的性能;其次,當一對多傳輸時,避免了對不同的目標系統使用不同的轉換演算法,而統一放在接收端進行。

當我們不需要進行格式轉換時,使用在MQMD中將消息格式Format欄位設置為MQFMT_NONE的方法來表示,MQFMT_NONE表示忽視任何格式轉換。

  1. 應用程序最好不要在MQGETMQPUT調用時使用過大的消息緩衝區,從而減少隊列管理器對內存的需求。

當應用程序發出MQGET和MQPUT調用時要設定用於裝載消息的數據緩衝區大小,MQ系統將據此來分配內存,如果使用過大的消息緩衝區,隊列管理器就會分配較大的內存來處理這些調用,從而造成內存浪費,影響性能。如果我們在接收端不知道消息的大小,為了不至於設置一個很大的緩衝區去接收數據,我們可以在真正的MQGET之前先使用Browse方式來瀏覽一下隊列中的消息,根據瀏覽到的消息的MQMD的OriginalLength欄位的數值來確定消息緩衝區的大小,然後再使用MQGET真正的將消息讀取出來。

  1. 在設定消息的優先順序時,不要直接在消息描述符中設置,最好設置隊列的優先順序,然後使用“defined as queue”來設置消息的優先順序。

優先順序是消息的屬性之一,當應用發生變化時,我們可能需要改變消息的優先順序,如果我們將其寫死在程序中,就會影響程序的靈活性。如果藉助於隊列的優先順序來設置消息的優先順序,系統管理員可以根據需要,通過改變隊列的DEFPRTY的屬性來更改其優先順序,而無需改變應用程序代碼,這樣可以大大提高系統管理和網路管理的靈活性。

  1. 請求型應用在打開請求隊列時,最好不要使用MQOO_OUTPUT選項。

請求隊列可以是本地隊列,也可能是遠程隊列,本地隊列是既可讀又可寫的,而遠程隊列是只可寫(MQOO_OUTPUT)的。通常,請求型應用一般都是需要將請求發送到請求隊列中,對隊列的操作都是MQPUT,這時,隊列的類型不會有影響,因為本地隊列和遠程隊列都是可寫的。但是,如果某個請求型應用需要讀取請求隊列,則要求請求隊列一定是本地隊列,這時就不能使用MQOO_OUTPUT選項。因此建議在打開請求隊列時,最好不使用MQOO_OUTPUT選項,可以使得不同的應用之間很容易移植,而且當隊列屬性改變時,不會對應用程序造成影響。

  1. 與資料庫交互時,MQPUT MQGET必須使用同步點控制,即使用MQ的兩階段提交功能,來保證數據的一致性和完整性。

為了保證資料庫操作和MQ操作同時成功或同時回滾,需要在做MQPUT 和 MQGET調用時,使用MQPMO_SYNCPOINT和MQGMO_SYNCPOINT選項將隊列操作和資料庫操作作為一個事物來完成。這樣,如果資料庫出現問題導致操作失敗時,消息可以被正確回滾;否則,會導致數據不一致的現象。

  1. 與資料庫操作相關的隊列消息的屬性最好設為永久性消息,即消息的persistence屬性應設為yes,並且永不過期。

由於與資料庫操作相關的消息的重要性很強,它必須被設置為永久性消息,被MQ系統記錄日誌,從而在隊列管理器重新啟動或機器重啟時不會丟失。另外,在”Send and Forget”通訊模式之下,由於這是典型的非同步通訊模式,消息何時被處理是不確定的,為了防止消息超時,我們應將其生命周期設置為永不過期。

  1. 對消息做了修改或者轉發的應用,最好傳遞原始消息的identity context(身份鑒別上下文)信息。

每個MQ的消息都有其特定的鑒別上下文,通過消息描述符(MQMD)的相關欄位來表示,它代表了消息是由誰產生的,當消息在MQ網路中傳輸時,該上下文應該必須被保留。當消息被另外的用戶修改或者轉發時,需要修改其上下文,將其原始的上下文重新賦進去。這主要是處於安全的考慮,通過上下文,可以獲得消息產生者的信息。

  1. 保持永久性動態隊列名稱的唯一性,確保同一個應用多次調用/運行產生的動態隊列的名字唯一性。

當應用程序需要動態產生應答隊列時,它可以產生臨時性動態隊列和永久性動態隊列,某些情況下,將利用永久性動態隊列保留一些可恢復的消息,這意味著該永久性動態隊列可以重新被打開,因此要保證它們名稱的唯一性。

  1. 在使用臨時性動態隊列來處理應答時,處理請求消息的應用要保證不要將應答消息設置為永久性消息。

臨時性動態隊列不能存儲永久性消息,鑒於此,對請求做出響應的應用程序必須知道接收應答的隊列是臨時性動態隊列還是永久動態隊列,請求端應用和響應端應用必須對響應消息的永久性進行協調,以保證只有非永久性消息會被放置到臨時動態隊列中。

結論:為了更好地使用MQ,我們必須遵循一定的標準和指導原則,使得我們開發出更加可靠、高效的應用程序,當然,這是從普遍意義上而言,在實際應用中需要您靈活掌握,因地制宜地選用更加適合您的配置和編程模式。

 

以下文章點擊率最高

Loading…

     

如果這文章對你有幫助,請掃左上角微信支付-支付寶,給於打賞,以助博客運營

發表評論

您的電子郵箱地址不會被公開。 必填項已用*標註