Skip to content

Commit

Permalink
merge main
Browse files Browse the repository at this point in the history
  • Loading branch information
Lancetnik committed Jun 5, 2023
2 parents 071e4db + ffe6421 commit 4d42151
Show file tree
Hide file tree
Showing 50 changed files with 596 additions and 86 deletions.
3 changes: 2 additions & 1 deletion docs/docs/en/contributing/2_contributing-index.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@ Activate the new environment with:
source ./venv/bin/activate
```

Make sure you have the latest pip version on your virtual environment to
Make sure you have the latest pip version on your virtual environment to

```bash
python -m pip install --upgrade pip
```
Expand Down
5 changes: 3 additions & 2 deletions docs/docs/en/getting_started/4_broker/2_routing.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,9 @@ This behavior is similar for all brokers, however, the parameters passed to `@br

To learn more about the behavior of specialized brokers, go to the following sections:

* [RabbitBroker](../../../rabbit/1_routing)
* [NatsBroker](../../../nats/2_routing)
* [RabbitBroker](../../../rabbit/1_routing/#routing-rules)
* [NatsBroker](../../../nats/1_nats-index/#routing-rules)
* [RedisBroker](../../../redis/1_redis-index/#routing-rules)

## Error handling

Expand Down
25 changes: 24 additions & 1 deletion docs/docs/en/nats/1_nats-index.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,26 @@
# NATS

{! docs/en/helpful/in-progress.md !}
## Advantages and disadvantages

**NATS** is an easy-to-use, high-performance message broker written in *Golang*. If your application does not require complex routing logic, should cope with high load, scale and do not require large hardware costs, *NATS* will be an excellent choice for you.

!!! note
More information about *NATS* can be found on the [official website](https://nats.io ){.external-link target="_blank"}

However, *NATS* has disadvantages that you should be aware of:

* Messages are not persistent. If the message is published while your consumer is disconnected, it will be lost.
* There are no complex routing mechanisms.
* There are no mechanisms for confirming receipt and processing of messages from the consumer.

These shortcomings are corrected by using the persistent level - *JetStream*. If you need strict guarantees for the delivery and processing of messages to the small detriment of the speed and resources consumed, you can use *NatsJS*.

## Routing rules

*NATS* does not have the ability to configure complex routing rules. The only entity in *NATS* is `subject`, which can be subscribed to either directly by name or by regular expression pattern.

Both examples are discussed [a little further](../3_examples/1_direct).

In order to support the ability to scale consumers horizontally, *NATS* supports the `queue group` functionality:
a message sent to `subject` will be processed by a random consumer from the `queue group` subscribed to this `subject`.
This approach allows you to increase the processing speed of `subject` by *N* times when starting *N* consumers with one group.
36 changes: 36 additions & 0 deletions docs/docs/en/nats/2_publishing.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# NATS Publishing

To send messages, `NatsBroker` uses the unified `publish` method.

```python
import asyncio
from propan import NatsBroker

async def pub():
async with NatsBroker() as broker:
await broker.publish("Hi!", subject="test")

asyncio.run(pub())
```

## Basic arguments

The `publish` method accepts the following arguments:

* `message`: bytes | str | dict | Sequence[Any] | pydatic.BaseModel - message to send
* `subject`: str - *subject*, where the message will be sent.

## Message Parameters

* `headers`: dict[str, Any] | None = None - headers of the message being sent (used by consumers)

## RPC arguments

Also `publish` supports common arguments for creating [*RPC* queries](../../getting_started/4_broker/5_rpc/#_3):

* `reply_to`: str = "" - which *channel* to send the response to (used for asynchronous RPC requests)
* `callback`: bool = False - whether to expect a response to the message
* `callback_timeout`: float | None = 30.0 - timeout waiting for a response. In the case of `None` - waits indefinitely
* `raise_timeout`: bool = False
* `False` - return None in case of timeout
* `True` - error `asyncio.TimeoutError` in case of timeout
3 changes: 0 additions & 3 deletions docs/docs/en/nats/2_routing.md

This file was deleted.

64 changes: 64 additions & 0 deletions docs/docs/en/nats/3_examples/1_direct.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
# Direct

**Direct** Subject is the basic way to route messages in *NATS*. Its essence is very simple:
`subject` sends messages to all consumers subscribed to it.

## Scaling

If one `subject` is listening by several consumers with the same `queue group`, the message will go to a random consumer each time.

Thus, *NATS* can independently balance the load on queue consumers. You can increase the processing speed of the message flow from the queue by simply launching additional instances of the consumer service. You don't need to make changes to the current infrastructure configuration: *NATS* will take care of how to distribute messages between your services.

## Example

**Direct** Subject is the type used in **Propan** by default: you can simply declare it as follows

```python
@broker.handler("test_subject")
async def handler():
...
```

Full example:

```python linenums="1"
{!> docs_src/nats/direct.py !}
```

### Consumer Announcement

To begin with, we have declared several consumers for two `subjects`: `test-subj-1` and `test-subj-2`:

```python linenums="7" hl_lines="1 5 9"
{!> docs_src/nats/direct.py [ln:7-17]!}
```

!!! note
Note that all consumers are subscribed using the same `queue_group`: within the same service, this does not make sense, since messages will come to these handlers in turn.
Here we emulate the work of several consumers and load balancing between them.

### Message distribution

Now the distribution of messages between these consumers will look like this:

```python
{!> docs_src/nats/direct.py [ln:21]!}
```

The message `1` will be sent to `handler1` or `handler2`, because they are listening to one `subject` within one `queue group`

---

```python
{!> docs_src/nats/direct.py [ln:22]!}
```

Message `2` will be sent similarly to message `1`

---

```python
{!> docs_src/nats/direct.py [ln:23]!}
```

The message `3` will be sent to `handler3`, because he is the only one listening to `test-subj-2`
55 changes: 55 additions & 0 deletions docs/docs/en/nats/3_examples/2_pattern.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
# Pattern

**Pattern** Subject is a powerful *NATS* routing engine. This type of `subject` messages to consumers by the *pattern* specified when they connect to `subject` and a message key.

## Scaling

If one `subject` is listening by several consumers with the same `queue group`, the message will go to a random consumer each time.

Thus, *NATS* can independently balance the load on queue consumers. You can increase the processing speed of the message flow from the queue by simply launching additional instances of the consumer service. You don't need to make changes to the current infrastructure configuration: *NATS* will take care of how to distribute messages between your services.

## Example

```python linenums="1"
{!> docs_src/nats/pattern.py !}
```

### Consumer Announcement

To begin with, we have announced several consumers for two `subjects`: `*.info` and `*.error`:

```python linenums="7" hl_lines="1 5 9"
{!> docs_src/nats/pattern.py [ln:7-17]!}
```

At the same time, in the `subject` of our consumers, we specify the *pattern* that will be processed by these consumers.

!!! note
Note that all consumers are subscribed using the same `queue_group`: within the same service, this does not make sense, since messages will come to these handlers in turn.
Here we emulate the work of several consumers and load balancing between them.

### Message distribution

Now the distribution of messages between these consumers will look like this:

```python
{!> docs_src/nats/pattern.py [ln:21]!}
```

The message `1` will be sent to `handler1` or `handler2`, because they listen to the same `subject` template within the same `queue group`

---

```python
{!> docs_src/nats/pattern.py [ln:22]!}
```

Message `2` will be sent similarly to message `1`

---

```python
{!> docs_src/nats/pattern.py [ln:23]!}
```

The message `3` will be sent to `handler3`, because it is the only one listening to the pattern `*.error*`
3 changes: 0 additions & 3 deletions docs/docs/en/nats/3_publishing.md

This file was deleted.

4 changes: 2 additions & 2 deletions docs/docs/en/redis/1_redis-index.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ to include a new heavy dependency (*Kafka*, *RabbitMQ*, *Nats*, etc.) to your in
*Redis* works fast, does not degrade with a large number of messages, and most importantly - you already have it!

!!! note
More information about the documentation of *Redis Pub/Sub* can be found at [official site](https://redis.io/docs/manual/pubsub/#messages-matching-both-a-pattern-and-a-channel-subscription){.external- link target="_blank"}
More information about *Redis Pub/Sub* can be found at [official website](https://redis.io/docs/manual/pubsub/#messages-matching-both-a-pattern-and-a-channel-subscription){.external- link target="_blank"}

However, *Redis* as a message broker has some important disadvantages:

Expand Down Expand Up @@ -47,4 +47,4 @@ This is necessary for the correct recognition of the *content-type* of the incom
If **Propan** receives a message sent using another library or framework (or just a message in a different format),
the entire body of this message will be perceived as the `data` field of the received message, and the `content-type` will be recognized automatically.

At the same time, **RPC** requests will not work, since there is no `reply_to` field in the incoming message.
At the same time, **RPC** requests will not work, since there is no `reply_to` field in the incoming message.
2 changes: 1 addition & 1 deletion docs/docs/en/redis/3_examples/1_direct.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

## Scaling

If one channel is listened by several consumers, the message will be received by **all** consumers of this channel.
If one channel is listening by several consumers, the message will be received by **all** consumers of this channel.
Thus, horizontal scaling by increasing the number of consumer services is not possible only using *Redis Pub/Sub*.

If you need similar functionality, look for *Redis Streams* or other brokers (for example, *Nats* or *RabbitMQ*).
Expand Down
2 changes: 1 addition & 1 deletion docs/docs/en/redis/3_examples/2_pattern.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# Pattern

**Pattern** Channel is a powerful *Redis* routing engine. This type of `channel` sends messages to consumers by the *pattern*
specified when they connect to the `channel` and the message key.
specified when they connect to the `channel` and a message key.

## Scaling

Expand Down
5 changes: 3 additions & 2 deletions docs/docs/ru/getting_started/4_broker/2_routing.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,9 @@ async def base_handler(body: str):

Чтобы узнать подробнее о поведении специализированных брокеров перейдите в следующие разделы:

* [RabbitBroker](../../../rabbit/1_routing)
* [NatsBroker](../../../nats/2_routing)
* [RabbitBroker](../../../rabbit/1_routing/#routing-rules)
* [NatsBroker](../../../nats/1_nats-index/#routing-rules)
* [RedisBroker](../../../redis/1_redis-index/#routing-rules)

## Обработка ошибок

Expand Down
25 changes: 24 additions & 1 deletion docs/docs/ru/nats/1_nats-index.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,26 @@
# NATS

{! docs/ru/helpful/in-progress.md !}
## Преимущества и недостатки

**NATS** - простой в использовании высокопроизводительный брокер сообщений, написанный на *Golang*. Если ваше приложение не требует сложной логики маршрутизации, должно справляться с высокой нагрузкой, просто масштабироваться и не требовать больших затрат на железо, *NATS* станет для вас отличным выбором.

!!! note
Подробнее с *NATS* вы можете ознакомиться на [официальном сайте](https://nats.io){.external-link target="_blank"}

Однако, у *NATS* есть и недостатки, о которых вам стоит знать:

* Сообщения не персистентны. Если сообщение будет опубликовано пока ваш потребитель отключен - оно будет потеряно.
* Отстутствуют механизмы сложной маршрутизации.
* Отстутствуют механизмы подтверждения получения и обработки сообщений со стороны потребителя.

Однако, эти недостатки исправляются использованием персистентного уровня - *JetStream*. Если вам нужны строгие гарантии доставки и обработки сообщений в небольшой ущерб скорости работы и потребляемым ресурсам, вы можете воспользоваться *NatsJS*.

## Правила маршрутизации

*NATS* не обладает возможностью настраивать сложные правила маршрутизации. Единственной сущностью в *NATS* является `subject`, на который можно подписаться либо напрямую по имени, либо по паттерну регулярного выражения.

Оба примера рассмотрены [чуть далее](../3_examples/1_direct).

С целью поддержки возможностей горизонтально масштабировать потребителей, *NATS* поддерживает функционал `queue group`:
сообщение, отправленное в `subject` будет обработано случайным потребителем из `queue group`, подписанной на этот `subject`.
Такой подход позволяет увеличить скорость обработки `subject` в *N* раз при запуске *N* потребителей с одной группой.
36 changes: 36 additions & 0 deletions docs/docs/ru/nats/2_publishing.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# NATS Publishing

Для отправки сообщений `NatsBroker` использует унифицированный метод `publish`.

```python
import asyncio
from propan import NatsBroker

async def pub():
async with NatsBroker() as broker:
await broker.publish("Hi!", subject="test")

asyncio.run(pub())
```

## Базовые аргументы

Метод `publish` принимает следующие аргументы:

* `message`: bytes | str | dict | Sequence[Any] | pydatic.BaseModel - сообщение для отправки
* `subject`: str - *subject*, куда будет отправлено сообщение.

## Параметры сообщения

* `headers`: dict[str, Any] | None = None - заголовки отправляемого сообщения (используются потребителями)

## RPC аргументы

Также `publish` поддерживает общие аргументы для создания [*RPC* запросов](../../getting_started/4_broker/5_rpc/#_3):

* `reply_to`: str = "" - в какой *channel* отправить ответ (используется для асинхронных RPC запросов)
* `callback`: bool = False - ожидать ли ответ на сообщение
* `callback_timeout`: float | None = 30.0 - таймаут ожидания ответа. В случае `None` - ждет бесконечно
* `raise_timeout`: bool = False
* `False` - возвращать None в случае таймаута
* `True` - ошибка `asyncio.TimeoutError` в случае таймаута
3 changes: 0 additions & 3 deletions docs/docs/ru/nats/2_routing.md

This file was deleted.

64 changes: 64 additions & 0 deletions docs/docs/ru/nats/3_examples/1_direct.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
# Direct

**Direct** Subject - базовый способ маршрутизации сообщений в *NATS*. Его суть очень проста:
`subject` отправляет сообщения всем потребителям, подписанным на него.

## Масштабирование

Если один `subject` слушает несколько потребителей с одинаковой `queue group`, сообщение будет уходить каждый раз случайному потребителю.

Таким образом, *NATS* может самостоятельно балансировать нагрузку на потребителей очереди. Вы можете увеличить скорость обработки потока сообщений из очереди просто запустив дополнительные инстансы сервиса-потребителя. Вам не нужно вносить изменений в текущую конфигурацию инфраструктуры: *NATS* сам позаботится о том, как распределить сообщения между вашими сервисами.

## Пример

**Direct** Subject - тип, используемый в **Propan** по умолчанию: вы можете просто объявить его следующим образом

```python
@broker.handler("test_subject")
async def handler():
...
```

Полный пример:

```python linenums="1"
{!> docs_src/nats/direct.py !}
```

### Объявление потребителей

Для начала мы объявили несколько потребителей для двух `subject`: `test-subj-1` и `test-subj-2`:

```python linenums="7" hl_lines="1 5 9"
{!> docs_src/nats/direct.py [ln:7-17]!}
```

!!! note
Обратите внимание, что все потребители подписаны с использованием одной `queue_group`: в рамках одного сервиса это не имеет смысла, так как сообщения будут приходить в эти обработчики поочередно.
Здесь мы эмулируем работу несколько потребителей и балансировку нагрузки между ними.

### Распределение сообщений

Теперь распределение сообщений между этими потребителями будет выглядеть следующим образом:

```python
{!> docs_src/nats/direct.py [ln:21]!}
```

Сообщение `1` будет отправлено в `handler1` или `handler2`, т.к. они слушают один `subject` в рамках одной `queue group`

---

```python
{!> docs_src/nats/direct.py [ln:22]!}
```

Сообщение `2` будет отправлено аналогично сообщению `1`

---

```python
{!> docs_src/nats/direct.py [ln:23]!}
```

Сообщение `3` будет отправлено в `handler3`, т.к. он единственный слушает `test-subj-2`
Loading

0 comments on commit 4d42151

Please sign in to comment.