Автор:

ice

AI / Rust / Python

Начал изучать новый для себя язык программирования - Rust. Его часто используют для системного программирования и высокопроизводительных приложений.

Параллельно с учёбой пилю пет-проект на GitHub, в котором есть интеграция с OpenAI (ChatGPT) и TTS-сервисом. Планирую прикрутить к нему ещё какой-нибудь AI для генерации картинок (вроде dreamstudio.ai).

Ещё пришлось написать небольшой скрипт на Python с FastAPI и Silero AI для TTS-запросов. Бот умеет отвечать голосом, зачитывая ответы ChatGPT. Ради этого пришлось поднять отдельный сервис.

Этот пет-проект уже оказался полезным в повседневной работе. С его помощью я сделал нормальную страницу профиля на GitHub, и он без конца отвечал на мои вопросы по изучению Rust.

Кстати, превью для этого поста я тоже сгенерировал на dreamstudio.ai.

Этот пет-проект уже хорошо себя показал. Помогает мне в повседневной работе, помог сделать нормальную страницу профиля на Github и без конца отвечает на мои вопросы по изучению Rust.

Кстати, превью для этого поста я тоже сгенерировал на dreamstudio.ai.

Prometheus-экспортер для кластера PM2

Понадобилось снимать из Node.js-приложения метрики по событиям. Например, инкрементить метрику при успешном ответе и всё в таком духе.

В моём распоряжении были Zabbix и Grafana.

Оказалось, что для Node.js есть модуль prom-client, который умеет экспортировать метрики, но вот беда - как и другие модули, с pm2 он не работает.

Проблема в том, что метрику, собранную в одном процессе, нельзя корректно получить в другом процессе, который отвечает за отдачу метрики zabix-агенту по http.

Исправляем упущение: pm2-prom-client

Модуль вообще-то для Prometheus, но и нам подходит.

Суть сводится к тому, что метрики передаются по шине pm2 в процесс агента и дальше экспортируются в plain-text или JSON.

Деплой без даунтайма / TypeScript

Сегодня поговорим о деплое без даунтайма (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 во многом растеряло свою субкультуру. Очень многих специалистов мотивируют исключительно деньги.

Некоторые вещи переворачивают сознание:

  • Люди, которые учили нас доброте и дружбе народов, ругали за сигареты и драки в школе, - поддерживают войну
  • До сих пор есть люди, которые абстрагировались от происходящего
  • Среди поддерживающих есть и православные
  • Израиль атакует Палестину, и это тоже не вызывает в православной среде особого осуждения
  • Итог многолетней работы одного человека в роли управленца - обнищание населения, запредельный уровень коррупции и третья мировая. Но люди продолжат повторять «если не он, то кто?».

Что я из этого вынес

Нужно держаться подальше от любой идеологии, особенно той, что навязывает тебе исключительность. Мир огромен. В нём бесконечно много знаний и возможностей. Сегодня у нас есть шанс быть свободными и выбрать себе дом там, где комфортно, в любой точке планеты.

Новые диски на Mark2: XXR 526 2P

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

Очень хотел SSR Minerva AGLE, но сейчас это большая редкость. Поэтому взял что-то похожее - XXR 526 2P, ширина 8.5 дюйма, вылет 35, размер R18. Плюс проставки на 1.2 см.

Читать полностью

Те, кто молчал, перестали молчать. Те, кому нечего ждать, садятся в седло

Три года назад я не мог понять, почему патриотическое воспитание идёт через милитаризацию: «Юнармия», концерты на военную тематику, регулярные выставки военной техники. Почему такая дешёвая ипотека у военных, а не у учёных? Сегодня вопросов не осталось, курс государства мне наконец-то ясен.

Мне очень жаль, что в России нет своих Илонов Масков, Биллов Гейтсов и Стивов Джобсов. Очевидно, что ближайшие лет 50 они здесь не вырастут. Агрессия, желание отнять всё у успешного соседа, зависть, хамство, тяга к халяве, желание обмануть ближнего - всё это мне чуждо и противно.

Мне уже понятно, что прорывных разработок, передовых технологий и освоения космоса в России я при своей жизни не увижу. Будут жалкие пародии вроде китайских телефонов с ОС от Google и американским железом, пиратство и, может быть, клоны популярных сервисов, которые всегда будут отставать по технической реализации и обрастут откатами и распилом.

Участвовать в компании по импортозамещению ушедших из России сервисов у меня нет никакого желания. Уроки я извлёк из истории с Павлом Дуровым и ВКонтакте, Евгением Чичваркиным и проектом Nginx. Я открывал и закрывал ИП, обивал пороги налоговой и Роскомнадзора. Хватит, спасибо.

Я решил в ближайшее время вложиться в образование и вывезти семью и детей. Чтобы дети росли свободными, без безумной цензуры. Чтобы могли спокойно говорить, что 2 * 2 = 4, а установка была на созидание, а не на разрушение.

PS: Да, я прекрасно понимаю, что цензура в той или иной мере есть везде, и понимаю, что теперь ко многим русским будут относиться предвзято. Но считаю, что шансов вырасти открытыми всему миру у детей там больше, чем здесь.

Asterisk, ARI. Коммутируем как боги

В этой небольшой статье не будет ничего про dialplan Asterisk. Речь пойдёт про ARI - гибкий интерфейс для управления Asterisk.

Как только Asterisk получает INVITE и вызов уходит в наше приложение (Stasis), у нас появляется возможность собирать звонок на низком уровне.

Работа с ARI строится так: получаем события от Asterisk по WebSocket, обрабатываем их и создаём новые через HTTP REST API в сторону ARI.

StasisStart

Событие, которое получает наша программа, когда канал попадает в контекст Stasis, - StasisStart. Оно может сигнализировать, например, о начале исходящего или входящего вызова, и в ответ мы запускаем низкоуровневую логику: создаём канал в сторону другого абонента, добавляем его в бридж с каналом инициатора и делаем вызов.

Упрощённый сценарий обычного звонка

  1. Получили от ARI событие StasisStart о том, что создан канал A.
  2. Создали канал B с эндпоинтом в сторону SIP-пира или локального прокси (пример: SIP/MY_PEER/331234567890).
  3. Объединили канал A из шага 1 и канал B из шага 2 в бридж.
  4. Запустили dial в канал B.
  5. Дождались от ARI события ChannelStateChange по каналу B. Если состояние ‘Up’, делаем answer в канале A, и начинается разговор.
  6. При получении события 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 #3

Мой третий заход в 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 выглядит как ТВ-приставка.

Корпус и SSD-расширение можно найти здесь: https://a.aliexpress.com/_9RbPFU

Ещё я развернул на ней копию сервера и гоняю весь игровой процесс локально на двух компьютерах. Думаю, позже покажу, как забавно мои тесты выглядят со стороны.

Stanford Graduate School of Business

С тех пор как я стал тимлидом большой команды, каждый день передо мной встаёт куча вопросов. Как выстроить команду, которой нравится работать и которая при этом показывает эффективность и продуктивность. Как вовлечь команду и так далее. Раньше я занимался только программированием, опыта в управлении и построении команд у меня не было, поэтому время от времени стараюсь учиться и закрывать пробелы.

Эта программа, безусловно, даёт массу инсайтов. Мне повезло! Удалось познакомиться с Бабой Шивом и другими крутыми профессорами Стэнфорда, а ещё пообщаться с другими CEO и менеджерами и узнать, с какими проблемами они сталкиваются и как их решают.