1. 程式人生 > >關於網絡模型中的同步異步的思考

關於網絡模型中的同步異步的思考

例子 明顯 時間 直接 upd 修改 官方 實例 關於

  最近寫畢設的時候,寫到了數據庫部分,想要異步操作mysql,發現mysql並未提供對應異步接口,於是我開始思考是否有辦法自己實現一個異步接口。

  想實現一個異步接口需要什麽條件?

  (1)不應該在IO操作上阻塞

  (2)每條消息(查詢)必須要有相應標識

  對於(1)不必所說,(2)的意思是每條消息發出去後,異步callback的時候你要知道這是哪條信息的回復。舉個例子,你分別向服務器發送了兩個查詢,“小明的id是什麽?”和“小紅的id是什麽?”,過了一會服務器給你回復了“1”,“2”。這時候小明的id一定是1嗎?不一定,有可能服務器先接收到了第二個查詢,然後回復,也可能是服務器先接收到了第一個查詢,但是你收到回復卻是先收到第二條。這時候異步是無法保證順序的,因此需要對信息進行標識。

  如何對信息進行標識呢?方法很多,同步是最容易的一種。“小明的id是什麽?”“1”“小紅的id是什麽?”“2”。這樣一來一回絕對不會出錯。但是我們現在是異步,那又要怎麽做呢?最暴力的方法是使用短連接,對於每個連接,我就只發一條信息,利用連接作為消息的標識。意思就是,連接A發送了“小明的id是什麽?”連接B發送了“小紅的id是什麽?”這樣收到消息也不會亂。這樣做缺點也很明顯,短連接的缺點就是創建和銷毀連接開銷大。還有一種方法是用長連接的,長連接需要在消息上帶上一個標識位,比如“消息id為1,查詢是小明的id是什麽?”“消息id為2,查詢是小紅的id是什麽?”,收到的回復是“消息id為1,答復是1”“消息id為2,答復是2”。這樣也不會亂了。但是這需要服務器提供對應的接口,需要能回復帶上消息id。

  說了這麽多,進入實例,如何在mysql上用上這個邏輯?我們操作mysql的時候,都是用官方給的接口,比如c++就有對應的mysql connector。如果這些接口不提供異步接口,比如說查詢就一個query接口,你一定要阻塞在那裏直到回復,那麽很遺憾,上述兩種方法你都用不上。十分悲慘的是,我在mysql connector上沒有找到異步接口,所以我開始找其他方法。

  我想到可以利用協程去解決,但是協程只能解決短連接問題,而且從總體上看,這應該是一個同步非阻塞模型而非異步模型。

  實在不行,還可以自己封裝一個mysql connector,只要知道與msql通信的具體協議就可以做了。但是這樣同樣無法支持長連接的帶消息id的做法,因為服務器本身並不支持。對於mysql有個比較投機取巧的方法,就是每次查詢帶上一個“select msgId”,例如“select 1 and id where name = 小明”。但是我覺得這種方法可能不能做通用,比如不支持update等操作。

  最後無計可施,我開始思考是否真的需要異步。其實要訪問mysql服務的進程和mysql是在同一臺機器上的,這時候同步異步性能差異並不大,異步的優勢在於能將等待時間利用流水線優化掉,同機器操作可以忽略等待時間。何況數據庫查詢一般都是最後的步驟了,阻塞非阻塞沒什麽意義,所以我可以直接使用阻塞模型。

  如果訪問mysql服務的進程和mysql不在同個機器,異步是很有必要的。這時候我想到個更合理的做法,就是在mysql服務的機器上增加一個mysql代理,代理可以提供非阻塞接口,但是連接mysql服務是阻塞的。可以采用epoll接收請求,然後將請求交給消息隊列,然後多工作線程去隊列獲取請求並阻塞請求mysql。這樣做需要自己定協議,工作量也不小,但是相對於修改mysql的協議來說合理很多。

關於網絡模型中的同步異步的思考