Rust web简单实战

news/2024/5/18 23:29:44

一、使用async搭建简单的web服务

1、修改cargo.toml文件添加依赖

[dependencies]
futures = "0.3"
tokio = { version = "1", features = ["full"] }
[dependencies.async-std]
version = "1.6"
features = ["attributes"]

2、搭建web服务

(1)单线程并发处理请求

这里解释一下,单线程并发处理请求,其实每一个请求都是一个future,如果一个future卡了,会去处理别的future,async原理可以去看我关于async的博客

use async_std::net::TcpListener;
use async_std::net::TcpStream;
use futures::stream::StreamExt;use std::fs;
use async_std::prelude::*;
use std::time::Duration;
use async_std::task;#[async_std::main]
async fn main() {let listener = TcpListener::bind("127.0.0.1:7878").await.unwrap(); // 创建 TCP 监听器,绑定到本地 7878 端口listener.incoming() // 监听传入的连接.for_each_concurrent(/* limit */ None, |tcpstream| async move {//stream 流异步并发处理let tcpstream = tcpstream.unwrap(); // 获取传入连接handle_connection(tcpstream).await; // 处理连接的异步任务}).await; // 等待所有连接处理完毕
}async fn handle_connection(mut stream: TcpStream) {let mut buffer = [0; 1024];stream.read(&mut buffer).await.unwrap(); // 读取 TCP 流数据到缓冲区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\r\n\r\n", "hello.html") // 如果请求是 GET /,返回 200 OK 状态和 hello.html 文件} else if buffer.starts_with(sleep) {task::sleep(Duration::from_secs(5)).await; // 如果请求是 GET /sleep,等待 5 秒钟("HTTP/1.1 200 OK\r\n\r\n", "hello.html") // 然后返回 200 OK 状态和 hello.html 文件} else {("HTTP/1.1 404 NOT FOUND\r\n\r\n", "404.html") // 其他情况返回 404 NOT FOUND 状态和 404.html 文件};// 读取文件内容到字符串let contents = fs::read_to_string(filename).unwrap();let response = format!("{status_line}{contents}"); // 构建响应字符串// 将响应字符串写入 TCP 流,并刷新stream.write(response.as_bytes()).await.unwrap();stream.flush().await.unwrap();
}

(2)多线程并发处理请求

lib.rs

// src/lib.rs
use std::{sync::{mpsc, Arc, Mutex},thread,
};pub struct ThreadPool {workers: Vec<Worker>,sender: Option<mpsc::Sender<Job>>,
}type Job = Box<dyn FnOnce() + Send + 'static>;impl ThreadPool {/// Create a new ThreadPool.////// The size is the number of threads in the pool.////// # Panics////// The `new` function will panic if the size is zero.pub fn new(size: usize) -> ThreadPool {assert!(size > 0);let (sender, receiver) = mpsc::channel();let receiver = Arc::new(Mutex::new(receiver));let mut workers = Vec::with_capacity(size);for id in 0..size {workers.push(Worker::new(id, Arc::clone(&receiver)));}ThreadPool {workers,sender: Some(sender),}}pub fn execute<F>(&self, f: F)whereF: FnOnce() + Send + 'static,{let job = Box::new(f);self.sender.as_ref().unwrap().send(job).unwrap();}
}impl Drop for ThreadPool {fn drop(&mut self) {drop(self.sender.take());for worker in &mut self.workers {println!("Shutting down worker {}", worker.id);if let Some(thread) = worker.thread.take() {thread.join().unwrap();}}}
}struct Worker {id: usize,thread: Option<thread::JoinHandle<()>>,
}impl Worker {fn new(id: usize, receiver: Arc<Mutex<mpsc::Receiver<Job>>>) -> Worker {let thread = thread::spawn(move || loop {let message = receiver.lock().unwrap().recv();match message {Ok(job) => {println!("Worker {id} got a job; executing.");job();}Err(_) => {println!("Worker {id} disconnected; shutting down.");break;}}});Worker {id,thread: Some(thread),}}
}

main.rs

