Эта страница переведена с английского языковой моделью. При любом расхождении верен английский оригинал.
Начало работы
Это руководство устанавливает ruststream-sqs-sns, запускает локальный SQS и первый сервис на нём.
Установка
[dependencies]
ruststream = { version = "0.7", features = ["macros", "json", "logging"] }
ruststream-sqs-sns = "0.7"
serde = { version = "1", features = ["derive"] }
tracing = "0.1"
[dev-dependencies]
ruststream-sqs-sns = { version = "0.7", features = ["testing"] }
tokio = { version = "1", features = ["macros", "rt"] }
ruststream - это фреймворк: macros даёт атрибуты, json - кодек по умолчанию, а logging выводит журнал сервиса в терминал.
ruststream-sqs-sns - брокер SQS, построенный на официальном клиенте aws-sdk-sqs.
Его фича testing добавляет SQS внутри процесса, на котором работают тесты.
У крейта брокера есть такие фичи, все выключены по умолчанию:
| Фича | Что даёт |
|---|---|
sns | публикацию в темы SNS через SnsPublish поверх aws-sdk-sns (Доставка всем подписчикам разом через SNS) |
asyncapi | привязки sqs и sns в генерируемом документе AsyncAPI |
testing | SQS внутри процесса для TestApp |
Локальный SQS
LocalStack запускает SQS и SNS в одном контейнере:
services:
localstack:
image: localstack/localstack:4.14.0
ports:
- "4566:4566"
environment:
SERVICES: sqs,sns
SQS_ENDPOINT_STRATEGY: path
docker compose up -d
Стенд отвечает на http://localhost:4566 и стартует без очередей.
Первый сервис
- Макросы
- Вручную
#[derive(Debug, PartialEq, Deserialize)]
struct Order {
id: u64,
amount_cents: u64,
}
// The local stack starts empty, so the descriptor creates the queue on subscribe.
#[subscriber(SqsQueue::new("orders").create_if_missing())]
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(
SqsBroker::new()
.endpoint("http://localhost:4566")
.region("us-east-1")
.test_credentials(),
|b| {
b.include(take_order);
},
)
}
#[derive(Debug, PartialEq, Deserialize, JsonSchema)]
#[schemars(crate = "ruststream::schemars")]
struct Order {
id: u64,
amount_cents: u64,
}
struct TakeOrder;
impl Handle<Order> for TakeOrder {
fn handle(
&self,
order: &Order,
_outs: &(),
_ctx: &mut Context<'_>,
) -> impl Future<Output = Result<(), HandlerOutcome>> {
if order.amount_cents == 0 {
tracing::warn!(order.id, "order without an amount dropped");
return ready(Err(HandlerOutcome::drop()));
}
tracing::info!(order.id, order.amount_cents, "order taken");
ready(Ok(()))
}
}
fn app() -> impl App {
RustStream::new(AppInfo::new("orders", "0.1.0")).with_broker(
SqsBroker::new()
.endpoint("http://localhost:4566")
.region("us-east-1")
.test_credentials(),
|b| {
// The local stack starts empty, so the descriptor creates the queue on subscribe.
b.include(
subscriber("orders", TakeOrder)
.map_source(|_| SqsQueue::new("orders").create_if_missing())
.build(),
);
},
)
}
fn main() -> ExitCode {
run_main(app)
}
take_order читает заказы в JSON из очереди orders.
Заказ с суммой обработчик подтверждает, а заказ без суммы отбрасывает.
Дескриптор SqsQueue называет очередь, а create_if_missing создаёт её при открытии подписки.
Сервис в промышленной эксплуатации этот шаг опускает и получает очереди от своей инфраструктуры.
SqsBroker::new только записывает конфигурацию.
endpoint, region и test_credentials направляют его на локальный стенд.
Клиенты SDK собираются при старте сервиса.
#[ruststream::app] превращает app в main, а в ручной форме то же делает run_main.
Запустите сервис:
cargo run -- run
Отправьте заказ из другого терминала через AWS CLI:
export AWS_ENDPOINT_URL=http://localhost:4566 AWS_REGION=us-east-1 \
AWS_ACCESS_KEY_ID=test AWS_SECRET_ACCESS_KEY=test
aws sqs send-message \
--queue-url "$(aws sqs get-queue-url --queue-name orders --output text)" \
--message-body '{"id":1,"amount_cents":1250}'
Сервис пишет в журнал order taken с идентификатором заказа.
Ctrl+C останавливает его.
Первый тест
В файле примера есть и два теста.
Каждый запускает то же app через TestApp::start, публикует заказ и проверяет, как take_order его завершил.
SqsBroker подключается там к SQS внутри процесса, поэтому стенд тестам не нужен:
cargo test
Эти тесты и то, что моделирует SQS внутри процесса, разбирает руководство Тестирование сервиса на SQS.
Переход с aws-sdk-sqs
Сервис на aws-sdk-sqs пишет собственный цикл получения.
Вот куда в RustStream переходит каждая его часть:
| aws-sdk-sqs | RustStream |
|---|---|
aws_config::load_from_env и Client::new | SqsBroker::new или SqsBroker::from_config с собственным SdkConfig (Промышленная эксплуатация) |
get_queue_url | имя очереди в SqsQueue::new, которое разрешается один раз при старте |
цикл receive_message().wait_time_seconds(20) | обработчик #[subscriber] (Чтение очередей) |
десериализация message.body() | тип параметра обработчика, который декодирует кодек |
delete_message после работы | исход обработчика, HandlerOutcome::ack (Подтверждение и гарантии доставки) |
change_message_visibility | HandlerOutcome::retry_after (Переобработка и DLQ) |
RedrivePolicy, заданная в консоли | max_attempts и dead_letter в точке монтирования |
send_message | ответ или параметр Out, отправленный через SqsPublish |
message_group_id и message_deduplication_id | шаги публикации SqsPublishSteps: группа и идентификатор дедупликации (Очереди FIFO) |