Перейти к основному содержимому
Нормативная версия документации - английская

Эта страница переведена с английского языковой моделью. При любом расхождении верен английский оригинал.

Сервис и его первый подписчик

Сначала магазин - это один подписчик, который принимает каждый оформленный заказ. Небольшое приложение монтирует его на in-memory брокере.

Сервис на RustStream - это приложение: билдер, который перечисляет брокеры сервиса и монтирует на них подписчиков. Подписчик - это async fn, первый параметр которой - сообщение. Макрос #[subscriber] задаёт тему, которую он читает. Рантайм декодирует полезную нагрузку до запуска тела обработчика, по умолчанию из JSON. Значение, которое возвращает тело, завершает доставку: HandlerOutcome::ack сообщает брокеру, что сообщение обработано. In-memory брокер передаёт сообщения внутри процесса, поэтому магазину ничего не нужно устанавливать. Глава 11 заменяет его сетевым брокером, а подписчики остаются прежними.

Заказ​

Заказ - это тип serde. Derive-макрос Outgoing задаёт тему, в которую публикуется заказ: orders.placed. Сам магазин пока не публикует заказы. Это делает тест, а в главе 10 - HTTP-витрина.

/// An order placed in the storefront.
#[derive(Debug, PartialEq, Deserialize, Serialize, Outgoing)]
#[outgoing(name = "orders.placed")]
struct Order {
id: u64,
customer: String,
}

Ручная форма содержит две реализации, которые генерирует derive-макрос: адрес публикации и заголовки заказа (пока их нет).

Первый подписчик​

accept читает тему orders.placed и записывает каждый заказ в журнал через tracing. Ручная форма - это именованный тип с реализацией impl Handle<Order>: Ok(()) подтверждает сообщение, а Err(outcome) завершает доставку этим исходом.

/// Takes every placed order; for now it only logs it.
#[subscriber("orders.placed")]
async fn accept(order: &Order) -> HandlerOutcome {
tracing::info!(order = order.id, customer = %order.customer, "order accepted");
HandlerOutcome::ack()
}

Приложение​

RustStream - это приложение. with_broker регистрирует брокер и монтирует на нём подписчиков через include. Билдер только запоминает переданное: брокер подключается при запуске сервиса. Атрибут #[ruststream::app] превращает билдер в main с командами run и asyncapi gen. В ручной форме этот main написан явно: run_main поверх билдера.

#[ruststream::app]
fn app() -> impl App {
RustStream::new(AppInfo::new("shop", "0.1.0")).with_broker(MemoryBroker::new(), |b| {
b.include(accept);
})
}

Запуск​

cargo run --example guide_first --features testing,macros,memory,json,logging -- run

Фича logging выводит журнал сервиса в терминал. Сервис подключает брокер, создаёт подписку на тему и работает до нажатия Ctrl+C:

INFO ruststream::lifecycle starting service service=shop version=0.1.0 brokers=1 subscribers=1
INFO ruststream::lifecycle broker connected broker="ruststream::memory::MemoryBroker"
INFO ruststream::dispatch subscriber started subscriber=orders.placed input="guide_first::Order"
INFO ruststream::lifecycle service running subscribers=1

Остальные главы превращают этот файл в магазин из examples/guide.