[comment]: # aside:5

[comment]: # ({f2a0429f-ebcec779})
# Отправка данных на сервер Zabbix или прокси

[zabbix\_utils](https://github.com/zabbix/python-zabbix-utils/blob/main/README.md) позволяет отправлять значения элементов данных на [элемент данных типа trapper](/manual/config/items/itemtypes/trapper) на сервер Zabbix или прокси (аналогично [Zabbix sender](/manual/concepts/sender)).

Вы можете отправить одно значение, несколько значений или даже на несколько кластеров Zabbix.

Данные можно отправлять в синхронном или асинхронном режиме:

-   В синхронном режиме ваш скрипт Python отправляет значения и ожидает ответа перед продолжением; это подходит для простых, последовательных и предсказуемых операций.
-   В асинхронном режиме скрипт отправляет значения, не ожидая каждого ответа, что позволяет выполнять другие операции параллельно; это эффективнее для медленных запросов или больших пакетов данных.

Примеры на этой странице сосредоточены на синхронном режиме, хотя [асинхронный режим](#asynchronous-mode) использует похожие подходы.
Дополнительные примеры доступны в репозитории GitHub [zabbix_utils](https://github.com/zabbix/python-zabbix-utils/tree/main/examples).

[comment]: # ({/f2a0429f-ebcec779})

[comment]: # ({33a3626c-2b58225a})
#### Импорт

Чтобы использовать zabbix\_utils для отправки значений элементов данных, импортируйте класс `Sender` в свой скрипт:

```python
from zabbix_utils import Sender
```

Для отправки нескольких значений вы также можете импортировать класс `ItemValue`:

```python
from zabbix_utils import Sender, ItemValue
```

[comment]: # ({/33a3626c-2b58225a})

[comment]: # ({6215a382-248fa427})
#### Отправка одного значения

Чтобы отправить значение элемента данных:

1. Создайте экземпляр `Sender`, указав IP-адрес и порт вашего сервера Zabbix или прокси.
2. Вызовите метод `send_value()` у экземпляра `Sender` в следующем формате:

```python
sender_instance.send_value('host', 'item.key', 'value', optional_timestamp, optional_nanoseconds)
```

Например, чтобы отправить `1` в элемент данных trapper `service.status` на узел сети `Linux server`:

```python
sender = Sender(server='127.0.0.1', port=10051)
response = sender.send_value('Linux server', 'service.status', 1)
```

[comment]: # ({/6215a382-248fa427})

[comment]: # ({ba5ab006-8c41d6ba})
##### Использование IP-адреса, отличного от значения по умолчанию

Если сервер, на котором выполняется ваш скрипт, имеет несколько IP-адресов, вы можете указать `source_ip`, который `Sender` будет использовать при отправке значений на сервер Zabbix или прокси:

```python
sender = Sender(
    server='127.0.0.1',
    port=10051,
    source_ip='10.10.7.1'
)
```

[comment]: # ({/ba5ab006-8c41d6ba})

[comment]: # ({bff53bf5-de824c12})
##### Использование тайм-аута

Вы можете задать `timeout` ответа для `Sender`, чтобы контролировать, как долго ваш скрипт должен ждать ответа от сервера Zabbix или прокси, прежде чем прекратить ожидание:

```python
sender = Sender(
    server='127.0.0.1',
    port=10051,
    timeout=30
)
```

[comment]: # ({/bff53bf5-de824c12})

[comment]: # ({04ae72fb-62dd1793})
##### Использование файла конфигурации агента

Вы можете разрешить zabbix\_utils считывать параметры [`Server`](/manual/appendix/config/zabbix_agentd#server) или [`ServerActive`](/manual/appendix/config/zabbix_agentd#serveractive) из локального файла конфигурации Zabbix агента или агента 2.
В таких случаях при создании экземпляра `Sender` не нужно указывать параметры подключения:

```python
sender = Sender(
    use_config=True,
    config_path='/etc/zabbix/zabbix_agent2.conf'
)
```

::: noteimportant
Если `ServerActive` содержит один или несколько кластеров Zabbix с несколькими экземплярами сервера, `Sender` отправляет данные на первый доступный сервер в каждом кластере.
Если `ServerActive` не задан, используется адрес из `Server` с портом по умолчанию (10051).
:::

[comment]: # ({/04ae72fb-62dd1793})

[comment]: # ({f95d47a7-a8b30296})
##### Использование шифрования

`Sender` не включает встроенную поддержку шифрования, но вы можете добавить ее, создав обертку с использованием сторонних библиотек:

```python
def psk_wrapper(sock, tls):
    # ...
    # Реализация обертки TLS PSK для сокета
    # ...

sender = Sender(
    server='127.0.0.1',
    port=10051,
    socket_wrapper=psk_wrapper
)
```

[comment]: # ({/f95d47a7-a8b30296})

[comment]: # ({e0a8b11b-b6a3d94d})
##### Ответ для одного значения

Ответ, возвращаемый сервером или прокси Zabbix, обрабатывается библиотекой и возвращается как объект `TrapperResponse`:

```python
print(response)
# {"processed": 1, "failed": 0, "total": 1, "time": "0.000123", "chunk": 1}

print(response.processed)
# 1

print(response.failed)
# 0

print(response.total)
# 1
```

[comment]: # ({/e0a8b11b-b6a3d94d})

[comment]: # ({d5faab4e-9eeea215})
##### Асинхронный режим

Асинхронный режим позволяет вашему Python-скрипту отправлять значения, не дожидаясь ответа от сервера Zabbix или прокси.
Это может сделать ваш скрипт более эффективным, когда ему нужно отправлять много значений или когда на отправку некоторых значений уходит много времени.

При использовании асинхронного режима есть несколько важных отличий по сравнению с синхронным режимом:

-   Импортируйте модуль Python `asyncio` (сначала необходимо [установить](/devel/python/install) требуемые зависимости).
-   Импортируйте `AsyncSender` вместо `Sender`.
-   Пишите код внутри асинхронной функции `async`.
-   Используйте `await` при вызове метода `send_value()`.

Например, чтобы отправить одно значение с использованием асинхронного режима:

```python
# 1. Импортируйте asyncio для асинхронного режима и AsyncSender из zabbix_utils:
import asyncio
from zabbix_utils import AsyncSender

# 2. Определите основную асинхронную функцию, в которой будут выполняться все операции отправки данных (для них нужно использовать await):
async def main():
    sender = AsyncSender(server='127.0.0.1', port=10051)
    response = await sender.send_value('Linux server', 'service.status', 1)

    # 3. Выведите ответ, возвращенный сервером Zabbix или прокси:
    print(response)

# 4. Запустите асинхронную функцию main() с помощью цикла событий asyncio:
asyncio.run(main())
```

[comment]: # ({/d5faab4e-9eeea215})

[comment]: # ({b845706b-34e0ff6d})
#### Отправка нескольких значений

Чтобы отправить несколько значений:

1. Подготовьте массив объектов `ItemValue`, каждый из которых использует тот же формат, что и метод [`send_value()`](#send-single-value).
2. Создайте экземпляр `Sender`, указав IP-адрес и порт вашего сервера Zabbix или прокси.
3. Вызовите метод `send()` (вместо `send_value()`) для экземпляра `Sender`, указав массив объектов со значениями для отправки.

Например, чтобы отправить пять значений на разные узлы сети:

```python
items = [
    ItemValue('server-de', 'service.status', 'up', 1770887205, 100),
    ItemValue('server-fr', 'service.status', 'up', 1770887205, 100),
    ItemValue('server-uk', 'service.status', 'up', 1770887205, 100),
    ItemValue('server-nl', 'service.status', 'up', 1770887205, 100),
    ItemValue('server-pl', 'service.status', 'up', 1770887205, 100),
]

sender = Sender(server='127.0.0.1', port=10051)
response = sender.send(items)
```

[comment]: # ({/b845706b-34e0ff6d})

[comment]: # ({c0410a6d-5848e96f})
##### Использование пользовательского размера фрагмента

Если вам нужно отправить больше значений, чем элемент данных типа trapper может принять за один запрос, вы можете разделить их на фрагменты.

По умолчанию размер фрагмента составляет 250 значений.
Вы можете изменить его, задав параметр `chunk_size` при создании экземпляра `Sender`.

Например, чтобы отправить пять значений в трех фрагментах (2-2-1), задайте параметр `chunk_size` равным `2`:

```python
items = [
    ItemValue('server-de', 'service.status', 'up'),
    ItemValue('server-fr', 'service.status', 'up'),
    ItemValue('server-uk', 'service.status', 'up'),
    ItemValue('server-nl', 'service.status', 'up'),
    ItemValue('server-pl', 'service.status', 'up'),
]

sender = Sender(server='127.0.0.1', port=10051, chunk_size=2)
response = sender.send(items)
```

[comment]: # ({/c0410a6d-5848e96f})

[comment]: # ({b06724dd-5a0b5783})
##### Отправка значений в несколько кластеров Zabbix

Чтобы отправить значения в несколько кластеров Zabbix:

1. Подготовьте массив кластеров Zabbix. Если у кластера несколько узлов, значение будет отправлено на первый **доступный** узел каждого кластера.
2. Создайте `Sender`, указав ваш массив кластеров Zabbix.
3. Вызовите метод `send_value()` для экземпляра `Sender`, используя тот же формат, что и метод [`send_value()`](#send-single-value).

Например, чтобы отправить значение на первый доступный узел в каждом кластере:

```python
zabbix_clusters = [
    ['zabbix.cluster1.node1', 'zabbix.cluster1.node2:10051'],
    ['zabbix.cluster2.node1:10051', 'zabbix.cluster2.node2', 'zabbix.cluster2.node3']
]

sender = Sender(clusters=zabbix_clusters)
response = sender.send_value('Linux server', 'service.status', 1)
```

[comment]: # ({/b06724dd-5a0b5783})

[comment]: # ({2c3cc8b2-d7964287})
##### Ответ для нескольких значений

По умолчанию `Sender` возвращает агрегированный результат отправки значений по всем узлам сети или кластерам:

```python
print(response)
# {"processed": 2, "failed": 0, "total": 2, "time": "0.000108", "chunk": 2}
```

Если вам нужна более подробная информация, вы можете просмотреть результаты для каждого кластера и каждого фрагмента с помощью атрибута `response.details`:

```python
print(response)
# {"processed": 2, "failed": 0, "total": 2, "time": "0.000108", "chunk": 2}

if response.failed == 0:
    print(f"Значение успешно отправлено за {response.time}")
else:
    print(response.details)
    # {
    #     127.0.0.1:10051: [
    #         {
    #             "processed": 1,
    #             "failed": 0,
    #             "total": 1,
    #             "time": "0.000051",
    #             "chunk": 1
    #         }
    #     ],
    #     zabbix.example.local:10051: [
    #         {
    #             "processed": 1,
    #             "failed": 0,
    #             "total": 1,
    #             "time": "0.000057",
    #             "chunk": 1
    #         }
    #     ]
    # }

    for node, chunks in response.details.items():
        for resp in chunks:
            print(f"Обработано {resp.processed} из {resp.total} на {node.address}:{node.port}")
            # Обработано 1 из 1 на 127.0.0.1:10051
            # Обработано 1 из 1 на zabbix.example.local:10051
```

[comment]: # ({/2c3cc8b2-d7964287})
