Проект для Лицея Академии Яндекс
В случае ошибки или при любых других вопросах пишите сюды @moolcoov
- Сохранение в базу данных
- Поддержка разных пользователей
- Общение агента и оркестратора с помощью gRPC
Для начала на вашем компьютере должен быть установлен Docker. Как его установить написано в документации.
Далее необходимо клонировать репозиторий с кодом
git clone https://github.com/moolcoov/expressionist.git
После этого нужно создать билд с помощью Docker Compose
docker-compose build
P.S Этот процесс может занять довольно много времени ☕
После этого можно запускать проект
Для того чтобы запустить все сервисы:
docker-compose up -d
Для того чтобы запустить еще агентов:
docker-compose scale agent=3
Вместо 3 можно подставить любое число, сколько агентов вы хотите.
После этого можно открыть localhost:3000 для доступа к веб-версии или получить прямой доступ к оркестратору через localhost:8080
Откройте сайт и зарегистрируйтесь.
Далее, на главной странице можно будет создать выражение. Пример: 1024 / 2 + 4 * (2 + 3)
. Спустя некоторое время выражение будет подсчитано, и ответ отобразится автоматически.
Чтобы посмотреть агентов, перейдите на страницу "Агенты" (/agents
).
Чтобы изменить настройки, перейдите на страницу "Настройки" (/settings
).
Чтобы войти в другой аккаунт или зарегистрировать новый, нажмите на кнопку выйти в правом верхнем углу.
Оркестратор находится в директории /orchestra
. Написан на Go.
Для роутинга изпользуется gorilla/mux. Все эндпоинты в субдиректории /routes
.
База данных, в которую сохраняются выражения: PostgreSQL. Для доступа используется sqlx с драйвером pgx. Файл, в котором происходит подключение к бд /lib/postgres.go
.
Для кэширования результатов используется key-value база данных Redis. Для подключения применяется пакет go-redis
. Файл, в котором происходит подключение /lib/redis.go
Для передачи выражений агентам используется RabbitMQ. Для подключения применяется amqp091-go
. Файл /lib/redis.go
.
Активность агентов проверяется в отдельной горутине с бесконечным циклом, которая каждые 10 секунд проверяет, не истекло ли время пинга у агентов.
go func() {
for {
agent.Agents.CheckAgents()
time.Sleep(10 * time.Second)
}
}()
Схема оркестратора выглядит так:
Агент находится в директории /agent
. Написан на Go.
Агент получает выражения с помощью того же RabbitMQ. Схема подключения такая же, как и у оркестратора. /lib/redis.go
.
Для вычисления используется измененная версия библиотеки go-shunting-yard.
Схема:
Фронтенд написан на Typescript, с использованием фреймворка Next.js. Находится в директории /client
.
На серверной части запросы делаются с помощью официального API (добавление нового выражения, обновление настроек).
const res = await fetch(... ,{})
На клиентской части используется библиотека SWR (получение списка выражений, агентов, настроек).
const { data, error, isLoading } = useSWR(..., fetcher)
Схема:
Все настройки для PostgreSQL, Redis, RabbitMQ и т.д. находятся в файле .env
При запуске оркестратор подключается к PostgreSQL (создает таблицу, если ее нет), Redis, открывает канал RabbitMQ. Также он объявляет настройки, которые получает из Redis под ключом _settings
. (Если в редисе значений нет, он их создает).
После этого оркестратор объявляет эндпоинты, запускает проверку агентов и начинает обрабатывать запросы.
При запуске агент генерирует uuid, регистрирует его у оркестратора, получает настройки, подключает RabbitMQ.
Далее запускает n
горутин, с бесконечным циклом.
n
определяется в переменной среды AGENT_GOROUTINES
в файле .env
.
В горутинах начинается принятие и обработка выражений. Также запускается горутина, которая пингует оркестратор и обновляет настройки, с задержкой в 30
секунд
При запуске начинает обрабатывать запросы пользователя.
После перехода на localhost:3000
открывается главная страница с полем для ввода и выражениями.
Когда фронтенд запрашивает выражения у оркестратора (/expressions
), бэкенд идет в postgres и берет их оттуда.
После ввода выражения в поле ввода серверная часть фронтенда отправляет POST
запрос на /new
с полями выражения и даты отправки в теле запроса.
Оркестратор записывает данные в структуру, парсит выражение, проверяет нет ли в redis закэшированного значения.
Если оно есть, оркестратор сразу возвращает значение.
После в отдельной горутине (для быстроты ответа клиенту) если выражение не посчитанно, отправляется агентам через очередь RabbitMQ.
В заключение выражение добавляется в PostgreSQL.
Тем временем агент получает выражение, обновляет его статус на "в прогрессе" и начинает вычислять.
Сначала проверяется валидность выражения, если оно невалидно статус переводится на "errored"
и обновляется у оркестратора (/submit
)
Если оно валидно, выражение переводится в обратную польскую нотацию и вычисляется.
После этого, подсчитывается количество операций, считается время ожидания из полученных настроек, а потом
time.Sleep()
Я знаю, что можно было эффективнее сделать, разбивать выражения на подвыражения, считать их по разному, но времени уже не было 💥💥💥
В конце выражение с результатом отправляется на /submit
Там оно кэшируется в Redis
На ней фронт запрашивает список агентов (/agents
). Агент может быть активным и нет. После регистрации каждые 30 секунд агент должен присылать запрос со своим id
на /ping
.
Если он этого не сделал в течении 40+-10 секунд, агент становится неактивным. Из базы данных получаются все выражения, которые считаются в данный момент и прикреплены к этому агенту. Выражения снова отправляются другим агентам через RabbitMQ. Там оно считается другими агентами.
Если агент неактивен в течении определённого времени, указанного в настройках, он исчезает.
Все это проверяется в горутине с бесконечным циклом с задержкой 10 секунд.
На ней фронт запрашивает настройки (/settings
).
При изменении настроек серверная часть отправляет новые значения на /settings/update
. Агент получит новые настройки в течении 30 секунд.
! Если настройки не изменились с первого раза, попробуйте обновить их еще раз.
Полная схема проекта выглядит так:
Я знаю, что можно было сделать намного лучше:
- Разделять выражения
- Оптимизировать код
- Улучшить структуру
Но я считаю, что я устал, и это все, что нужно.
Ну в общем все, если что пишите. Не судите строго, всем бобра.
by moolcoov 21/04/2024