// src/main.rs
use hello_package::ThreadPool;
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();let pool = ThreadPool::new(4);for stream in listener.incoming().take(4) {//take限制请求次数let stream = stream.unwrap();pool.execute(|| {handle_connection(stream);});}println!("Shutting down.");
}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_all(response.as_bytes()).unwrap();stream.flush().unwrap();
}

在这里插入图片描述


http://www.mrgr.cn/p/11356851

相关文章

sql 中having和where区别

where 是用于筛选表中满足条件的行&#xff0c;不可以和聚类函数一起使用 having 是用于筛选满足条件的组 &#xff0c;可与聚合函数一起使用 所以having语句中不能使用select中定义的名字

.Net 8.0 下的新RPC,IceRPC之如何创建连接connection

作者引言很高兴啊,我们来到了IceRPC之如何创建连接connection,基础引导,让自已不在迷茫,快乐的畅游世界。如何创建连接connection学习如何使用IceRPC,创建和接受连接。连接有什么用途? 连接在 IceRPC 中发挥着核心作用: 通过连接向服务端发送请求,然后通过同一连接收到响应…

Elasticsearch 数据聚合

Bucket聚合&#xff08;桶聚合&#xff09; 对文档做分组&#xff0c;aggs 按照文档字段值或日期进行分组&#xff0c;能参与分词的字段不能做聚合&#xff0c;如text类型的字段 例如&#xff1a;根据城市名称做聚合&#xff0c;也就是城市名称对数据进行分组统计。可以加qu…

Redis---------实现商品秒杀业务,包括唯一ID,超卖问题,分布式锁

订单ID必须是唯一 唯一ID构成&#xff1a; 代码生成唯一ID&#xff1a; import org.springframework.data.redis.core.StringRedisTemplate; import org.springframework.stereotype.Component; import java.time.LocalDateTime; import java.time.ZoneOffset; import java.tim…

pde复习笔记 第一章 波动方程 第六节 能量不等式、波动方程解的唯一性和稳定性

能量不等式 这一部分需要知道的是能量的表达式 \[E(t)=\int_{0}^{l}u_{t}^{2}+a^{2}u_{x}^{2} dx \]一般而言题目常见的问法是证明能量是减少的,也就是我们需要证明 \[\dfrac{d}{dt}E(t) \le0 \]在计算\(\dfrac{d}{dt}E(t) \le0\)的时候一定会用的题目给的方程条件去凑微分…

2024 年 5 月 4 日 青年节 周六 多云 常(910 字)

正文看完了《只有街舞》系列的纪录片。每次看完这种类型的片子,总会激发我许多感触。我总是想书写一个庞大而宏伟的故事,通过故事和人物的行动折射背后深沉的主题。使命感、勇气、选择、放弃、未知、疲惫、克制、时间、迷茫、信念、坚持、自我感动、爱、友情、生活等等等等。…

Mac更新python3.12 解决pip3安装报错

Mac使用homebrew更新了python3.12,删除了以前的版本和pip3安装软件时候报错。error: externally-managed-environment This environment is externally managed ╰─> To install Python packages system-wide, try brew installxyz, where xyz is the package you are try…

数据结构练习题---环形链表详解

链表成环&#xff0c;在力扣中有这样的两道题目 https://leetcode.cn/problems/linked-list-cycle/ https://leetcode.cn/problems/linked-list-cycle-ii/description/ 这道题的经典解法是利用快慢指针&#xff0c;如果链表是一个环形链表&#xff0c;那么快指针(fast)和慢指…

【c++】模板编程解密:C++中的特化、实例化和分离编译

&#x1f525;个人主页&#xff1a;Quitecoder &#x1f525;专栏&#xff1a;c笔记仓 朋友们大家好&#xff0c;本篇文章我们来学习模版的进阶部分 目录 1.非类型模版参数按需实例化 2.模版的特化函数模版特化函数模版的特化类模版全特化偏特化 3.分离编译模版分离编译 1.非类…

2024-05-05 通达信选股 双黄连

黄金阴:=O>REF(C,1) AND C<O AND V<REF(V,1)*SL2; 黄金阳:= C>O AND O<REF(C,1) AND V<REF(V,1)*SL1 AND C<REF(C,1); COUNT(黄金阳,1)>=1 AND COUNT(黄金阴,5)>=1; ----------------------------------------------------------------------------…

01-MySQL 基础篇笔记

一、MySQL 概述 1.1 数据库相关概念 数据库&#xff1a;&#xff08;DB&#xff1a;DataBase&#xff09; 存储数据的仓库&#xff0c;数据是有组织的进行存储 数据库管理系统&#xff1a;&#xff08;DBMS&#xff1a;DataBase Management System&#xff09; 操作和管理数…

网课-微积分学习笔记

qwq微分有时也写作 \(\frac{\mathrm{d} y}{\mathrm{d} x}\)。 常见函数导数:可以认为,\(e\) 的定义就是 \((e^x) = e^x\)。 导数是一个线性的算子,即:其中 \(f(g(x))\) 指的是在求出 \(f(x)\) 后把 \(g(x)\) 代入。(以上定律根据 \(f(x+\Delta) = f(x)+f(x)\Delta+o(\Delt…

232Modbus转Profinet网关接扫码枪与PLC通讯

232Modbus转Profinet网关(XD-PNR100/300)的主要作用是实现Modbus协议和Profinet协议之间的转换和通信。本案例是用Modbus转Profinet网关接扫码枪与PLC通讯,扫码枪通常通过特定的接口与计算机或其他设备传输数据,而PLC(可编程逻辑控制器)则通常使用Profinet等工业通信协议…

Goose:Go语言渐进式的数据库迁移工具

Goose:Go语言渐进式的数据库迁移工具 原创 K8sCat 源自开发者 2024-05-04 22:57 广东 听全文源自开发者 专注于提供关于Go语言的实用教程、案例分析、最新趋势,以及云原生技术的深度解析和实践经验分享。 214篇原创内容公众号数据库迁移是软件开发过程中重要的一部分,随着业…

将java项目上传到GitHub步骤

文章目录 GitHub 作用github如何修改默认分支为master手把手教你把项目上传github上github怎么删除仓库或项目执行到push时报错的解决办法github怎么修改仓库语言 GitHub 作用 GitHub 是一个存放软件代码的网站&#xff0c;主要用于软件开发者存储和管理其项目源代码&#xff…

数据分析的五大流程:需求、获取、处理、分析、可视化

数据分析的五大流程:需求、获取、处理、分析、可视化

DDD:根据maven的脚手架archetype生成ddd多模块项目目录结构

随着领域驱动的兴起&#xff0c;很多人都想学习如何进行ddd的项目开发&#xff0c;那ddd的项目结构是怎么样的&#xff1f;又是如何结合SpringBoot呢&#xff1f;那么针对这个问题&#xff0c;笔者使用maven的archetype封装一个相对通用的ddd的项目目录&#xff0c;方便一键生成…

图像处理ASIC设计方法 笔记21 标记ASIC的顶层状态机

目录 (一)标记ASIC的工作流程1 ASIC首先从控制寄存器内读出待标记图像的基本参数2若写入了有效的启动命令,则进入下面一帧图像的标记过程。3 ASIC通过接口模块从FIFO1中读取待标记的图像4一帧图像初步标记完成后进行等价表的整理压缩5从临时标记存储器中读取临时标记送入标记…

Matlab各个版本介绍、区别分析及推荐

MATLAB&#xff0c;由美国MathWorks公司出品&#xff0c;是一款广泛应用的商业数学软件。自其诞生之初&#xff0c;MATLAB便以其强大的矩阵计算能力、灵活的编程环境以及广泛的应用领域&#xff0c;赢得了全球科研工作者和工程师的青睐。本文将详细介绍MATLAB的各个版本&#x…

智慧文旅展现文化新风貌,科技助力旅行品质升级:借助智慧技术,文旅产业焕发新生机,为旅行者带来更高品质的文化体验之旅

一、引言 在数字化、智能化的浪潮下&#xff0c;文旅产业正迎来前所未有的发展机遇。智慧文旅作为文旅产业与信息技术深度融合的产物&#xff0c;不仅为旅行者带来了全新的文化体验&#xff0c;也为文旅产业注入了新的活力。本文旨在探讨智慧文旅如何借助智慧技术展现文化新风…