最近,筆者要用rust實(shí)現(xiàn)一個(gè)高性能網(wǎng)絡(luò)服務(wù)讯蒲,首先就需要選擇一個(gè)好的異步網(wǎng)絡(luò)庫(kù)痊土,在c++里面我們有太多選擇,libev墨林,libevent赁酝,libuv,甚至筆者自己也寫(xiě)過(guò)一個(gè)libtnet旭等,不過(guò)在rust里面酌呆,mio幾乎就是唯一的方案了。
mio實(shí)現(xiàn)很簡(jiǎn)單搔耕,就是在系統(tǒng)相關(guān)函數(shù)epoll隙袁,kqueue上面封裝了自己的相關(guān)邏輯,抽象出好用的接口供外面使用弃榨。如果大家熟悉python tornado菩收,就可以發(fā)現(xiàn),mio就非常類似于tornado里面的IOLoop惭墓。
mio的使用很簡(jiǎn)單坛梁,我們只需要?jiǎng)?chuàng)建一個(gè)EventLoop,然后實(shí)現(xiàn)自己的Handler腊凶,就可以運(yùn)行起來(lái)了划咐。
mio的Handler是一個(gè)Trait,定義如下:
pub trait Handler: Sized {
type Timeout;
type Message;
// 注冊(cè)的token有相關(guān)事件的時(shí)候調(diào)用钧萍,譬如我們注冊(cè)了一個(gè)socket褐缠,
// socket可讀或者可寫(xiě)的時(shí)候就會(huì)調(diào)用ready。
fn ready(&mut self, event_loop: &mut EventLoop<Self>, token: Token, events: EventSet) {
}
// 有通知的時(shí)候調(diào)用风瘦。
fn notify(&mut self, event_loop: &mut EventLoop<Self>, msg: Self::Message) {
}
// 注冊(cè)的timer到期了調(diào)用
fn timeout(&mut self, event_loop: &mut EventLoop<Self>, timeout: Self::Timeout) {
}
// 收到interupted signal的時(shí)候調(diào)用队魏。
fn interrupted(&mut self, event_loop: &mut EventLoop<Self>) {
}
// 每輪末尾的時(shí)候調(diào)用。
fn tick(&mut self, event_loop: &mut EventLoop<Self>) {
}
}
一個(gè)簡(jiǎn)單的例子:
use mio::{EventLoop, Handler};
struct BaseHandler;
impl Handler for BaseHandler {
type Timeout = ();
type Message = ();
}
let mut event_loop = EventLoop::<BaseHandler>::new().unwrap();
event_loop.shutdown();
在上面的例子中万搔,我們實(shí)現(xiàn)了一個(gè)簡(jiǎn)單的BaseHandler胡桨,因?yàn)镠andler這個(gè)Trait本身有默認(rèn)的函數(shù)實(shí)現(xiàn),所以BaseHandler沒(méi)有干任何事情瞬雹。
我們通過(guò)EventLoop::new
創(chuàng)建一個(gè)event loop昧谊,但是沒(méi)有干任何事情就shutdown了。
Timeout的例子:
struct TimeoutHandler;
impl Handler for TimeoutHandler {
type Timeout = u32;
type Message = ();
fn timeout(&mut self, event_loop: &mut EventLoop<Self>, _: Self::Timeout) {
event_loop.shutdown();
}
}
let mut event_loop = EventLoop::new().unwrap();
event_loop.timeout_ms(100, 0).unwrap();
event_loop.run(&mut TimeoutHandler).unwrap();
上面的TimeoutHandler我們實(shí)現(xiàn)了timeout函數(shù)酗捌,里面直接shutdown這個(gè)event loop了呢诬,然后在run之前涌哲,我們通過(guò)timeout_ms這個(gè)函數(shù),注冊(cè)了一個(gè)timer尚镰,100ms之后過(guò)期阀圾,調(diào)用對(duì)應(yīng)的timeout函數(shù)。
Notify例子:
struct NotifyHandler;
impl Handler for NotifyHandler {
type Timeout = ();
type Message = u32;
fn notify(&mut self, event_loop: &mut EventLoop<Self>, _: Self::Message) {
event_loop.shutdown();
}
}
let mut event_loop = EventLoop::new().unwrap();
let sender = event_loop.channel();
let child = thread::spawn(move || {
sender.send(0).unwrap();
});
event_loop.run(&mut NotifyHandler).unwrap();
child.join().unwrap();
上面的NotifyHandler實(shí)現(xiàn)了notify接口狗唉,在run之前初烘,我們通過(guò)channel函數(shù)生成了一個(gè)sender,mio的EventLoop是一個(gè)單線程的loop敞曹,如果要在其他線程跟EventLoop進(jìn)行交互账月,只能通過(guò)channel生成的sender综膀。
上面的例子澳迫,我們生成了sender并且將其move到另一個(gè)新的線程中,然后在新的線程里面send了一個(gè)message剧劝,event loop會(huì)在自己的事件循環(huán)里面檢測(cè)到這個(gè)事件橄登,觸發(fā)notify操作。
Tick的例子:
struct TickHandler;
impl Handler for TickHandler {
type Timeout = ();
type Message = ();
fn tick(&mut self, event_loop: &mut EventLoop<Self>) {
if !event_loop.is_running() {
// Handle quit here.
return;
}
}
fn notify(&mut self, event_loop: &mut EventLoop<Self>, _: Self::Message) {
event_loop.shutdown();
}
}
let mut event_loop = EventLoop::new().unwrap();
let sender = event_loop.channel();
sender.send(0).unwrap();
event_loop.run(&mut TickHandler).unwrap();
EventLoop在每輪處理的最后讥此,會(huì)調(diào)用tick這個(gè)回調(diào)函數(shù)拢锹,有時(shí)候我們會(huì)通過(guò)notify來(lái)告訴EventLoop結(jié)束,但是如果我們?cè)趎otify這個(gè)message里面處理close萄喳,沒(méi)準(zhǔn)這個(gè)message后面還有一些message需要處理卒稳,沒(méi)準(zhǔn)這時(shí)候因?yàn)橹熬鸵呀?jīng)在close里面釋放相關(guān)資源了導(dǎo)致panic,所以筆者更傾向的一種做法就是close在tick里面處理他巨,因?yàn)閠ick是每輪最后一個(gè)調(diào)用的函數(shù)充坑,我們可以保證之后不會(huì)再處理任何的事件了。
TODO:
這里并沒(méi)有說(shuō)明ready的用法染突,因?yàn)檫@個(gè)跟socket關(guān)系比較大捻爷,后續(xù)筆者開(kāi)始實(shí)現(xiàn)TcpServer的時(shí)候在詳細(xì)說(shuō)明。