进行负载测试后,每个新的mio连接都会立即挂断

Ele*_*Ent 5 rust mio

我使用mio在Rust中编写了一个多线程异步HTTP服务器.当我运行负载测试(使用围攻)时,服务器在第一次负载测试时工作正常,但是当负载测试完成后,对服务器的所有请求都开始失败.

使用一些日志记录,我注意到我遇到的每个新连接都会accept()立即收到挂起事件.服务器连接本身不会收到任何错误或挂起事件.

我在OS X 10.11 El Capitan上运行带有mio 0.6的Rust 1.12.0

这是我的服务器的主要事件循环:

pub fn run(self) {
    let poll = Poll::new().unwrap();
    let server = TcpListener::bind(&SocketAddr::from_str(&self.host).unwrap()).unwrap();
    poll.register(&server, SERVER, Ready::readable(), PollOpt::edge()).unwrap();
    let mut events = Events::with_capacity(1024);
    let mut next_conn: usize = 1;
    let mut workers = Vec::new();
    // Create worker threads.
    for _ in 0..self.num_workers {
        let (tx, rx) = channel();
        let worker_handler = self.event_handler.duplicate();
        thread::spawn(move || {
            Self::process_events(rx, worker_handler);
        });
        workers.push(tx);
    }
    loop {
        println!("Polling...");
        match poll.poll(&mut events, None) {
            Err(e) => panic!("Error during poll(): {}", e),
            Ok(_) => {}
        }
        for event in events.iter() {
            match event.token() {
                SERVER => {
                    println!("Accepting..");
                    match server.accept() {
                        Ok((stream, _)) => {
                            println!("Registering new connection...");
                            match poll.register(&stream,
                                                Token(next_conn),
                                                Ready::readable(),
                                                PollOpt::edge()) {
                                Err(e) => panic!("Error during register(): {}", e),
                                Ok(_) => {
                                    println!("New connection on worker {} ",
                                             next_conn % self.num_workers);
                                    workers[next_conn % self.num_workers]
                                        .send(Msg::NewConn(next_conn, stream))
                                        .unwrap();
                                    next_conn += 1;
                                }
                            }
                        }
                        Err(e) => panic!("Error during accept() : {}", e),
                    }
                }
                Token(id) => {
                    println!("Sending event on conn {} to worker {}",
                             id,
                             id % self.num_workers);
                    workers[id % self.num_workers]
                        .send(Msg::ConnEvent(id, event.kind()))
                        .unwrap();
                }
            }
        }
    }
}

fn process_events(channel: Receiver<Msg>, mut event_handler: Box<EventHandler>) {
    loop {
        let msg = channel.recv().unwrap();
        match msg {
            Msg::NewConn(id, conn) => {
                event_handler.new_conn(id, conn);
            }
            Msg::ConnEvent(id, event) => {
                event_handler.conn_event(id, event);
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

我正在使用的示例webapp的完整代码可以在GitHub上找到.

加载测试命令:

siege -b -c10 -d10 -t20S http://localhost:8080
Run Code Online (Sandbox Code Playgroud)

Cul*_*lly 5

我不知道为什么负载测试应用程序不能更好地记录这一点.几个月前我遇到了同样的问题.听起来你已经达到了"短暂的港口限制".以下是文章中的一些引用,总结了这个想法:

只要在客户端和服务器之间建立连接,系统就会将该连接绑定到临时端口 - 一组在有效端口范围的高端指定的端口.

OS X上可用的临时端口总数为16,383.

请注意,此限制不会影响对实时服务器的实际请求,因为每个TCP连接都由源IP,源端口,目标IP和目标端口的元组定义 - 因此临时端口限制仅适用于单个客户端/服务器对.

换句话说,它正在发生,因为您正在运行从localhost到localhost的负载测试,并且可能在大约16,383个连接之后耗尽短暂的端口.

您可以做几件事来测试这是否是问题所在:

  1. 让负载测试仪报告所建立的连接数.如果它大约是16,000,那么这可能是罪魁祸首.

  2. 增加临时端口限制并再次运行负载测试.如果您获得更多的连接,那么这可能就是问题所在.但请记住,如果这是问题,它将不会成为一个问题.

您可以使用以下命令查看临时端口范围:

$ sysctl net.inet.ip.portrange.first net.inet.ip.portrange.last

你可以使用这个命令增加它:

$ sysctl -w net.inet.ip.portrange.first=32768

运行测试后,您应该将端口范围设置回原来的范围,因为此增加代表非标准范围.