【功能的具體化】
首先,因為經我改造後的 rabbitmq-c 用戶端程式是基于 libevent 的,是以天然可以做到單線程中同時處理多 tcp 連接配接。理論上,可以對外宣稱”該用戶端程式支援任意數量生産者和消費者的組合“,而不用擔心多線程切換的開銷。唯一可能拖慢整個系統的地方,是在 consumer 針對 rabbitmq 伺服器應答進行回調處理的過程中,即回調處理函數中不能,或者說不應該,進行耗時的操作。
a. 處理應答的回調函數中可能需要實作的處理邏輯有哪些?
擷取本地應用發來的 rabbitmq msg,再按目的地(routing_key)進行純轉發動作。對應模型為【1p + 1c + 内部轉發 queue】,運作在單獨一個線程中。其中 p 代表 producer,c 代表 consumer,均是 rabbitmq 裡的概念。
擷取外部應用發來的 rabbitmq msg,提取消息中含有的 sql 語句并執行,執行成功後再發送 rabbitmq msg 通知本地應用資料庫更新成功。對應模型為【1p + nc + 内部 sql 處理 queue】,運作于兩個線程中,一個用于 rabbitmq 相關消息處理和轉發,另一用于 sql 處理。
需要支援 json 或/和 xml 解析。
b. 采用單線模型還是多線程模型(每個線程中都有獨自的 event_base)?
對于功能 1,單線程足矣;對于功能 2,目前看來至少需要2個線程,即 1p + nc 使用一個線程,sql 處理使用單獨一個線程。
c. 1p + 1c 和 1p + nc 是否可以或者應該在一個線程中處理?
按照前文的說明,一個線程中可以處理任意多 tcp 連接配接(目前改造後的 rabbitmq-c 的基礎前提是:一個 tcp 連接配接上僅使用一個 channel,是以每個 producer 和 consumer 都需要獨占一個 tcp 連接配接),是以關鍵問題是是否應該這麼做。對于 consumer 來說,因為可讀事件是會被即時檢測的,是以對于兩種業務模型的差别就在于回調函數的實作:前者僅需關注轉發目的地,後者需要額外的線程來執行 sql 語句。
一種可能的執行流程如下:consumer 在回調函數進行中将待執行 sql 語句提取出來後放入内部 sql 處理 queue 中,待 sql 處理線程從中提取并完成 sql 執行後,将結果 push 進另外一個内部 sql_result queue 中,而 producer 會通過定時檢測的方式擷取 sql_result queue 中的結果,并發送到用于通知本地業務的 exchange 上。
上述流程沒有詳細說明的細節:consumer 從 rabbitmq 伺服器消費消息時采用的是”自動應答“模式還是”手動應答“模式,這将會對消息的可靠性産生影響,間接影響到資料的一緻性。因為在”自動應答“模式下,消息一旦被消費就會被 rabbitmq 伺服器删除掉,而一旦在 sql 執行過程中出現異常,則會導緻資料不一緻,需要自行實作某種機制保證資料的一緻性。若在”手動應答“模式下,則可能的一種實作方式為,針對從 rabbitmq 消費消息的應答,需要等待 sql 執行完成後才能進行。一則會拖慢整個事件驅動的輪轉,二則當 sql 執行失敗後,即使使用 reject 信令告之 rabbitmq 伺服器無法正确處理,該消息仍然會被删除掉,依然會有資料不一緻的問題存在。如果使用了 帶有 requeue 屬性的 reject 信令呢?結果還是一樣的,因為隻有唯一一個 consumer 去消費該消息,當再次重新消費該消息時,之前出現的 sql 異常可能還會出現。看來隻有華山一條路了,自己實作某種糾錯機制,保證資料的一緻性。(關于糾錯的問題,後續部落格再說)
【實作中遇到的挫折】
如何在 sql 線程執行完成後,告之 producer 去發送通知消息給其他業務。手段可能有很多種,但是考慮到 rabbitmq 線程是基于 libevent 運轉的,這就需要一點小技巧了,後續有單獨部落格進行說明。
b. 消息傳遞 queue 的使用
在最初的設計中,【1p + 1c + 内部轉發 queue】模型裡使用的内部轉發 queue 是基于 producer 和 consumer 所使用的 conn 連接配接的,即每一個 conn 上都有這樣一個 queue 的存在。當開始調試【1p + 1c】功能時,發現當 consumer 拿到 msg 後隻能将其存放在自身 conn 上的 queue 中,而 producer 無法直接獲得這些 msg (當然可以借助外部處理來 pop 和 push ,但似乎不是個好方案),是以決定還是采用全局 msgq(轉發 msg 用)的方式來處理。
在改為全局 queue 後又發現一個新問題,即 consumer 對 msg 的接收具有即時性,但 producer 對 msg 的發送卻無法做到。原因在于,可以針對 consumer 對應 conn 的 sockfd 監聽可讀和逾時事件,但針對 producer 對應 conn 的 sockfd 卻不能去監聽可寫事件。這就導緻了一個問題,即 producer 隻能按照定時器指定的時間發送 msg 。換句話說,即使設定的定時時間再短,也達不到即時發送的效果。那麼如何解決這個問題呢?留個思考給大家把~~
(在隻使用可讀、可寫、逾時事件的情況下應該是無解的,估計可以利用信号事件解決)
最後附上一張【1p + 1c】的結構圖: