如果您在 YouTube 和 Twitch 等平台上观看流媒体,您可能遇到过歌曲请求系统。歌曲请求系统允许观众将歌曲添加到 歌曲队列中。当歌曲到达队列的最前面时,歌曲会在直播流中播放。
在 Rocket crate 的帮助下,用Rust 为这个系统创建一个 API 非常容易。
使用Rocket箱创建一个 RESTful API。
初始化:
cargo init <YOUR_PROJECT_NAME>
|
加入依赖:
[dependencies]
# NOTE: This is a pre-release version. # Thus, It is suggested NOT to use this in production. rocket = "0.5.0-rc.2"
|
在我们的Cargo项目准备就绪后,我们可以完全删除默认的主函数。这将为我们的新rocket方法腾出空间,该方法归属于Rocket的发射程序宏。
这个函数实质上将取代我们的主函数,并在启动时被调用。
//main.rs
#[macro_use] extern crate rocket;
use rocket::{Build, Rocket};
#[launch] fn rocket() -> Rocket<Build> { Rocket::build() // 设置`/`路由路径作为我们路由的基础。 // 当我们创建我们的路由时,我们将把它们包括在`routes!`宏的参数中。 .mount("/", routes![]) }
|
运行后输出:
Configured for debug. >> address: 127.0.0.1 >> port: 8000 >> workers: 6 >> ident: Rocket >> limits: bytes = 8KiB, data-form = 2MiB, file = 1MiB, form = 32KiB, json = 1MiB, msgpack = 1MiB, string = 8KiB >> temp dir: C:\Users\dev\AppData\Local\Temp\ >> http/2: true >> keep-alive: 5s >> tls: disabled >> shutdown: ctrlc = true, force = true, grace = 2s, mercy = 3s >> log level: normal >> cli colors: true Fairings: >> Shield (liftoff, response, singleton) Shield: >> X-Content-Type-Options: nosniff >> Permissions-Policy: interest-cohort=() >> X-Frame-Options: SAMEORIGIN Rocket has launched from http://127.0.0.1:8000
|
现在我们有了我们的rocket函数,我们可以开始为我们的API开发功能。
储存歌曲队列
为了存储歌曲队列,我们将使用一个静态的LinkedList。
我们使用LinkedList是因为它包含在标准库中,并且基本上是作为一个具有额外功能的队列。
不过,为了允许静态地修改列表,我们必须用Mutex来包装这个列表。
use std::collections::LinkedList; use std::sync::Mutex;
static SONG_QUEUE: Mutex<LinkedList<String>> = Mutex::new(LinkedList::new());
|
更多Mutex, click here.(中文:https://github.com/kumakichi/easy_rust_chs)
我们将多次重复以下代码:
let lock = SONG_QUEUE .lock() .expect("Unable to acquire lock on song queue because the Mutex was poisoned");
|
如果我们创建一个为我们执行此操作的函数会更好。
use std::sync::{Mutex, MutexGuard};
fn acquire_queue<'a>() -> MutexGuard<'a, LinkedList<String>> { SONG_QUEUE .lock() .expect("Unable to acquire lock on song queue because the Mutex was poisoned") }
|
创建add路由
SONG_QUEUE定义好变量后,我们就可以开始创建路由了。
该add路由将收到一个包含歌曲名称的POST请求,然后将其添加到歌曲队列中。
或者,我们可以返回歌曲所在的位置。
#[post("/add/<song_name>")] fn add_song(song_name: String) -> String { let mut lock = acquire_queue();
lock.push_back(song_name);
format!("Song added. This song is in position {}.", lock.len()) }
|
不要忘记在routes!宏中注册这个新路由。
- - .mount("/", routes![])
- + .mount("/", routes![add_song])
测试:
C:\Users\dev>curl -X POST http://localhost:8000/add/Hello Song added. This song is in position 1. C:\Users\dev>curl -X POST http://localhost:8000/add/Hello%20number%202 Song added. This song is in position 2.
|
创建view路由
用户现在可以添加歌曲,但无法查看当前在队列中的歌曲。
别担心,我们将创建一个新的GET路由。
只有一行代码!
#[get("/view")] fn view() -> String { format!("{:?}", acquire_queue()) }
|
不要忘记在routes!宏中注册这个新路由。
- - .mount("/", routes![add_song])
- + .mount("/", routes![add_song, view])
运行:
C:\Users\dev>curl -X POST http://localhost:8000/add/Hello%20World Song added. This song is in position 1.
C:\Users\dev>curl http://localhost:8000/view ["Hello World"]
|
删除歌曲
为了文章的简单起见,一旦歌曲到达队列的最前面,我们将不会真正播放它们。相反,我们只会在经过一定时间后删除歌曲。
在这种情况下,我们会在歌曲排到队列前 60 秒后移除歌曲
use std::thread; use std::time::Duration;
fn remove_song_timer() { while !acquire_queue().is_empty() { thread::sleep(Duration::from_secs(60)); acquire_queue().pop_front(); } }
|
我们需要修改我们的add_song路由以确保在remove_song_timer将歌曲添加到空队列时生成线程。
#[post("/add/<song_name>")] fn add_song(song_name: String) -> String { let mut lock = acquire_queue();
if lock.is_empty() { thread::spawn(remove_song_timer); } lock.push_back(song_name);
format!("Song added. This song is in position {}.", lock.len()) }
|
结果:
C:\Users\dev>curl -X POST http://localhost:8000/add/Hello%20World Song added. This song is in position 1. C:\Users\dev>curl http://localhost:8000/view ["Hello World"]
|
60秒后……
C:\Users\dev>curl http://localhost:8000/view []
|