Нам нужны твои мозги

Хотите расти как разработчик и найти крутую работу? Не протирайте штаны — займитесь Open Source проектами. Так легче всего попасть в лучшие команды разработчиков и положить себе в резюме настоящий проект, вместо нелепых «примеров кода». Но найти подходящий проект для участия сложно. Начинаются лень и отговорки, а за ними — отсутствие профессионального роста, критики по-настоящему крутых программистов, уныние и застой.

На Cult of Martians мы собираем интересные задачи для современных веб-программистов. Можно выбрать подходящую по сложности, продолжительности и специализации. Задачи не выдуманы «из воздуха» — каждая решает насущную проблему, и решить ее можно через создание нового Open Source проекта или улучшение существующего. Решайте задачи, прокачивайтесь, присылайте решение на оценку. Лучших могут пригласить к себе на работу компании, программистам которых понравится ваше решение.

Бэк: Утилита для запуска WebSocket-клиентов с поддержкой сценариев

Для новичков, задача на неделю

Необходимо разработать Ruby-инструмент (CLI) для выполнения сценариев взаимодействия WebSocket-клиентов с сервером.

Утилита может быть использована как для blackbox-тестирования (в том числе и нагрузочного) WebSocket-приложений, так и просто в качестве удобного консольного клиента при разработке.

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

Решение: итоговая реализация основана на решениях Кирилла Архипова и Сергея Елушева, за что им выражается особая благодарность.

Как это должно работать?

Рассмотрим два примера. Предположим, что наш сервер — это Action Cable.

Пример №1

Предположим, что мы хотим проверить следующий сценарий (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 информацию о том, на каком этапе сценария произошел сбой.

Пример №2

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

  • все клиенты подключаются к серверу и подписываются на канал;

  • клиенты 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.

Инструкции по выполнению

  1. Форкнуть проект wsdirector на GitHub.
  2. Реализовать необходимый функционал.
  3. Сделать Pull Request.