Хотите расти как разработчик и найти крутую работу? Не протирайте штаны — займитесь Open Source проектами. Так легче всего попасть в лучшие команды разработчиков и положить себе в резюме настоящий проект, вместо нелепых «примеров кода». Но найти подходящий проект для участия сложно. Начинаются лень и отговорки, а за ними — отсутствие профессионального роста, критики по-настоящему крутых программистов, уныние и застой.
На Cult of Martians мы собираем интересные задачи для современных веб-программистов. Можно выбрать подходящую по сложности, продолжительности и специализации. Задачи не выдуманы «из воздуха» — каждая решает насущную проблему, и решить ее можно через создание нового Open Source проекта или улучшение существующего. Решайте задачи, прокачивайтесь, присылайте решение на оценку. Лучших могут пригласить к себе на работу компании, программистам которых понравится ваше решен ие.
Для новичков, задача на неделю
Необходимо разработать Ruby-инструмент (CLI) для выполнения сценариев взаимодействия WebSocket-клиентов с сервером.
Утилита может быть использована как для blackbox-тестирования (в том числе и нагрузочного) WebSocket-приложений, так и просто в качестве удобного консольного клиента при разработке.
Польза: возможность применить на практике методы многопоточного программирования, попрактиковаться в написании CLI, поближе познакомиться с технологией WebSocket.
Решение: итоговая реализация основана на решениях Кирилла Архипова и Сергея Елушева, за что им выражается особая благодарность.
Как это должно работать?
Рассмотрим два примера. Предположим, что наш сервер — это Action Cable.
Предположим, что мы хотим проверить следующий сценарий (echo
):
клиент отправляет сообщение серверу;
сервер в ответ отправляет такое же сообщение клиенту.
Запуск сценария будет производиться так:
$ wsdirector echo.yml ws://localhost:3444/cable
Здесь ws://localhost:3444/echo
– путь к нашему WS-приложению, а echo.yml
– файл сценария, который
может выглядеть так:
# приветственное сообщение от Action Cable
- receive:
# если поле data – объект, то
# предполагаем, что это json
data:
type: "welcome"
# подписка на канал и ожидание подтверждения
- send:
data:
command: "subscribe"
identifier: '{"channel":"TestChannel"}'
- receive:
data:
type: "subscription_confirmation"
identifier: '{"channel":"TestChannel"}'
- send:
data:
command: "message"
identifier: '{"channel":"TestChannel"}'
data: '{"text": "echo","action":"echo"}'
- receive:
data:
identifier: '{"channel":"TestChannel"}'
message: '{"text": "echo"}'
В случае возникновения ошибки на каком-либо шаге, скрипт должен завершаться с ненулевым статусом (exit code) и выводить в STDERR
информацию о том, на каком этапе сценария произошел сбой.
Давайте рассмотрим более интересный случай с несколькими клиентами с отличающимся поведением.
все клиенты подключаются к серверу и подписываются на канал;
клиенты X отправляют сообщение;
все клиенты должны получить такое число сообщений, сколько у нас клиентов X.
Кроме того, нам бы хотелось, чтобы мы могли легко конфигурировать число клиентов:
Наша команда будет выглядеть так:
$ wsdirector -s 10 broadcast.yml ws://localhost:3444/cable
А файл сценария broadcast.yml
:
# сценарий публикующего клиента
- client:
# :scale – это параметр переданный в команде через -s
# multiplier говорит, сколько нам нужно таких клиентов
# (коэффициент "размножения")
multiplier: ":scale"
actions:
# приветственное сообщение от Action Cable
- receive:
data:
type: "welcome"
# подписка на канал и ожидание подтверждения
- send:
data:
command: "subscribe"
identifier: "{\"channel\":\"TestChannel\"}"
- receive:
data:
type: "subscription_confirmation"
identifier: "{\"channel\":\"TestChannel\"}"
# важный момент – ждём, пока все клиенты достигнут этой точки в сценарии
- wait_all
# отправляем сообщение в канал
- send:
data:
command: "message"
identifier: "{\"channel\":\"TestChannel\"}"
data: "{\"text\": \"hello\", \"action\":\"broadcast\"}"
# сценарий обычного клиента
- client:
# слушателей мы хотим в 10 раз больше
multiplier: ":scale * 10"
actions:
# аналогичные действия для подписки на канал пропустим (кстати, как бы нам их переиспользовать?)
...
- wait_all
- receive:
# каждый клиент должен получить сообщение от каждого публикующего клиента
multiplier: ":scale"
data:
identifier: "{\"channel\":\"TestChannel\"}"
message: "{\"text\": \"hello\"}"
Советы по реализации
Язык реализации: Ruby.
Код Action Cable каналов из примеров – gist.
Для реализации веб-сокет клиента можно использовать gem websocket-client-simple, а также посмотреть примеры его использования в Rails или Lite Cable.
Инструкции по выполнению