[comment]: # aside:5

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

[zabbix\_utils](https://github.com/zabbix/python-zabbix-utils/blob/main/README.md) позволяет отправлять значения элементов данных в [trapper item](/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

Вы можете задать `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` (сначала необходимо [install](/devel/python/install) требуемые зависимости).
-   Импортируйте `AsyncSender` вместо `Sender`.
-   Пишите код внутри функции `async`.
-   Используйте `await` при вызове метода `send_value()`.

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

```python
# 1. Import asyncio for asynchronous mode, and AsyncSender from zabbix_utils:
import asyncio
from zabbix_utils import AsyncSender

# 2. Define the main async function where all data sending operations (must await) will be executed:
async def main():
    sender = AsyncSender(server='127.0.0.1', port=10051)
    response = await sender.send_value('Linux server', 'service.status', 1)

    # 3. Print the response returned by Zabbix server or proxy:
    print(response)

# 4. Run the async main() function using asyncio's event loop:
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})
