ANTICHAT

ANTICHAT (https://forum.antichat.xyz/index.php)
-   Общие вопросы программирования (https://forum.antichat.xyz/forumdisplay.php?f=206)
-   -   Модель акторов и её реализация (https://forum.antichat.xyz/showthread.php?t=765287)

gattsu 07.02.2016 15:20

Долго мучаюсь, но вот уже созреваю до конечной программной концепции, своей реализации модели акторов.

Для общего ознакомления рекомендую почитать статью на википедиии

Модель акторов — Википедия

Для полного ознакомления, рекомендую почитать:

Нюансы моей реализации:

- в системе есть фиксированное количество потоков(worker).

- отсутствует блокирующая синхронизация

- синхронизация с помощью atomic(в класе EventBus)

- распределение вычислений делайте actorsystem(global

Пытаюсь сделать полностью однородную среду, реализовать по принципу "все есть актор". Сейчас переписываю в 100 раз. Пытаюсь замкнуть все контексте и производить обработку пулом, но при этом оставить однородность. Если будут вопросы пишите, пока не могу расписать, что да как, но думаю на конкретные вопросы смогу ответить. Думаю уже близко к концу, удовлетворяющему меня варианту.

actor-model / actors — Bitbucket

ПС пакет actor.test содержит пару примеров реализации.

Да, про другие реализации модели акторов, автор знает и пробовал на них делать программы.

Немного абстрактного кода для примера из ла2

Цитата:

Сообщение от Спойлер


Код:


Код:

class Account extends BaseActor {
    public static final Logout logout = new Logout();
 
    static final LoginSuccess success = new LoginSuccess();
 
    final String login;
 
    String password;
 
    {
        behavior(State.FREE)
            .Case(
                auth -> {
                    if(password.equals(auth.password)) {
                        reply(success);
                        become(State.AUTH);
                    } else
                        reply(LoginFail.WRONG_PASSWORD);
                },
                Auth.class
            );
         
        behavior(State.AUTH)
            .Case(
                auth -> reply(LoginFail.IN_USE),
                Auth.class
            )
            .Case(
                logout -> become(State.FREE),
                Logout.class
            );
        ...
    }
 
    enum State {
        FREE,
        AUTH,
        BLOCK,
        ...
    }
 
 
    public static class Auth {
        final String password;
     
    }
 
    public static class LoginSuccess {
 
    }
 
    public static class Logout {
 
    }
 
    public static enum LoginFail {
        IN_USE,
        WRONG_PASSWORD,
        ....
    }
}



gattsu 13.02.2016 17:48

Обновления:

Создал новую ветку.

Пробую реализовать, другим способом:

- уменьшилось количество этапов передачи сообщения(Сообщение передается на прямую без посредником, посредник только при отправке сообщения через не связанную ссылку)

- удалось увеличить количество обрабатывающих сообщений на 70-80%. (для разного рода действий, следует организовывать разную балансировку между акторами, можно попробовать реализовать рекомендацию, для балансировщика, при создании актора, по принципу привязки к одному воркеру и тп)

- Атомарная синхронизация в каждом mailbox

- через класс Balancer, происходит передача, Worker-ам, на исполнение событий.

ПС В один момент времени класс актор, может обрабатывать только одно входящее сообщение

gattsu 16.02.2016 03:38

Расширил функционал очереди. Добавил различные функции, которые учитывают указанную задержку, для обработки.

Пробую алгоритм рекурсивных сообщений, в различных случаях позволяет добиться 15-20 миллионов сообщений в секунда, на 3000 акторов.(тест на счетчиках)

Система с единичным акторов позволяет достичь 5 миллионов сообщений(единичный счетчик)

Тест конечно частного случая, но позволяют следить за пропускной способность, задержкой. Если добавить нагрузку в обработку сообщений, можно получить меньший результат.

Уменьшил количество мест синхронизаций, до двух, на одну обработку события.

Потоки могут войти в монитор, при обращении к одному и тому же актору(публикация события)

Потоки могут войти в монитор, если при балансировке, указал один и тот же поток порождения задачи, для двух других.

Прикрепил общую схему работы воркеров(потоков)

gattsu 22.02.2016 21:43

Продолжим.

Реализовал систему с помощью обычной синхронизации, не блокирующие очереди оставил на потом. Реализовано все топорно, чтобы работал фундамент.

На данный момент можно:

- создавать актор

- отправлять сообщение актору

- делать запрос с ожиданием ответа к актору.

Основные принципы акторов.

Каждый актор в один момент времени, может обрабатывать только одно входящее сообщение.

Во время обработки сообщения актор может:

- менять свое поведения, для реакции на следующее сообщение.

- посылать сообщения другим акторам, включая себя(рекурсивное сообщение)

- создавать других акторов

gattsu 22.02.2016 23:15

"Один актор не является актором. Актор является частью системы акторов"

Рассмотри простой пример двух акторов.

У нас, есть два актора Ping и Pong. Акторы, с задержкой в одну секунду, пересылаю друг другу сообщения. Актор Ping отправляет строку "ping" актору Pong. Получив строку, Pong отображает её и отправляет строку "pong". Получив сообщение актор отображает строку и отправляет сообщение "ping". Этот процесс происходит до выхода из программы, пользователем.

Точка входа в модель

Код:


Код:

public static void main(String...args) {
        ActorSystem.launch(system -> {
          system.create(Ping.class, "ping", system.create(Pong.class, "pong"));
        });
}

В данному участке кода, запуск производится через статический метод ActorSystem.launch. Методом лябда выражения, в языке java, мы создает Consumer и передаем его методу launch. system - корневой актор.

Производим создание актора Pong

Код:


Код:

system.create(Pong.class, "pong")
Данный метод create, c двумя аргументами, принимает два значения.

Первый аргумент типа Сlass, передается класс который наследует BaseActor, в нашем случае Pong.class

Второй аргумент типа String - уникальное имя актора в узле. "pong"

Производим создание актора Ping

Код:


Код:

system.create(Ping.class, "ping", system.create(Pong.class, "pong"))
В данном случае вызывается перегруженный метод create(Class clazz, String name, Object...args);

Первые два аргумента являются идентичными, а в третий передается массив аргументов(синтаксический сахар vargs, Object...arss эквивалентно записи Object[] args). Система произведет поиск соответствующего конструктора, с заданными аргументами. В нашем случае будет вызван конструктор, для класса Ping

Код:


Код:

public Ping(Actor actor) {
    pong = actor;
}

так как метод create, возвращает ссылку на запись актора, которая наследует базовый абстрактный класс Actor. Таким образом мы скрываем реализацию, и защищаем классы от внешних изменений.

Другой вариант записи

Код:


Код:

public static void main(String...args) {
        ActorSystem.launch(new Consumer() {
            @Override
            public void accept(Actor system) {
                Actor pong = system.create(Pong.class, "pong");
                Actor ping = system.create(Ping.class, "ping", pong);
            }
        });
    }

Каждый актор обладает заданным поведение. Чтобы назначить поведение актору, мы сначала его создаем через метод behavior() анонимное, или поведение которое можно назначить по ключу behavior(Object key). Данные два метода возвращают класс который наследует интерфейс Actor.IBehavior. Создав поведение, через метод behavior, который вернул класс поведения, мы можем назначить ему случай поведения на указанный тип, через мето Case(Consumer consumer, Class type);

consumer - функция поведения для указанного типа, она вызывается в случае, когда актор получает соответсвующий тип сообщения

type - класс типа

T - тип значения

Код:


Код:

IBehavior behavior = behavior();
behavior.Case(value -> {
...произвести действия
}, Integer.class);
become(behavior);

Мы задали поведение, условному актору, который реагирует на входящее сообщения, числового типа, и производит какие-то вычисления.

В нашем примере поведение двух акторов выглядим следующим образом

Дла актора Pong

Код:


Код:

public static class Pong extends BaseActor {
        {
            become(
                behavior().
                    Case(
                        value -> {
                            System.out.println(value);
                            sender().send("pong", 1000);
                        },
                        String.class
                    )
            );
        }
    }

Метод sender() Возращает актора, который отправил полученное сообщение. Метод send отправляет сообщение "pong" с задержкой в 1 секунду.

То есть, получении сообщения типа String, мы отображаем его в консоли, затем отправляем сообщению "ping", отправителю, с задержкой в одну секунду.

Для актора Ping

Код:


Код:

public static class Ping extends BaseActor {
        Actor pong;
     
        public Ping(Actor actor) {
            pong = actor;
        }
     
        {
            become(
                behavior().
                    Define(
                        d -> pong.send("ping") 
                    ).
                    Case(
                        value -> {
                            System.out.println(value);
                            pong.send("ping", 1000);
                        },
                        String.class
                    )
            );
        }
    }

В данном примере присутствует шаблонный случай Define. Его можно заменить на

Код:


Код:

behavior().
Case(
define -> {
pong.send("ping")
},
Actor.Define.class
);

Actor.Define системный класс-сообщение, которое отправляется, при создании каждому актору. Данное сообщение не обязательно для обработки.

Данное сообщение актор получает, обрабатывает, в первую очередь, затем все осталньные входящие, если на момент создания такие есть.

В шаблонном поведении Define, мы отправляем сообщение актору pong, который передали при создании, pong.send("ping"). Это является источником вычислений в данной модели. То есть при создании актора ping мы отправляем сообщение актору pong, на что, в случае актора pong, он получает и обрабатывает сообщение, отобразив его на консоли и ответив сообщением "pong".

Результат выполнения данной программы будет, бесконечное, поочередное отображения, на консоли, строк "ping" и "pong"

Цитата:

Сообщение от Спойлер

ping

pong

ping

pong

ping

pong

ping

pong

ping

pong

ping

pong

ping

pong

...

У меня отсутствует понятие планировки, вся планировка организовывается путем задержки сообщений.

ПС На данный момент, в этой имплементации, не реализована отправка сообщения по адресу.

gattsu 23.02.2016 00:44

Расширил управление поведением. Добавил:

- возможность предиката (actor.samples.EvenAndOdd)

- возможность задавать поведения, для определенных значения(actor.samples.ValuesCase)

Добавил подержу синглтон акторов.

Синглтон актор - актор, объект которого может находиться в одном экземпляре, в системе.

Singletonрасширяется BaseActorкласс.

Объявление синглтон актора, делается путем наследования класса Singleton. Любой актор расширяемый от класс Singleton, сможет быть создан, в одном экземпляре, в системе, через метод create.

Обращение, к синглтон актору, можно сделать путем вызова метода actor(T.class), определенного в классе BaseActor. T класс должен быть расширяем actor.Singleton. Вызов данного метода возвращает ссылку на заданный актор.

Примечание: actor(T.class) может вернуть ссылку на не существующий синглтон.

Пример реализации PingPong через singleton

gattsu 25.02.2016 13:53

Обновление.

- Добавил актор-консоль. Через трейт actor.util.Consol.TConsole, добавляется метод, через который можно отправлять строку на консоль(println(Object)).

- Добавил систему остановки акторов. При вызове метода stop, для актора, генерируется событие Event.Stop. При обработке его, актор получает сообщение Actor.Stop, которое можно обработать, производя действия с останавливающим акторов. При остановке, актор удаляется из системы.

- Теперь ссылку на корневой актор можно получить через метод system, определенный в классе ActorBase.

- Для корневого актора добавлена возможность отображения дерева акторов. Через строковое сообщение "dump-tree". Пример можно посмотреть (src/test/java/actor.test.DumpTreeTest)

- Добавил поиск актора по адресу.

- Добавил возможность общения запрос-ответ.

velafrys 25.02.2016 13:58

behavior -> case - напомнило Pattern matching из скалы)

gattsu 25.02.2016 14:08

Ну она где-то так и есть. D java нету механизма pattern matching. Но в данном случае получается reactive парадигма. Программируемое поведение на лету.

В scala бы этот участок был бы грациозней хД

kick 11.01.2017 15:10

Всё? Конец?


Время: 12:38