使用rust写一个Web服务器——async-std版本
文章目录
- 实现异步代码
- 并发地处理连接
- 使用多线程提升性能
使用rust实现一个异步运行时是async-std的单线程Web服务器。
仓库地址: 1037827920/web-server: 使用rust编写的简单web服务器 (github.com)
在之前的单线程版本的Web服务器代码上进行修改,具体代码在给的仓库地址中。
实现异步代码
首先将handle_connection修改为async实现:
async fn handle_connection(mut stream: TcpStream) {}
该修改会将函数的返回值从()变成Future<Output = ()>,因此直接运行将不再有任何效果,只用通过.await或执行器的poll。
使用async-std作为异步运行时:
async-std运行时允许使用属性#[async_std::main]将我们的fn main函数变成async fn main,这样就可以在main函数中直接调用其他async函数,否则你得用block_on方法来让main去阻塞等待异步函数的完成,但是这种简单粗暴的阻塞等待方式并不灵活
Cargo.toml:
[dependencies]
futures = "0.3"[dependencies.async-std]
version = "1.6"
features = ["attributes"]
下面将main函数修改为异步的,并在其中调用前面修改的异步版本handle_connection:
use std::{io::{prelude::*, BufReader},net::{TcpListener, TcpStream},fs,time::Duration,
};
extern crate async_std;
use async_std::task;#[async_std::main]
async fn main() {let listener = TcpListener::bind("localhost:8080").unwrap();for stream in listener.incoming() {let stream = stream.unwrap();// 这里还是无法并发handle_connection(stream).await;}
}
实现异步版本的handle_connection:
/// # 函数作用
/// 处理连接:读取请求,回应请求
async fn handle_connection(mut stream: TcpStream) {let buf_reader = BufReader::new(&mut stream);// 使用next而不是lines,因为我们只需要读取第一行,判断具体的request方法let request_line = buf_reader.lines().next().unwrap().unwrap();// 根据请求的不同,返回不同的响应let (status_line, filename) = match &request_line[..] {"GET / HTTP/1.1" => ("HTTP/1.1 200 OK", "hello.html"), // 请求 / 资源"GET /sleep HTTP/1.1" => { // 请求 /sleep 资源// 没有使用std::thread::sleep进行睡眠,原因是该函数是阻塞的,它会让当前线程陷入睡眠中,导致其他任务无法继续运行task::sleep(Duration::from_secs(5)).await;("HTTP/1.1 200 OK", "hello.html")}_ => ("HTTP/1.1 404 NOT FOUND", "404.html"),};let contents = fs::read_to_string(filename).unwrap();let length = contents.len();let response = format!("{status_line}\r\nContent-Length: {length}\r\n\r\n{contents}");// write_all接收&[u8]类型作为参数,这里需要用as_bytes将字符串转换为字节数组stream.write_all(response.as_bytes()).unwrap();
}
可以看出,只是把函数变成async往往是不够的,还需要将它内部的代码也都变成异步兼容,阻塞线程绝对是不可行的
但是线程web服务器还是不能进行并发处理请求,原因是listener.incoming()是阻塞的迭代器。当listener在等待连接时,执行器是无法执行其他Future的,而且只有当我们处理完已有的连接后,才能接收新的连接。
并发地处理连接
上面的解决方法是将listener.incoming()从一个阻塞的迭代器变成一个非阻塞的Stream
use std::{fs,time::Duration,
};
extern crate async_std;
use async_std::{net::{TcpListener, TcpStream},io::{prelude::*, BufReader},task,
};
use futures::StreamExt;#[async_std::main]
async fn main() {let listener = TcpListener::bind("localhost:8080").await.unwrap();listener.incoming().for_each_concurrent(None, |tcpstream| async move {let tpcstream = tcpstream.unwrap();handle_connection(tpcstream).await;}).await;
}
异步版本的TcpListener为listener.incoming()实现了Stream trait,这样listener.incoming()不再阻塞,且使用for_each_concurrent可以并发地处理从Stream获取的元素。
现在关键在于handle_connection不能再阻塞:
/// # 函数作用
/// 处理连接:读取请求,回应请求
async fn handle_connection(mut stream: TcpStream) {let buf_reader = BufReader::new(&mut stream);// 使用next而不是lines,因为我们只需要读取第一行,判断具体的request方法let request_line = buf_reader.lines().next().await.unwrap().unwrap();// 根据请求的不同,返回不同的响应let (status_line, filename) = match &request_line[..] {"GET / HTTP/1.1" => ("HTTP/1.1 200 OK", "hello.html"), // 请求 / 资源"GET /sleep HTTP/1.1" => { // 请求 /sleep 资源// 没有使用std::thread::sleep进行睡眠,原因是该函数是阻塞的,它会让当前线程陷入睡眠中,导致其他任务无法继续运行task::sleep(Duration::from_secs(5)).await;("HTTP/1.1 200 OK", "hello.html")}_ => ("HTTP/1.1 404 NOT FOUND", "404.html"),};let contents = fs::read_to_string(filename).unwrap();let length = contents.len();let response = format!("{status_line}\r\nContent-Length: {length}\r\n\r\n{contents}");// write_all接收&[u8]类型作为参数,这里需要用as_bytes将字符串转换为字节数组stream.write_all(response.as_bytes()).await.unwrap();
}
在将数据读写改造成异步后,现在该函数也彻底变成了异步版本,可以并发地处理连接
使用多线程提升性能
async并发和多线程其实并不冲突,async-std包也允许我们使用多个线程去处理,由于handle_connection实现了Send trait不会阻塞,因此使用async_std::task::spawn是非常安全的:
use async_std::task::spawn;#[async_std::main]
async fn main() {let listener = TcpListener::bind("localhost:8080").await.unwarp():listener.incoming().for_each_concurrent(None, |stream| async move {let stream = stream.unwrap();spawn(handle_connection(stream));}).await;
}
但是这里是为每个请求都单独创建了一个线程,实际上需要限制创建线程的数量,可以通过线程池来实现。具体可以看这篇无async的多线程版本的Web服务器
相关文章:
使用rust写一个Web服务器——async-std版本
文章目录 实现异步代码并发地处理连接使用多线程提升性能 使用rust实现一个异步运行时是async-std的单线程Web服务器。 仓库地址: 1037827920/web-server: 使用rust编写的简单web服务器 (github.com) 在之前的单线程版本的Web服务器代码上进行修改,具体…...
C语言复习概要(一)
本文 C语言入门详解:从基础概念到分支与循环1. C语言常见概念1.1 程序的基本结构1.2 变量作用域和存储类1.3 输入输出1.4 编译与运行 2. C语言中的数据类型和变量2.1 基本数据类型2.2 变量的声明与初始化2.3 常量与枚举 3. C语言的分支结构3.1 if语句3.2 if-else语句…...
二、kafka生产与消费全流程
一、使用java代码生产、消费消息 1、生产者 package com.allwe.client.simple;import lombok.extern.slf4j.Slf4j; import org.apache.kafka.clients.producer.KafkaProducer; import org.apache.kafka.clients.producer.ProducerConfig; import org.apache.kafka.clients.pr…...
本地搭建OnlyOffice在线文档编辑器结合内网穿透实现远程协作
文章目录 前言1. 安装Docker2. 本地安装部署ONLYOFFICE3. 安装cpolar内网穿透4. 固定OnlyOffice公网地址 前言 本篇文章讲解如何使用Docker在本地Linux服务器上安装ONLYOFFICE,并结合cpolar内网穿透实现公网访问本地部署的文档编辑器与远程协作。 Community Editi…...
ScrapeGraphAI 大模型增强的网络爬虫
在数据驱动的动态领域,从在线资源中提取有价值的见解至关重要。从市场分析到学术研究,对特定数据的需求推动了对强大的网络抓取工具的需求。 NSDT工具推荐: Three.js AI纹理开发包 - YOLO合成数据生成器 - GLTF/GLB在线编辑 - 3D模型格式在线…...
PDF转换为TIF,JPG的一个简易工具(含下载链接)
目录 0.前言: 1.工具目录 2.工具功能(效果),如何运行 效果 PDF转换为JPG(带颜色) PDF转换为TIF(LZW形式压缩,可以显示子的深浅) PDF转换为TIF(CCITT形…...
Wireshark 解析QQ、微信的通信协议|TCP|UDP
写在前面 QQ,微信这样的聊天软件。我们一般称为im,Instant Messaging,即时通讯系统。那大家会不会有疑问,自己聊天内容会不会被黑客或者不法分子知道?这种体量的im是基于tcp还是udp呢?这篇文章我们就来探索…...
网络编程(5)——模拟伪闭包实现连接的安全回收
六、day6 今天学习如何利用C11模拟伪闭包实现连接的安全回收,之前的异步服务器为echo模式,但存在安全隐患,在极端情况下客户端关闭可能会导致触发写和读回调函数,二者都进入错误处理逻辑,进而造成二次析构。今天学习如…...
C#绘制动态曲线
前言 用于实时显示数据动态曲线,比如:SOC。 //用于绘制动态曲线,可置于定时函数中,定时更新数据曲线 void DrawSocGraph() {double f (double)MainForm.readData[12]; //display datachart1.Series[0].Points.Add(f);if (ch…...
用Python实现运筹学——Day 10: 线性规划的计算机求解
一、学习内容 1. 使用 Python 的 scipy.optimize.linprog 进行线性规划求解 scipy.optimize.linprog 是 Python 中用于求解线性规划问题的函数。它实现了单纯形法、内点法等算法,能够处理求解最大化或最小化问题,同时满足线性约束条件。 线性规划问题的…...
[C++]使用C++部署yolov11目标检测的tensorrt模型支持图片视频推理windows测试通过
官方框架: https://github.com/ultralytics/ultralytics yolov8官方最近推出yolov11框架,标志着目标检测又多了一个检测利器,于是尝试在windows下部署yolov11的tensorrt模型,并最终成功。 重要说明:安装环境视为最基…...
霍夫曼树及其与B树和决策树的异同
霍夫曼树是一种用于数据压缩的二叉树结构,通常应用于霍夫曼编码算法中。它的主要作用是通过对符号进行高效编码,减少数据的存储空间。霍夫曼树在压缩领域扮演着重要角色,与B树、决策树等数据结构都有一些相似之处,但又在应用场景和…...
CompletableFuture常用方法
一、获得结果和触发计算 1.获取结果 (1)public T get() public class CompletableFutureAPIDemo{public static void main(String[] args) throws ExecutionException, InterruptedException{CompletableFuture<String> completableFuture Com…...
本地化测试对游戏漏洞修复的影响
本地化测试在游戏开发的质量保证过程中起着至关重要的作用,尤其是在修复bug方面。当游戏为全球市场做准备时,它们通常会被翻译和改编成各种语言和文化背景。这种本地化带来了新的挑战,例如潜在的语言错误、文化误解,甚至是不同地区…...
使用rust实现rtsp码流截图
中文互联网上的rust示例程序源码还是太稀少,找资料很是麻烦,下面是自己用业余时间开发实现的一个对批量rtsp码流源进行关键帧截图并存盘的rust demo源码记录。 要编译这个源码需要先安装vcpkg,然后用vcpkg install ffmpeg安装最新版本的ffmpe…...
Cpp::STL—string类的模拟实现(12)
文章目录 前言一、string类各函数接口总览二、默认构造函数string(const char* str "");string(const string& str);传统拷贝写法现代拷贝写法 string& operator(const string& str);传统赋值构造现代赋值构造 ~string(); 三、迭代器相关函数begin &…...
一文搞懂SentencePiece的使用
目录 1. 什么是 SentencePiece?2. SentencePiece 基础概念2.1 SentencePiece 的工作原理2.2 SentencePiece 的优点 3. SentencePiece 的使用3.1 安装 SentencePiece3.2 训练模型与加载模型3.3 encode(高频)3.4 decode(高频&#x…...
一个简单的摄像头应用程序1
这个Python脚本实现了一个基于OpenCV的简单摄像头应用,我们在原有的基础上增加了录制视频等功能,用户可以通过该应用进行拍照、录制视频,并查看已拍摄的照片。以下是该脚本的主要功能和一些使用时需要注意的事项: 功能 拍照: 用户可以通过点击界面上的“拍照”按钮或按…...
通过PHP获取商品详情
在电子商务的浪潮中,数据的重要性不言而喻。商品详情信息对于电商运营者来说尤为宝贵。PHP,作为一种广泛应用的服务器端脚本语言,为我们提供了获取商品详情的便捷途径。 了解API接口文档 开放平台提供了详细的API接口文档。你需要熟悉商品详…...
【Android】获取备案所需的公钥以及签名MD5值
目录 重要前提 获取签名MD5值 获取公钥 重要前提 生成jks文件以及gradle配置应用该文件。具体步骤请参考我这篇文章:【Android】配置Gradle打包apk的环境_generate signed bundle or apk-CSDN博客 你只需要从头看到该文章的配置build.gradle(app&…...
看480p、720p、1080p、2k、4k、视频一般需要多大带宽呢?
看视频都喜欢看高清,那么一般来说看电影不卡顿需要多大带宽呢? 以4K为例,这里引用一位网友的回答:“视频分辨率4092*2160,每个像素用红蓝绿三个256色(8bit)的数据表示,视频帧数为60fps,那么一秒钟画面的数据量是:4096*2160*3*8*60≈11.9Gbps。此外声音大概是视频数据量…...
解决IDEA中@Autowired红色报错的实用指南:原因与解决方案
前言: 在使用Spring Boot开发时,Autowired注解是实现依赖注入的常用方式。然而,许多开发者在IDEA中使用Autowired时,可能会遇到红色报错,导致代码的可读性降低。本文将探讨导致这种现象的原因,并提供几种解…...
408知识点自检(一)
一、细节题 虚电路是面向连接的吗?虚电路线路上会不会有其他虚电路通过?虚电路适合什么类型的数据交换?虚电路的可靠性靠其他协议还是自己?固态硬盘的优势体现在什么存取方式?中断向量地址是谁的地址?多播…...
负载均衡--相关面试题(六)
在负载均衡的面试中,可能会遇到一系列涉及概念、原理、实践应用以及技术细节的问题。以下是一些常见的负载均衡面试题及其详细解答: 一、什么是负载均衡? 回答:负载均衡是一种将网络请求或数据传输工作分配给多个服务器或网络资源…...
【Unity踩坑】Unity更新Google Play结算库
一、问题描述: 在Google Play上提交了app bundle后,提示如下错误。 我使用的是Unity 2022.01.20f1,看来用的Play结算库版本是4.0 查了一下文档,Google Play结算库的维护周期是两年。现在需要更新到至少6.0。 二、更新过程 1. 下…...
Redis:hash类型
Redis:hash类型 hash命令设置与读取HSETHGETHMGET 哈希操作HEXISTSHDELHKEYSHVALSHGETALLHLENHSETNXHINCRBYHINCRBYFLOAT 内部编码ziplisthashtable 目前主流的编程语言中,几乎都提供了哈希表相关的容器,Redis自然也会支持对应的内容…...
力扣9.30
1749. 任意子数组和的绝对值的最大值 给你一个整数数组 nums 。一个子数组 [numsl, numsl1, ..., numsr-1, numsr] 的 和的绝对值 为 abs(numsl numsl1 ... numsr-1 numsr) 。 请你找出 nums 中 和的绝对值 最大的任意子数组(可能为空),…...
kafka下载配置
下载安装 参开kafka社区 zookeeperkafka消息队列群集部署https://apache.csdn.net/66c958fb10164416336632c3.html 下载 kafka_2.12-3.2.0安装包快速下载地址分享 官网下载链接地址: 官网下载地址:https://kafka.apache.org/downloads 官网呢下载慢…...
nlp任务之预测中间词-huggingface
目录 1.加载编码器 1.1编码试算 2.加载数据集 3.数据集处理 3.1 map映射:只对数据集中的sentence数据进行编码 3.2用filter()过滤 单词太少的句子过滤掉 3.3截断句子 4.创建数据加载器Dataloader 5. 下游任务模型 6.测试预测代码 7.训练代码 8.保…...
《程序猿之Redis缓存实战 · Redis 与数据库一致性》
📢 大家好,我是 【战神刘玉栋】,有10多年的研发经验,致力于前后端技术栈的知识沉淀和传播。 💗 🌻 CSDN入驻不久,希望大家多多支持,后续会继续提升文章质量,绝不滥竽充数…...
django做的网站有哪些/网站搭建需要什么技术
给主题的fork加一个remote 给 fork 配置一个 remote使用 git remote -v 查看远程状态➜ next git:(master) git remote -v origin gitgithub.com:luodaoyi/hexo-theme-next.git (fetch) origin gitgithub.com:luodaoyi/hexo-theme-next.git (push)把原作者的远程仓库添加到r…...
首页网站关键词优化教程/下载百度app免费下载安装
sift算法详解及应用(特别详细版)BEIJINGINSTITUTEOF TECHNOLOGY尺度不变特征变换匹配算法Scale Invariant FeatureTransform(SIFT )宋丹2016/11/ 9 1/60SIFTScale Invariant Feature Transform1. SIFT简介2. SIFT算法实现细节3. SIFT算法的应用领域4. SIFT算法的扩展与改进2016…...
做网站颜色黑色代码多少/官方网站怎么查询
SVN中TortoiseSVN 是比较出门的一款SVN软件 TortoiseSVN 是Subversion 版本控制系统的一个免费开源客户端。 由于TortoiseSVN 默认是英文的;所以很多小伙伴可能有压力;那么这次我们来介绍一下怎么样安装中文的语言包 工具/原料 能上网的windows电脑一台方…...
网站设计与管理的软件/优书网首页
使用终端恢复你 Linux 系统上仍在运行进程的已删除文件的快速指南。许多情况下,删除的文件都可以恢复,比如在该文件有活动的进程在操作它,并且目前被单个或多个用户使用时。在 Linux 系统中,每个当前正在运行的进程都会获得 ID&am…...
犀牛建设网站/福建百度推广
百度智能云 云生态狂欢季 热门云产品1折起>>> Zabbix 4.2.0 新版本已发布,该版本引入了大量的新特性和改进,以及 bug 修复。列举部分值得关注的如下: 在预处理测试表单中进行测试会话之前实现了测试值保存改进对多行输入的控制为 …...
杭州做网站比较好的公司/游戏推广员好做吗
python2中的range返回的是一个列表 python3中的range返回的是一个迭代值 for i in range(1,10)在python2和python3中都可以使用,但是要生成1-10的列表,就需要用list(range(1,10)) 在Python核心编程中要输出abcde的子序列,如果用(p…...