Начал изучать новый для себя язык программирования - Rust. Его часто используют для системного программирования и высокопроизводительных приложений.
Параллельно с учёбой пилю пет-проект на GitHub, в котором есть интеграция с OpenAI (ChatGPT) и TTS-сервисом. Планирую прикрутить к нему ещё какой-нибудь AI для генерации картинок (вроде dreamstudio.ai).
Ещё пришлось написать небольшой скрипт на Python с FastAPI и Silero AI для TTS-запросов. Бот умеет отвечать голосом, зачитывая ответы ChatGPT. Ради этого пришлось поднять отдельный сервис.
Этот пет-проект уже оказался полезным в повседневной работе. С его помощью я сделал нормальную страницу профиля на GitHub, и он без конца отвечал на мои вопросы по изучению Rust.
Кстати, превью для этого поста я тоже сгенерировал на dreamstudio.ai.
Этот пет-проект уже хорошо себя показал. Помогает мне в повседневной работе, помог сделать нормальную страницу профиля на Github и без конца отвечает на мои вопросы по изучению Rust.
Кстати, превью для этого поста я тоже сгенерировал на dreamstudio.ai.
Понадобилось снимать из Node.js-приложения метрики по событиям. Например, инкрементить метрику при успешном ответе и всё в таком духе.
В моём распоряжении были Zabbix и Grafana.
Оказалось, что для Node.js есть модуль prom-client, который умеет экспортировать метрики, но вот беда - как и другие модули, с pm2 он не работает.
Проблема в том, что метрику, собранную в одном процессе, нельзя корректно получить в другом процессе, который отвечает за отдачу метрики zabix-агенту по http.
Исправляем упущение: pm2-prom-client
Модуль вообще-то для Prometheus, но и нам подходит.
Суть сводится к тому, что метрики передаются по шине pm2 в процесс агента и дальше экспортируются в plain-text или JSON.
Сегодня поговорим о деплое без даунтайма (zero downtime deployment) в Node.js-приложении.
Чтобы это стало возможным, приложение должно работать в дочернем процессе. Например, через PM2 или ts-node-dev.
Первое, что нужно сделать, - сообщить, что новый (задеплоенный) процесс готов принять работу передеплоиваемого приложения. Для этого нужно вызвать…
process.send(“ready”)
в своём приложении после того, как все нужные зависимости перешли в состояние готовности, например после успешного подключения к базе данных.
Кроме того, в приложение нужно добавить логику завершения всех текущих операций при получении сигнала остановки (SIGINT).
process.on(“SIGINT”, () => {
this.connection.close(); // For example, we will close the current connection to AMQP and stop accepting new messages.
setTimeout(() => {
process.exit()
}, 5000) // Let's wait for 5 seconds until the current processes are completed.
})
Если запустить такое приложение, например, через ts-node-dev, можно заметить, что после нажатия control+c оно завершается не сразу, а через 5 секунд.
Что произойдёт:
Новое приложение, запущенное в дочернем процессе, стартует и сообщает «ready», после чего приступает к своей бизнес-логике.
В это же время старому процессу отправляется сигнал SIGINT, и он переходит в режим завершения и доделывает свою работу, после чего уничтожается.
Пример
class App {
private isOnline = true
public start() {
console.log("Started")
this.restartListener()
this.loop()
}
private loop() {
if (!this.isOnline) return
console.log("Some business logic like redis/sql/amqp and others operation")
this.loop()
}
private restartListener() {
if (typeof process.send !== "function") return
process.send(“ready”)
process.on(“SIGINT”, () => {
this.isOnline = false
setTimeout(() => {
process.exit()
}, 3000)
})
}
}
В этом году я постоянно ловлю себя на мысли, что происходят вещи, которые ещё недавно я не мог себе даже представить:
Война России с Украиной, а вместе с ней - угроза применения ядерного оружия, примитивная и топорная пропаганда с разжиганием межнациональной ненависти. Свобода слова исчезла полностью, а за инакомыслие штрафуют и сажают
Рождение детей и вынужденная эмиграция: сначала из Москвы в Сочи (два дня за рулём), потом из Сочи в Казахстан - с семьёй и на машине
Раскол во мнениях в России привёл к конфликтам и ссорам, в том числе между близкими людьми
Глобальный кризис в IT. Многие компании-гиганты сокращают штат, и тысячи айтишников остаются без работы
На фоне глобального кризиса и войны многие талантливые разработчики (особенно неженатые) отказываются уезжать из страны (даже если есть возможность) и терпеливо ждут, когда их позовут на войну
ChatGPT уже готов конкурировать со многими профессионалами. Он вполне прилично пишет код на любых языках по текстовому ТЗ, находит уязвимости и баги в любом коде, а ещё с ним можно написать хороший текст на любом языке для LinkedIn
На фоне привлекательных зарплат обучение в IT поставили на поток, и IT во многом растеряло свою субкультуру. Очень многих специалистов мотивируют исключительно деньги.
Некоторые вещи переворачивают сознание:
Люди, которые учили нас доброте и дружбе народов, ругали за сигареты и драки в школе, - поддерживают войну
До сих пор есть люди, которые абстрагировались от происходящего
Среди поддерживающих есть и православные
Израиль атакует Палестину, и это тоже не вызывает в православной среде особого осуждения
Итог многолетней работы одного человека в роли управленца - обнищание населения, запредельный уровень коррупции и третья мировая. Но люди продолжат повторять «если не он, то кто?».
Что я из этого вынес
Нужно держаться подальше от любой идеологии, особенно той, что навязывает тебе исключительность. Мир огромен. В нём бесконечно много знаний и возможностей. Сегодня у нас есть шанс быть свободными и выбрать себе дом там, где комфортно, в любой точке планеты.
Прежние диски стояли на этой машине с самого начала, так что пора было их обновить. К тому же я устал их чинить. Низкий профиль резины сохранности дисков не способствует.
Очень хотел SSR Minerva AGLE, но сейчас это большая редкость. Поэтому взял что-то похожее - XXR 526 2P, ширина 8.5 дюйма, вылет 35, размер R18. Плюс проставки на 1.2 см.
Три года назад я не мог понять, почему патриотическое воспитание идёт через милитаризацию: «Юнармия», концерты на военную тематику, регулярные выставки военной техники. Почему такая дешёвая ипотека у военных, а не у учёных? Сегодня вопросов не осталось, курс государства мне наконец-то ясен.
Мне очень жаль, что в России нет своих Илонов Масков, Биллов Гейтсов и Стивов Джобсов. Очевидно, что ближайшие лет 50 они здесь не вырастут. Агрессия, желание отнять всё у успешного соседа, зависть, хамство, тяга к халяве, желание обмануть ближнего - всё это мне чуждо и противно.
Мне уже понятно, что прорывных разработок, передовых технологий и освоения космоса в России я при своей жизни не увижу. Будут жалкие пародии вроде китайских телефонов с ОС от Google и американским железом, пиратство и, может быть, клоны популярных сервисов, которые всегда будут отставать по технической реализации и обрастут откатами и распилом.
Участвовать в компании по импортозамещению ушедших из России сервисов у меня нет никакого желания. Уроки я извлёк из истории с Павлом Дуровым и ВКонтакте, Евгением Чичваркиным и проектом Nginx. Я открывал и закрывал ИП, обивал пороги налоговой и Роскомнадзора. Хватит, спасибо.
Я решил в ближайшее время вложиться в образование и вывезти семью и детей. Чтобы дети росли свободными, без безумной цензуры. Чтобы могли спокойно говорить, что 2 * 2 = 4, а установка была на созидание, а не на разрушение.
PS: Да, я прекрасно понимаю, что цензура в той или иной мере есть везде, и понимаю, что теперь ко многим русским будут относиться предвзято. Но считаю, что шансов вырасти открытыми всему миру у детей там больше, чем здесь.
В этой небольшой статье не будет ничего про dialplan Asterisk. Речь пойдёт про ARI - гибкий интерфейс для управления Asterisk.
Как только Asterisk получает INVITE и вызов уходит в наше приложение (Stasis), у нас появляется возможность собирать звонок на низком уровне.
Работа с ARI строится так: получаем события от Asterisk по WebSocket, обрабатываем их и создаём новые через HTTP REST API в сторону ARI.
StasisStart
Событие, которое получает наша программа, когда канал попадает в контекст Stasis, - StasisStart. Оно может сигнализировать, например, о начале исходящего или входящего вызова, и в ответ мы запускаем низкоуровневую логику: создаём канал в сторону другого абонента, добавляем его в бридж с каналом инициатора и делаем вызов.
Упрощённый сценарий обычного звонка
Получили от ARI событие StasisStart о том, что создан канал A.
Создали канал B с эндпоинтом в сторону SIP-пира или локального прокси (пример: SIP/MY_PEER/331234567890).
Объединили канал A из шага 1 и канал B из шага 2 в бридж.
Запустили dial в канал B.
Дождались от ARI события ChannelStateChange по каналу B. Если состояние ‘Up’, делаем answer в канале A, и начинается разговор.
При получении события ChannelHangupRequest по каналу A или B завершаем звонок.
Сессия звонка
Может понадобиться хранить данные о ходе звонка, включая CDR, по каждому каналу.
В качестве хранилища я бы рекомендовал Redis: при высоком CPS интенсивные запросы в базу могут сильно нагрузить диски и просадить производительность.
Оптимизация
Чтобы ARI-приложение нормально работало при высоком CPS, я бы советовал свести к минимуму запросы в реляционную базу. У меня на практике запросы шли только для небольшой логики лимитов при инициализации звонка и для записи CDR после его завершения. Всё остальное - в Redis.
Ещё рекомендую с точки зрения инфраструктуры держать ARI-приложение на одном хосте с Asterisk, чтобы снизить задержки.
Нагрузочный тест
Для теста я взял консольную утилиту baresip и bash-скриптом запустил 200 звонков на пул тестовых номеров, которые отвечают asterisk-овскими tt-monkeys.
200 параллельных звонков в сумме съели около 5% CPU. Все звонки завершились успешно, CDR записались.
Отладка stasis-приложения, которое сигнализирует об отвеченных звонках.SIP-сигнализация
Мой третий заход в GameDev оказался довольно продуктивным.
В очередной раз ностальгия по закрытому игровому проекту Rival Fire, где наш клан бился с ордами монстров и побеждал в PvP-турнирах 4×4 и 2×2, подтолкнула меня попробовать написать похожий проект с нуля.
Я давно хотел попробовать себя в создании игровых миров. Но чтение литературы и примитивные прототипы меня не устраивали. Сейчас я настроен сделать рабочий прототип многопользовательской мобильной игры. Главный ресурс - время, и его вечно не хватает. Конечно, если всё получится, модели персонажей, оружие и локации планирую отдать на аутсорс.
Игровой клиент
Для игрового клиента я выбрал движок Unity 3D и, чтобы не перегореть ещё на этапе графики, взял для рабочего прототипа бесплатные модели из открытого доступа. Хотя за последние три года я и набрался опыта в Blender, делать 3D-модели - это очень трудозатратно. Так что пока из своей графики у меня только UI.
В альфа-версии запланировал
Кооперативный бой на уровне (до четырёх игроков онлайн) с волнами, где игроку с командой нужно зачистить локацию от разных монстров (от летающих до ползающих), а на финальной волне сразиться с боссом.
PvP-бой 1 на 1 на зеркальной арене.
Сервер
Сервер матчей я написал на Node.js. Клиент с сервером общаются через веб-сокеты в формате JSON. На каждый матч поднимается отдельный процесс, который обслуживает игровую сессию для всех клиентов матча на своём сетевом порту. Пока не решил, хорошая ли это идея - использовать TCP вместо UDP. Но при тестах с двумя устройствами одновременно проблем не было
Сейчас сервер успешно спавнит NPC-монстров по сценарию из базы данных для всех игровых клиентов.
Отдельная большая тема - синхронизация позиции и действий игрока, особенно когда теряются пакеты. Может получиться так, что на клиенте игрока персонаж уже в точке C, а у всех остальных (из-за потерянных данных) он всё ещё в точке B.
Игровые ассеты
Игровые ассеты, которые могут часто меняться (персонажи, модели, звуки, логика стрельбы), я решил вынести в Asset Bundle и проверять обновления при каждом запуске клиента. Так обычно делают в современных играх, чтобы не заставлять пользователей постоянно качать приложение из маркета ради хотфиксов, а заодно уменьшить начальный размер игры и не отпугнуть игроков.
Git-репозиторий
Игра состоит из серверного и клиентского кода. С серверным всё просто, он весит всего 4 мегабайта, а вот с клиентом сложнее. Сырые текстуры для пребилдов занимают около гигабайта, и дальше будет только больше. Поэтому я решил поднять дома свой Git-сервер на Raspberry Pi 4.
Купил Raspberry Pi 4 вместе с корпусом, кулером и M.2 SSD на 512 ГБ и переношу весь код локально на «малинку».
Raspberry Pi 4 в корпусе Argon с M.2 выглядит как ТВ-приставка.
Ещё я развернул на ней копию сервера и гоняю весь игровой процесс локально на двух компьютерах. Думаю, позже покажу, как забавно мои тесты выглядят со стороны.
С тех пор как я стал тимлидом большой команды, каждый день передо мной встаёт куча вопросов. Как выстроить команду, которой нравится работать и которая при этом показывает эффективность и продуктивность. Как вовлечь команду и так далее. Раньше я занимался только программированием, опыта в управлении и построении команд у меня не было, поэтому время от времени стараюсь учиться и закрывать пробелы.
Эта программа, безусловно, даёт массу инсайтов. Мне повезло! Удалось познакомиться с Бабой Шивом и другими крутыми профессорами Стэнфорда, а ещё пообщаться с другими CEO и менеджерами и узнать, с какими проблемами они сталкиваются и как их решают.