1
0
mirror of https://github.com/actix/actix-website synced 2024-11-24 00:41:07 +01:00

Add explanations regarding actix-web's multithreaded server model (#150)

* Add explanations regarding actix-web's multithreaded server model

* Improve phrasing

* Typo fix

Co-authored-by: Yuki Okushi <huyuumi.dev@gmail.com>
This commit is contained in:
Rotem Yaari 2020-01-29 14:27:22 +02:00 committed by GitHub
parent c1a8103cbc
commit 8c8de9f7fb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

View File

@ -31,18 +31,53 @@ Following example shows how to start http server in separate thread.
## Multi-threading
`HttpServer` automatically starts a number of http workers, by default this number is
`HttpServer` automatically starts a number of http *workers*, by default this number is
equal to number of logical CPUs in the system. This number can be overridden with the
[`HttpServer::workers()`][workers] method.
{{< include-example example="server" file="workers.rs" section="workers" >}}
The server creates a separate application instance for each created worker. Application state
is not shared between threads. To share state, `Arc` could be used.
Once the workers are created, they each receive a separate *application* instance to handle
requests. Application state is not shared between the threads, and handlers are free to manipulate
their copy of the state with no concurrency concerns.
> Application state does not need to be `Send` or `Sync`, but application
factory must be `Send` + `Sync`.
To share state between worker threads, use an `Arc`. Special care should be taken once sharing and
synchronization are introduced. In many cases, performance costs are inadvertently introduced as a
result of locking the shared state for modifications.
In some cases these costs can be alleviated using more efficient locking strategies, for example
using [read/write locks](https://doc.rust-lang.org/std/sync/struct.RwLock.html) instead of
[mutexes](https://doc.rust-lang.org/std/sync/struct.Mutex.html) to achieve non-exclusive locking,
but the most performant implementations often tend to be ones in which no locking occurs at all.
Since each worker thread processes its requests sequentially, handlers which block the current
thread will cause the current worker to stop processing new requests:
```rust
fn my_handler() -> impl Responder {
std::thread::sleep(Duration::from_secs(5)); // <-- Bad practice! Will cause the current worker thread to hang!
"response"
}
```
For this reason, any long, non-cpu-bound operation (e.g. I/O, database operations, etc.) should be
expressed as futures or asynchronous functions. Async handlers get executed concurrently by worker
threads and thus don't block execution:
```rust
async fn my_handler() -> impl Responder {
tokio::time::delay_for(Duration::from_secs(5)).await; // <-- Ok. Worker thread will handle other requests here
"response"
}
```
The same limitation applies to extractors as well. When a handler function receives an argument
which implements `FromRequest`, and that implementation blocks the current thread, the worker thread
will block when running the handler. Special attention must be given when implementing extractors
for this very reason, and they should also be implemented asynchronously where needed.
## SSL
There are two features for the ssl server: `rustls` and `openssl`. The `rustls` feature is for