--- tags: Research --- # Reactor vs Proactor ## Difference * Reactor 是 non-blocking synchronous I/O 模式,透過就緒 (Ready) 可讀寫事件觸發。每次到有事件發生(比如可讀就緒事件)後,就需要 application 主動調用 read 方法來完成資料的讀取,也就是要 application 主動將 socket receiving buffer 中的資料讀到 application memory 中,這個過程是同步的,讀取完資料後才能進一步處理資料。 * Proactor 是 asynchronous I/O 模式,透過已完成的讀寫事件(kernel thread 已經讀到 userspace 的 buffer 中)觸發。在發起 asynchronous 讀寫請求時,需要傳入 userspace data buffer 的地址(用來存放結果資料),這樣 kernel thread 才可以自動幫我們把資料的讀寫工作完成,這裡的讀寫由作業系統來做,並不需要像 Reactor 那樣還需要主動發起 read/write 來讀寫資料,作業系統完成讀寫工作後,就會通知 application 直接處理資料。 ## 為什麼目前大部分 Server 應用的是 Reactor Design Pattern,而不是 Proactor ? * 問題是 AIO 並不能完整應用在所有的作業系統,Windows 有提供比較完整的 AIO interface IOCP,而 Linux 有提供一套 native async IO interface,不過這套介面存在一些缺點。首先,在開啟檔案時必須設定為 `O_DIRECT`,之後資料的 write/read 操作都是繞過 OS 維護的 page cache,直接對 disk 做 I/O 操作,但這套 AIO 不支援對於 network sockets 的 I/O 操作,而且普遍情況下,這樣的設定會影響效能。第二個缺點是 Linux AIO 雖然是 Asynchronous 的,但卻有很多原因會使 io_submit 出現 block 現象。不過後來在 Liunx 系統上實作了 io_uring 介面,但 io_uring 是 2019 年 Linux 5.1 才引入的。 另外, MacOS 並沒有提供 async IO interface。 因此目前大部分 Server 應用的是 Reactor Design Pattern ## Proactor 缺點 1. 系統分常複雜 * 需要 handler 去區分 request 是 I/O 操作還是連線操作 * 連線操作會有 acceptor * I/O 操作會直接進行 Asynchronous I/O 交由 OS 去做 * OS 做完會把完成的任務放到 queue 裡面,Proactor 會持續去看 event queue 是否有完成的事件,有的話則交由 completion handler * completion handler 去做 I/O 完成後要處理的事 2. Asynchronous 操作難以 debug,很難得知程式執行的順序 3. request 有相依或是有順序性時,必須實作讓 asynchonous 有 priority 或是可以取消,那這部分在複雜系統很難實作 ## Reference * https://www.zhihu.com/question/26943938 * https://arthurchiao.art/blog/intro-to-io-uring-zh/