Instant PlanningPoker на socket.io

в 9:04, , рубрики: agile, javascript, node.js, scrum, socket.io, метки: , , ,

Мотиватция

Однажды в студеную зимнюю пору течении очередного планнинг-митинга на работе, где каждый пользовался своим инструментом для голосования — бумажные карты, всевозможные приложения для телефонов, etc — меня посетила мысль — а зачем сидеть всем в одном помещении, когда планирование можно проводить со своих рабочих мест, или даже из дома.

Итак идея — сделать сервис удаленного планирования, посредством техники Planning Poker, так популярной в agile мире. А также чуть лучше разобраться с тем как работает socket.io и сопутствующие технологии.

Постановка задачи

Разработать страницу заходя на которую пользователь вводит свое имя и «комнату», далее один из пользователей устанавливает обсуждаемую задачу, все голосуют — когда проголосовал последний из группы — карты открываются.

Первая попытка реализации протокола

Итак начитавшись этой статьи — первая мысль: браузер — тонкий клиент, вся логика и сосотояние хранятся на сервере, как только подклучается новый клиент, в определенную комнату — сервер рассылает всем новый снапшот состояния комнаты. Тоже происходит когда клиенты голосуют, меняют тему обсуждения и т.д.

Однако такой подход требует какого-то хранилища на сервере, (мне хотелось избежать использования базы данных, так как хостить прект хотелось по возможности бесплатно). Как вариант хранить состояние в памяти — массиве или некоем сложном объекте, но это чревато утечками памяти.

Вторая и финальная реализация

Вторым подходом к решению задачи было сделать каждый отдельный клиент ответсвенным за хранение своего собственного состояния и обмен состояниями со всеми участниками. Если абстрагориваться от socket.io то протокол можно обяснить следующим образом — вежливые люди в темной комнате:

  • когда кто-то входит join — он объявляет об этом и называет свое имя — в ответ все остальные люди в комнате называют свои имена и состояния.
  • когда кто-то называет свое (обновившееся) состояние update — все в комнате запомианют его.
  • когда кто-то уходит из комнаты leave — сервер сообщает об этом всем остальным учасникам и они благополучно о нем забывают.

последний пункт не вписывается в правила вежливости — так как клиент не может оповестить всех о своем отключении, эту фунцию пришлось возложить на сервер — именно сервер отвественнен за то чтоб сообщить другим об уходе одного из участников:

 socket.on('disconnect', function () {
        var rooms = that.__io.sockets.manager.roomClients[socket.id];
        for(var room in rooms) {
          if ((room != '') && (rooms[room])) {
            /*removing a slash*/
            room = room.substring(1);
            that.__io.sockets.in(room).emit('leave', { room : room, id : socket.id });
       }
     }
  });

Отдельно надо упомянуть поддержку комнат в socket.io, о которой нислова не упоминается на на офиц. сайте. Я много информации почерпнул отсюда. По большому счету комнаты есть некий логический способ разделить всех клиентов подклученных к серверу.
Вступить в комнату или покинуть ее можо только с серверной части, одновременно можно находится в нескольких комнатах:

socket.join('room');
socket.leave('room');

В остальном север — всего лишь проксирует сообщения между клиентами в одной комнате. Единственная «бизнес логика» на сервере — это ограничение на вхождение в комнаты до 12 человек. Например пересылка сообщения update на севере выглядит так:

  /*sending update*/
  socket.on('update', function (data) {
    /*notify everyone*/
    data = that.identify(data, socket);
    that.__io.sockets.in(data.room).emit('update', data);
  });
Клиент

Вся логика у нас происходит на клиенте. Он ответвственнен за голосование клиента (выбор карты), принимает решение когда показывать карты остальных участников, сбрасывает голоса когда меняется топик и/или приходят новые люди. Клиент также публикует события — это сделано для того чтобы отделить бизнес логику клиента от собственно манипуляций с DOMом и т.п. Выглядит это так:

client.on('reveal', function(){
    $('.flippable').each(function(i, val) {
          setTimeout(function(){
            $(val).addClass('flipped');
        }, i*50);
    });
  });

Заключение и демонстрация

В заклюцении хочу сказать что подобный подход хоть и проявил себя не плохо, не лишен недостатков — т.е. есть возможность влезть в логику клиента и таким образом нарушить синхронизацию бежду учасниками. Так же голоса прибывают сразу как только ктото проголосовал, они только не видны пока все не проголосовали, однако не хитрыми манипуляциями в Firebug можно подглядывать голоса других.

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

как оказалось в самый последний момент Heroku не поддерживает websockets, и пришлось скатиться до long polling — посему предлагаемое вам демо — лишено одной важной возможности — клиент вовремя не узнает что один из участников отключился, это событие не происходит. Для полноценного ознакомления — я предлагаю вам склонировать этот репозиторий на Github'e и запустить на локальной машине, серверный скрипт делает проверку на Heroku и не находя её, работает в штатном режиме с websocket'ами. Так же для полноценного тестирования понадобится 2 или более участников.

Удачи!

Автор: artem82

Источник

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js