mirror of
https://github.com/tokio-rs/axum.git
synced 2026-09-08 00:00:24 +02:00
docs(axum): recommend actor pattern for concurrent WebSocket read/write (#3842)
Co-authored-by: David Mládek <[email protected]>
This commit is contained in:
co-authored by
David Mládek
parent
194030d7d0
commit
130b421f26
+51
-2
@@ -65,8 +65,54 @@
|
|||||||
//!
|
//!
|
||||||
//! # Read and write concurrently
|
//! # Read and write concurrently
|
||||||
//!
|
//!
|
||||||
//! If you need to read and write concurrently from a [`WebSocket`] you can use
|
//! To read and write concurrently it's not necessary to split the socket into
|
||||||
//! [`StreamExt::split`]:
|
//! separate halves. Instead, have a single task own the [`WebSocket`] — the
|
||||||
|
//! [actor pattern] — and use [`select!`] to race receiving a message from the
|
||||||
|
//! socket against other event sources, such as an [`mpsc`] channel that other
|
||||||
|
//! tasks use to send messages to the client. [`WebSocket::recv`] is cancel
|
||||||
|
//! safe, so it can be used as a `select!` branch directly:
|
||||||
|
//!
|
||||||
|
//! ```rust,no_run
|
||||||
|
//! use axum::extract::ws::{Message, WebSocket};
|
||||||
|
//! use tokio::sync::mpsc;
|
||||||
|
//!
|
||||||
|
//! async fn handle_socket(mut socket: WebSocket) {
|
||||||
|
//! let (tx, mut rx) = mpsc::channel::<Message>(32);
|
||||||
|
//!
|
||||||
|
//! // give `tx` to tasks that want to send messages to the client
|
||||||
|
//! tokio::spawn(background_task(tx));
|
||||||
|
//!
|
||||||
|
//! loop {
|
||||||
|
//! tokio::select! {
|
||||||
|
//! msg = socket.recv() => {
|
||||||
|
//! match msg {
|
||||||
|
//! Some(Ok(msg)) => {
|
||||||
|
//! // handle the message received from the client
|
||||||
|
//! # _ = msg;
|
||||||
|
//! }
|
||||||
|
//! // client disconnected or an error occurred
|
||||||
|
//! _ => return,
|
||||||
|
//! }
|
||||||
|
//! }
|
||||||
|
//! Some(msg) = rx.recv() => {
|
||||||
|
//! if socket.send(msg).await.is_err() {
|
||||||
|
//! // client disconnected
|
||||||
|
//! return;
|
||||||
|
//! }
|
||||||
|
//! }
|
||||||
|
//! }
|
||||||
|
//! }
|
||||||
|
//! }
|
||||||
|
//!
|
||||||
|
//! async fn background_task(tx: mpsc::Sender<Message>) {
|
||||||
|
//! // ...
|
||||||
|
//! }
|
||||||
|
//! ```
|
||||||
|
//!
|
||||||
|
//! Alternatively you can use [`StreamExt::split`] to split the socket into a
|
||||||
|
//! sender and a receiver half that can be moved to independent tasks. Note
|
||||||
|
//! that each half is protected by a lock that must be acquired for every
|
||||||
|
//! operation, so prefer the actor pattern where possible:
|
||||||
//!
|
//!
|
||||||
//! ```rust,no_run
|
//! ```rust,no_run
|
||||||
//! use axum::{Error, extract::ws::{WebSocket, Message}};
|
//! use axum::{Error, extract::ws::{WebSocket, Message}};
|
||||||
@@ -88,6 +134,9 @@
|
|||||||
//! }
|
//! }
|
||||||
//! ```
|
//! ```
|
||||||
//!
|
//!
|
||||||
|
//! [actor pattern]: https://ryhl.io/blog/actors-with-tokio/
|
||||||
|
//! [`mpsc`]: https://docs.rs/tokio/latest/tokio/sync/mpsc/index.html
|
||||||
|
//! [`select!`]: https://docs.rs/tokio/latest/tokio/macro.select.html
|
||||||
//! [`StreamExt::split`]: https://docs.rs/futures/0.3.17/futures/stream/trait.StreamExt.html#method.split
|
//! [`StreamExt::split`]: https://docs.rs/futures/0.3.17/futures/stream/trait.StreamExt.html#method.split
|
||||||
|
|
||||||
use self::rejection::*;
|
use self::rejection::*;
|
||||||
|
|||||||
Reference in New Issue
Block a user