Что такое актор-модель в Actix и чем она отличается от стандартного асинхронного программирования?
Актор в Actix — изолированный объект с собственным состоянием, который получает сообщения через Addr<A> и обрабатывает их последовательно без Mutex. В отличие от async/await, состояние не разделяется — гонки данных исключены архитектурно.
Актор-модель в Actix
Actix построен на актор-модели из библиотеки actix. Актор — это изолированный объект с собственным состоянием, который общается с другими только через сообщения. Каждый актор имеет почтовый ящик (Addr<A>), обрабатывает сообщения последовательно и никогда не разделяет состояние напрямую.
Ключевые типажи: Actor (жизненный цикл: started, stopping, stopped), Handler<M> (обработка конкретного типа сообщения), Message (тип с ассоциированным Result).
Отличие от стандартного async/await
В стандартном Tokio-коде вы создаёте async fn, которая выполняется на пуле потоков и может владеть любыми данными. Состояние либо клонируется, либо защищается Arc<Mutex<T>>. В актор-модели состояние принадлежит только актору — никакого Mutex не нужно, гонки данных исключены на уровне архитектуры.
- Async/await: явный контроль
await-точек, состояние — в переменных илиArc - Акторы: состояние инкапсулировано в структуре актора, доступ только через
send()/do_send() - Акторы удобны для долгоживущих объектов с изменяемым состоянием (WebSocket-сессии, кэши, очереди)
Пример актора-счётчика
use actix::prelude::*;
struct Counter {
count: usize,
}
impl Actor for Counter {
type Context = Context<Self>;
}
// Сообщение — инкремент
struct Increment;
impl Message for Increment {
type Result = usize;
}
impl Handler<Increment> for Counter {
type Result = usize;
fn handle(&mut self, _: Increment, _: &mut Context<Self>) -> usize {
self.count += 1;
self.count
}
}
#[actix::main]
async fn main() {
let addr = Counter { count: 0 }.start();
let result = addr.send(Increment).await.unwrap();
println!("Count: {}", result); // Count: 1
}
Метод send() возвращает Future с результатом. do_send() — fire-and-forget без ожидания ответа.
Actix-web и акторы
В современном actix-web 4 обработчики маршрутов — это обычные async fn, а не акторы. Actix-сам по себе использует акторы внутренне (например, для WebSocket), но строить бизнес-логику через акторы — выбор разработчика. Для WebSocket-хендлеров реализуется StreamHandler<Result<ws::Message, ws::ProtocolError>>.
Подводные камни
- Блокирующий код внутри
handle()заблокирует весь актор — используйтеctx.spawn()или выносите тяжёлые операции вactix_rt::task::spawn_blocking do_send()молча теряет сообщение, если почтовый ящик переполнен — всегда мониторьте backpressure- Акторы работают в одном потоке своего
Arbiter; для параллельной обработки создавайте пул акторов черезSyncArbiter::start(n, ...) - Циклические ссылки через
Addrне освобождаются автоматически — актор не остановится, пока жив хотя бы одинAddr - Тестирование акторов требует
#[actix::test]-рантайма, обычный#[tokio::test]не подойдёт - Версии
actixиactix-webдолжны совпадать — несовместимость вызывает ошибки компиляции с неочевидными сообщениями - Состояние актора не персистентно — перезапуск системы уничтожает все данные в памяти
Common mistakes
- Отвечать определением без production-сценария.
- Не называть runtime boundary, security boundary или failure mode.
- Игнорировать версию API, observability и тестовую проверку.
What the interviewer is testing
- Объясняет механизм своими словами и без выдуманных API.
- Называет реальные риски, диагностику и критерий корректности.
- Связывает ответ с текущей документацией и миграционными ограничениями.