Rust从入门到实战系列二百四十七:使用线程池改善吞吐量

线程池(thread pool)是一组预先分配的等待或准备处理任务的线程。当程序收到一个新任务,线程池
中的一个线程会被分配任务,这个线程会离开并处理任务。其余的线程则可用于处理在第一个线程处理
任务的同时处理其他接收到的任务。当第一个线程处理完任务时,它会返回空闲线程池中等待处理新任
务。线程池允许我们并发处理连接,增加 server 的吞吐量。
我们会将池中线程限制为较少的数量,以防拒绝服务(Denial of Service,DoS)攻击;如果程序为每一
个接收的请求都新建一个线程,某人向 server 发起千万级的请求时会耗尽服务器的资源并导致所有请求
的处理都被终止。
不同于分配无限的线程,线程池中将有固定数量的等待线程。当新进请求时,将请求发送到线程池中做
处理。线程池会维护一个接收请求的队列。每一个线程会从队列中取出一个请求,处理请求,接着向对
队列索取另一个请求。通过这种设计,则可以并发处理 N 个请求,其中 N 为线程数。如果每一个线程都
在响应慢请求,之后的请求仍然会阻塞队列,不过相比之前增加了能处理的慢请求的数量。
这个设计仅仅是多种改善 web server 吞吐量的方法之一。其他可供探索的方法有 fork∕join 模型和单线
程异步 I∕O 模型。如果你对这个主题感兴趣,则可以阅读更多关于其他解决方案的内容并尝试用 Rust 实
现他们;对于一个像 Rust 这样的底层语言,所有这些方法都是可能的。
在开始之前,让我们讨论一下线程池应用看起来怎样。当尝试设计代码时,首先编写客户端接口确实有
助于指导代码设计。以期望的调用方式来构建 API 代码的结构,接着在这个结构之内实现功能,而不是
先实现功能再设计公有 API。
类似于第十二章项目中使用的测试驱动开发。这里将要使用编译器驱动开发(compiler-driven development)。我们将编写调用所期望的函数的代码,接着观察编译器错误告诉我们接下来需要修改什么使
得代码可以工作。
20.2. 将单线程 SERVER 变为多线程 SERVER 537
为每一个请求分配线程的代码结构
首先,让我们探索一下为每一个连接都创建一个线程的代码看起来如何。这并不是最终方案,因为正如
之前讲到的它会潜在的分配无限的线程,不过这是一个开始。示例 20-11 展示了 main 的改变,它在 for
循环中为每一个流分配了一个新线程进行处理:
文件名: src∕main.rs

use std::fs;

use std::io::prelude:😗;

use std::net::TcpListener;

use std::net::TcpStream;

use std::thread;

use std::time::Duration;

fn main() {
let listener = TcpListener::bind(“127.0.0.1:7878”).unwrap();
for stream in listener.incoming() {
let stream = stream.unwrap();
thread::spawn(|| {
handle_connection(stream);
});
}
}

fn handle_connection(mut stream: TcpStream) {

let mut buffer = [0; 1024];

stream.read(&mut buffer).unwrap();

let get = b"GET / HTTP/1.1\r\n";

let sleep = b"GET /sleep HTTP/1.1\r\n";

let (status_line, filename) = if buffer.starts_with(get) {

(“HTTP/1.1 200 OK”, “hello.html”)

} else if buffer.starts_with(sleep) {

thread::sleep(Duration::from_secs(5));

(“HTTP/1.1 200 OK”, “hello.html”)

} else {

(“HTTP/1.1 404 NOT FOUND”, “404.html”)

};

let contents = fs::read_to_string(filename).unwrap();

let response = format!(

“{}\r\nContent-Length: {}\r\n\r\n{}”,

status_line,

contents.len(),

contents

);

stream.write(response.as_bytes()).unwrap();

stream.flush().unwrap();

}

示例 20-11: 为每一个流新建一个线程
538 CHAPTER 20. 最后的项目: 构建多线程 WEB SERVER
正如第十六章讲到的,thread::spawn 会创建一个新线程并在其中运行闭包中的代码。如果运行这段代
码并在在浏览器中加载 ∕sleep,接着在另两个浏览器标签页中加载 ∕,确实会发现 ∕ 请求不必等待 ∕sleep
结束。不过正如之前提到的,这最终会使系统崩溃因为我们无限制的创建新线程。

  • 4
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值