# PEACEPIE

Простая система акторов. Пакет `peacepie` представляет собой систему акторов, осуществляющих взаимодействие между собой посредством сообщений.
Указанной архитектуре присущи следующие положительные черты:
- масштабируемость
- отказоустойчивость
- реактивность

Основная отличительная черта — изначально поддерживаемая динамическая загрузка акторов из 
внешних пакетов, что позволяет распространять акторы между процессами и хостами, 
не перезапуская систему, строить и конфигурировать систему из набора готовых блоков, 
которые могут храниться как в общедоступных, так и в приватных репозиториях готовых решений.
Система представляет собой, в общем случае, множество процессов (из модуля `multiprocessing`) 
обмен между которыми происходит через TCP каналы. Внутри процесса акторы обрабатывают сообщения 
в отдельных корутинах, обмениваясь сообщениями через "asyncio.Queue".

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

## Установка

Установить пакет `peacepie` можно с помощью менеджера пакетов [pip](https://pip.pypa.io/en/stable/),
выполнив следующую команду:

```bash
 pip install peacepie
```

## Ознакомление

Создаем проект с любым именем. С помощью команды, приведенной выше, устанавливаем пакет `peacepie`
в проект. Создаем скрипт, приведенный ниже, и запускаем его первый раз без параметров. При запуске скрипта
без параметров `peacepie` формирует окружение по умолчанию. При повторных запусках скрипт лучше запускать
с параметром `"./config/peacepie.cfg"`. При первоначальной инициализации происходит скачивание около
десятка библиотек из удаленных репозиториев, что занимает некоторое время.

```python
import asyncio
import multiprocessing
import sys

from peacepie import PeaceSystem


multiprocessing.set_start_method('spawn', force=True)


async def main():
    param = sys.argv[1] if len(sys.argv) > 1 else None
    pp = PeaceSystem(param)
    await pp.start()
    try:
        await pp.task
    except asyncio.CancelledError:
        pass


if __name__ == '__main__':
    asyncio.run(main())
```

В случае удачного запуска приложения по адресу http://localhost:9090 станет доступным простейший
веб-интерфейс. В случае если порт занят, в скрипте `./config/app_starter_example.py` измените
порт на любой доступный и перезапустите скрипт.

В веб-интерфейсе присутствует пять уровней: главный администратор (HEAD), список администраторов хостов (PRIME),
список администраторов процессов (PROCESS), список акторов (ACTORS), актор (ACTOR). Главный администратор присутствует
на всех уровнях, администраторы хостов и администраторы процессов - на всех нижележащих уровнях. Это связано с тем,
что указанные акторы выполняют в системе сразу несколько ролей. Отправлять команды в систему можно только с уровня актора.
Все страницы акторов для отправки сообщений равнозначны, единственное различие - поле "Получатель" заполняется именем
текущего актора, но его можно изменить.

Из двух кнопок "ASK" и "SEND" лучше выбирать "ASK", поскольку режим "ASK" подразумевает
содержательный ответ от исполнителя. Исключение, приходящее на ум,- команда "recreate_actor" 
в отношении актора типа "SimpleWebFace", который обеспечивает веб-интерфейс. 

Ознакомление начнем со стандартного "Hello, World!". Выполняем команду:

| Поле       | Значение                                                                                            |
|------------|-----------------------------------------------------------------------------------------------------|
| Комманда   | create_actor                                                                                        |
| Тело       | {"class_desc": {"requires_dist": "peacepie_example", "class": "HelloWorld"}, "name": "hello_world"} |
| Получатель | first.main.admin                                                                                    |

В результате создастся новый актор типа HelloWorld из пакета `peacepie_example`, имя можно 
задать любое, пакет, если пакет еще не кеширован на локальной машине, будет скачан из удаленного репозитория. 
Если отправить команду "tick" в адрес созданного актора, используя его имя, то актор будет выводить в консоль
сообщение "Hello, World!" и возвращать ответ с командой "hello_world"

| Поле       | Значение    |
|------------|-------------|
| Команда    | tick        |
| Тело       |             |
| Получатель | hello_world |

Удалить вновь созданный актор можно командой

| Поле       | Значение                |
|------------|-------------------------|
| Команда    | remove_actor            |
| Тело       | {"name": "hello_world"} |
| Получатель | first.main.admin        |

Следующий актор похож на предыдущий, но выводит сообщение "Hello, World!" в консоль с заданной периодичностью
после получения команды "tick_start". Кроме периодичности можно еще задать лимит сообщений. При заданном лимите
актор выведет ограниченное число сообщений.


| Поле       | Значение                                                                                                               |
|------------|------------------------------------------------------------------------------------------------------------------------|
| Команда    | create_actor                                                                                                           |
| Тело       | {"class_desc": {"requires_dist": "peacepie_example", "class": "IteratingHelloWorld"}, "name": "iterating_hello_world"} |
| Получатель | first.main.admin                                                                                                       |


| Поле       | Значение                                                                         |
|------------|----------------------------------------------------------------------------------|
| Команда    | set_params                                                                       |
| Тело       | {"params": \[{"name": "period", "value": 2}, {"name": "limit", "value": null}\]} |
| Получатель | iterating_hello_world                                                            |

| Поле       | Значение              |
|------------|-----------------------|
| Команда    | tick_start            |
| Тело       |                       |
| Получатель | iterating_hello_world |

Создаем новый процесс:

| Поле        | Значение         |
|-------------|------------------|
| Команда     | create_process   |
| Тело        |                  |
| Получатель  | first.main.admin |

И перемещаем в него наш объект:

| Поле        | Значение                                                             |
|-------------|----------------------------------------------------------------------|
| Команда     | recreate_actor                                                       |
| Тело        | {"node": "first.process_0.admin", "entity": "iterating_hello_world"} |
| Получатель  | first.main.admin                                                     |

Остановить вывод можно с помощью команды "tick_stop"

| Поле       | Значение              |
|------------|-----------------------|
| Команда    | tick_stop             |
| Тело       |                       |
| Получатель | iterating_hello_world |

Удаляем актор командой

| Поле       | Значение                          |
|------------|-----------------------------------|
| Команда    | remove_actor                      |
| Тело       | {"name": "iterating_hello_world"} |
| Получатель | first.process_0.admin             |

Командой "quit" завершаем выполнение примера. Получатель можем быть любым, поскольку команда "quit"
всегда передается главному администратору.

| Поле       | Значение         |
|------------|------------------|
| Комманда   | quit             |
| Тело       |                  |
| Получатель | first.main.admin |


Следующий пример более интересный. Создаем актор типа CircleStarter

| Поле        | Значение                                                                                                  |
|-------------|-----------------------------------------------------------------------------------------------------------|
| Команда     | create_actor                                                                                              |
| Тело        | {"class_desc": {"requires_dist": "peacepie_example", "class": "CircleStarter"}, "name": "circle_starter"} |
| Получатель  | first.main.admin                                                                                          |

Устанавливаем параметры созданного актора

| Поле       | Значение                                                                                                                           |
|------------|------------------------------------------------------------------------------------------------------------------------------------|
| Команда    | set_params                                                                                                                         |
| Тело       | {"params": \[{"name": "process_count", "value": 3}, {"name": "dancers_per_process", "value": 2}, {"name": "period", "value": 4}\]} |
| Получатель | circle_starter                                                                                                                     |

Далее запускаем процесс
 
| Поле        | Значение                           |
|-------------|------------------------------------|
| Команда     | begin                              |
| Тело        |                                    |
| Получатель  | circle_starter                     |

Стартер "circle_starter" создаст 3 процесса и в каждом по 2 актора типа CircleDancer, каждому актору
CircleDancer присваивается ссылка на следующий актор, последний актор ссылается на первый. Поведение
акторов CircleDancer очень простое - при получении сообщения с командой "tick" актор через период времени
заданный в параметре "period" передает сообщение следующему актору. В итоге получается, что сообщение
циркулирует по кругу. Сообщение передается как внутри процесса, так и между процессами.
Следующая серия команд создает еще один процесс с актором типа CircleDancer внутри и встраивает вновь
созданный актор в круг акторов, созданных ранее.

| Поле        | Значение         |
|-------------|------------------|
| Команда     | create_process   |
| Тело        |                  |
| Получатель  | first.main.admin |

| Поле        | Значение                                                                                           |
|-------------|----------------------------------------------------------------------------------------------------|
| Команда     | create_actor                                                                                       |
| Тело        | {"class_desc": {"requires_dist": "peacepie_example", "class": "CircleDancer"}, "name": "dancer_x"} |
| Получатель  | first.process_2.admin                                                                              |

| Поле        | Значение                                                                                    |
|-------------|---------------------------------------------------------------------------------------------|
| Команда     | set_params                                                                                  |
| Тело        | {"params": \[{"name": "consumer", "value": "dancer_00"}, {"name": "period", "value": 10}\]} |
| Получатель  | dancer_x                                                                                    |

| Поле        | Значение                                                  |
|-------------|-----------------------------------------------------------|
| Команда     | set_params                                                |
| Тело        | {"params": \[{"name": "consumer", "value": "dancer_x"}\]} |
| Получатель  | dancer_05                                                 |

Командой "quit" завершаем выполнение примера

| Поле       | Значение         |
|------------|------------------|
| Комманда   | quit             |
| Тело       |                  |
| Получатель | first.main.admin |



## Репозитарий

https://github.com/vnmol/peacepie
