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

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

Начало работы

Это руководство устанавливает ruststream-gcp-pubsub, запускает эмулятор Pub/Sub и первый сервис на нём.

Установка​

Cargo.toml
[dependencies]
ruststream = { version = "0.7", features = ["macros", "json", "logging"] }
ruststream-gcp-pubsub = "0.7"
serde = { version = "1", features = ["derive"] }
tracing = "0.1"

[dev-dependencies]
ruststream-gcp-pubsub = { version = "0.7", features = ["testing"] }
tokio = { version = "1", features = ["macros", "rt"] }

ruststream - это сам фреймворк. Фича macros даёт атрибуты, json - кодек по умолчанию, а logging выводит журнал сервиса в терминал. ruststream-gcp-pubsub - брокер Pub/Sub. Его фича testing добавляет внутрипроцессный Pub/Sub, на котором работают тесты.

У крейта брокера две фичи, обе выключены по умолчанию:

ФичаЧто она даёт
testingвнутрипроцессный Pub/Sub, на котором работает TestApp
asyncapiпривязки Pub/Sub в генерируемом документе AsyncAPI

Клиент под ним - google-cloud-pubsub. Он написан на Rust, поэтому сборке не нужны системные библиотеки. Крейту нужен Rust 1.95 или новее.

Локальный эмулятор​

Google поставляет эмулятор Pub/Sub в образе Cloud SDK:

compose.yaml
services:
pubsub:
image: gcr.io/google.com/cloudsdktool/google-cloud-cli:emulators
command: gcloud beta emulators pubsub start --host-port=0.0.0.0:8085 --project=my-project
ports:
- "8085:8085"
docker compose up -d

Эмулятор слушает localhost:8085. Он запускается пустым и принимает любой идентификатор проекта.

Первый сервис​

src/main.rs
#[derive(Debug, PartialEq, Deserialize)]
struct Order {
id: u64,
amount_cents: u64,
}

#[subscriber(GooglePubSub::new("orders-workers").create_with_topic("orders"))]
async fn take_order(order: &Order) -> HandlerOutcome {
if order.amount_cents == 0 {
tracing::warn!(order.id, "order without an amount dropped");
return HandlerOutcome::drop();
}
tracing::info!(order.id, order.amount_cents, "order taken");
HandlerOutcome::ack()
}

#[ruststream::app]
fn app() -> impl App {
RustStream::new(AppInfo::new("orders", "0.1.0")).with_broker(
PubSubBroker::new("my-project").emulator("localhost:8085"),
|b| {
b.include(take_order);
},
)
}

take_order читает заказы в JSON из подписки orders-workers. Заказ с суммой он принимает, а заказ без суммы отбрасывает. Сервис на Pub/Sub всегда читает подписку, а не тему. GooglePubSub описывает подписку, а create_with_topic при запуске создаёт её на теме orders вместе с самой темой. PubSubBroker::new принимает идентификатор проекта и только запоминает его. emulator направляет брокер на локальный эмулятор, потому что клиент на Rust не читает PUBSUB_EMULATOR_HOST. Соединение открывается при запуске сервиса. #[ruststream::app] превращает app в main. В ручной форме то же делает run_main.

Запустите сервис:

cargo run -- run

Опубликуйте заказ из другого терминала через REST API эмулятора:

curl -X POST localhost:8085/v1/projects/my-project/topics/orders:publish \
-H 'content-type: application/json' \
-d "{\"messages\": [{\"data\": \"$(printf '{"id":1,"amount_cents":1250}' | base64)\"}]}"

В REST API Pub/Sub передаёт тело сообщения в base64, поэтому заказ сначала кодируется. Сервис пишет в журнал order taken с номером заказа. Ctrl+C останавливает его.

Первый тест​

В файле примера есть и два теста. Каждый запускает то же app через TestApp::start, публикует заказ и проверяет, как take_order завершил его доставку. Там PubSubBroker подключается к внутрипроцессному Pub/Sub, поэтому тестам не нужен эмулятор:

cargo test

Руководство Тестирование сервиса на Pub/Sub разбирает такие тесты и то, что моделирует внутрипроцессный Pub/Sub.

Переход с google-cloud-pubsub​

Сервис на google-cloud-pubsub сам строит клиенты, читает сообщения в цикле и публикует вручную. Вот куда переходит каждая часть в RustStream:

google-cloud-pubsubRustStream
Subscriber::builder() с with_endpoint и with_credentialsPubSubBroker::new с endpoint, credentials или emulator
subscriber.subscribe(name) и цикл по потоку сообщенийобработчик #[subscriber(..)]
set_max_outstanding_messages, set_max_lease_extension, set_max_leasemax_outstanding, ack_extension, max_lease на дескрипторе (Управление потоком и аренда)
create_topic и create_subscription на административных клиентахcreate_with_topic (Темы и подписки)
serde_json::from_slice над данными сообщениятип параметра обработчика, который декодирует кодек
handler.ack(), handler.nack()исход обработчика: ack, retry, retry_after, drop (Подтверждение и переобработка)
Publisher::publishответ или параметр Out, отправленный через политику Publish (Публикация и ответы)
Message::set_ordering_keyшаг ordering_key (Ключи упорядочивания)