【问题标题】:"Connection reset by peer" when benchmarking a simple Rust HTTP server with ab使用 ab 对简单的 Rust HTTP 服务器进行基准测试时“由对等方重置连接”
【发布时间】:2016-01-26 10:28:05
【问题描述】:

我正在尝试用 Rust 编写一个极其简单的并发服务器,以使用该语言的并发原语及其线程模型。这是我的代码:

use std::io::prelude::*;
use std::io::Result;
use std::net::{TcpListener, TcpStream, Shutdown};
use std::sync::{Arc, Mutex};
use std::thread;

fn handle_client(mut stream: TcpStream) -> Result<()> {
    try!(stream.write(b"HTTP/1.0 200 OK\r\nContent-Type: text/plain\r\nContent-Length: 5\r\n\r\nPong!\r\n"));
    // try!(stream.shutdown(Shutdown::Both));

    Ok(())
}

fn main() {
    let listener = TcpListener::bind("127.0.0.1:1337").unwrap();
    // let count = Arc::new(Mutex::new(0));

    for stream in listener.incoming() {
        match stream {
            Ok(stream) => {
                // let count = count.clone();
                thread::spawn(move || {
                    let _ = handle_client(stream);

                    // let mut count = count.lock().unwrap();
                    // *count += 1;
                    // println!("{:?}", *count);
                });
            }
            Err(e) => {
                println!("Error: {}", e);
            }
        }
    }

    drop(listener);
}

当我在上面列出的程序运行的情况下运行ab -c 100 -n 100 http://127.0.0.1:1337/ 时,我几乎立即得到apr_socket_recv: Connection reset by peer (104)。为什么?

当我添加 try!(stream.shutdown(Shutdown::Both));(在上面的顶部附近注释掉)时,我不再像以前那样收到 apr_socket_recv 错误,但是 apachebench 给我的结果是 199 个由于异常而失败的请求。为什么?我做错了什么?

Concurrency Level:      100
Time taken for tests:   0.008 seconds
Complete requests:      100
Failed requests:        199
   (Connect: 0, Receive: 0, Length: 0, Exceptions: 199)
Total transferred:      500 bytes

【问题讨论】:

  • 顺便说一句,您的内容长度不是 5,而是 7(您错过了最后一个 \r\n)。但我认为这不太可能是错误的原因......
  • 您可以尝试在详细模式下运行它吗(在命令行中添加-v 标志)?奇怪的是 2/3 的请求都失败了……如果它们都失败了,我会理解的……
  • 我建议不要使用use std::io::Result,因为这会影响“正常”Result 类型。我建议改为use std::ioio::Result。此外,很少需要明确使用drop。值将在定义它们的范围结束时自动删除。
  • 您还应该在提问时创建一个MCVE。例如,this version 使用 no threading 但在使用 ab -c 1 -n 5000 运行时会出现同样的问题。
  • 此外,cURL 偶尔会报告相同的“连接重置”错误,因此它与 apachebench 无关。

标签: multithreading http server rust apachebench


【解决方案1】:

我认为问题在于您没有完全读取从客户端发送的数据,因此客户端永远没有机会转换为读取响应。当它尝试写入更多数据时,它注意到套接字已关闭并失败。

我已将您的示例扩充为在回复之前读取所有 HTTP 标头,而忽略任何请求正文。我正在处理漂亮的错误处理,如果有错误就会惊慌失措:

use std::io::prelude::*;
use std::io::BufReader;
use std::net::{TcpListener, TcpStream};
use std::thread;

fn handle_client(mut stream: TcpStream) {
    // Read all the headers
    for header in BufReader::new(&mut stream).lines() {
        let header = header.unwrap();
        if header == "\r" { break }
    }

    // Write our response
    stream.write_all(b"HTTP/1.0 200 OK\r\n\r\n").unwrap();
}

fn main() {
    let listener = TcpListener::bind("127.0.0.1:8080").unwrap();

    for stream in listener.incoming() {
        let stream = stream.unwrap();
        thread::spawn(|| {
            handle_client(stream);
        });
    }
}

这适用于ab -c 50 -n 5000 http://127.0.0.1:8080/

Benchmarking 127.0.0.1 (be patient)
Completed 500 requests
Completed 1000 requests
Completed 1500 requests
Completed 2000 requests
Completed 2500 requests
Completed 3000 requests
Completed 3500 requests
Completed 4000 requests
Completed 4500 requests
Completed 5000 requests
Finished 5000 requests


Server Software:
Server Hostname:        127.0.0.1
Server Port:            8080

Document Path:          /
Document Length:        0 bytes

Concurrency Level:      50
Time taken for tests:   1.293 seconds
Complete requests:      5000
Failed requests:        0
Total transferred:      95000 bytes
HTML transferred:       0 bytes
Requests per second:    3868.22 [#/sec] (mean)
Time per request:       12.926 [ms] (mean)
Time per request:       0.259 [ms] (mean, across all concurrent requests)
Transfer rate:          71.77 [Kbytes/sec] received

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        1    6   1.7      6      14
Processing:     1    6   1.7      6      14
Waiting:        1    6   1.7      6      14
Total:          5   13   2.6     12      23

Percentage of the requests served within a certain time (ms)
  50%     12
  66%     13
  75%     13
  80%     14
  90%     17
  95%     19
  98%     21
  99%     22
 100%     23 (longest request)

【讨论】:

    【解决方案2】:

    documentation of TcpStream 声明

    当值被删除时,套接字将被关闭。

    由于您的函数没有正确关闭 TCP 流而只是通过关闭套接字而结束,因此您会收到 Connection reset by peer 错误。完整的 TCP 关闭需要双向发送多条消息。这显然不能再发生,因为套接字已关闭。

    你的第二个问题我没有答案。此外,在 stackoverflow 上,您应该每个问题只问一个问题。

    【讨论】:

    • 这看起来非常可疑。如果Drop 实现不以正确的方式关闭 TCP 连接,那将是一个非常糟糕的设计。
    • 如果不这样做,Drop::drop 调用可能需要很长时间才能完成。两种行为都不是完美的,但这种方式是确定性的。
    • 那么应该如何正确关闭套接字,尊重 TCP?
    • 正如问题所示:通过调用shutdown
    • 添加 shutdownflush 并没有解决我的错误。我添加了另一个答案。
    猜你喜欢
    • 1970-01-01
    • 2019-03-25
    • 2014-09-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-07-11
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多