Comments 64
Эх, как раз недавно закончил проект на WS и Tornado. Реализация во многом похожа. Если бы знал про эту либу — скорее всего использовал бы её.
Ага, а я три месяца не мог выложить это… руки не доходили. В итоге в pypi название какой-то парень занял, пршлось назвать wsrpc-tornado.
Я тоже занимаюсь работой с WS и хотел бы предупредить.
Если открывать соединение с WS на каждой странице то может случится так что один пользователь попытается открыть ваш сайт сразу в нескольких вкладках и тогда он может исчерпать лимит активных соединений с сервером, в хроме это 6 соединений. После исчерпания лимита новые соединения включая простые ajax запросы будут ждать пока не закроется одно из открытых соединений.

Для этого надо держать одно общее соединение на все вкладки, я уже почти закончил статью о реализации этого функционала в js думаю на следующей неделе опубликую.
Как альтернативный вариант — можно «раскидать» вебсокет-запросы по поддоменам.
Я проверял такой вариант в crome и выявил что таким образом ограничение вместо 6 запросов на домен расширяется на 255 запросов на все домены в сумме.
Гораздо правильнее использовать коммуникацию между вкладками браузера. К примеру вот моя реализация github.com/Levhav/signal.js для передачи сообщений от вкладки к вкладке.
Тут возникает целая куча проблем. Например, если я закрою вкладку в которой у меня открыт коннекшн, что делать остальным, кому установить соединение, как они выберут мастера???

ИМХО: * в DNS A записи и генерация субдомена при соединении из случайной строки, и 255 соединений куда реалистичнее.
У меня на сайте так реализовано, в Хроме открываем хоть 20 вкладок и никаких проблем нету.
Т.е. выше указанного бага не наблюдаю.
Спасибо за статью, присматриваюсь краем глаза к этой теме, а тут вот. Сразу полез в пример для нетерпеливых и вдруг не обнаружил trace'а запросов между браузером и сервером. Я в WebSoсket'ах не копенгаген, но при разработке web-приложений мне очень полезна бывает информация о том, что за данные ходят между клиентом и сервером, ходят ли вообще, а если ходят, то как выглядят.


Увидел ничего, испугался и полез для проверки на www.websocket.org/echo.html — там меня успокоили тем, что trace обнаружился. В связи с чем вопрос — простое демо работает, как и задумывалось или не совсем? Потому что ответ «I'm delayed 2 seconds» я получаю, а вот в логах этого не вижу. Раз в минуту браузер отправляет запрос на сервер:
GET ws://wsrpc.mosquito.su/ws/ HTTP/1.1
Host: wsrpc.mosquito.su
Connection: Upgrade
Pragma: no-cache
Cache-Control: no-cache
Upgrade: websocket
Origin: http://wsrpc.mosquito.su
Sec-WebSocket-Version: 13
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.99 Safari/537.36
Accept-Encoding: gzip, deflate, sdch
Accept-Language: ru-RU,ru;q=0.8,en-US;q=0.6,en;q=0.4
Sec-WebSocket-Key: cp5b9j20tvZtX5n70kyYXw==
Sec-WebSocket-Extensions: permessage-deflate; client_max_window_bits


и получает ответ:
HTTP/1.1 101 Switching Protocols
Server: nginx
Date: Thu, 22 Jan 2015 05:17:53 GMT
Connection: upgrade
Upgrade: websocket
Sec-WebSocket-Accept: iWSx/PeUVE8B3ti9ZjYa2WjiSIQ=


И так — каждую минуту. Похоже, пример не рабочий.
Каждую минуту такой запрос идет скорее всего из-за того что время сессии в nginx ограничено минутой, что вполне нормально при реверс-проксировании к приложению.

proxy_read_timeout 60;
proxy_send_timeout 60;


На www.websocket.org/echo.html тоже нет ничего на вкладке DevTools/Network. Единственное что там сделано — «логирование» данных по эвентам в dom-элемент средствами самого JS.

А пример, как по мне, рабочий, так как он отвечает на ваши запросы и помещает ответ в отведенное «окно».
В точку. Так как демо это демо а не полноценное приложение то я выставил таймауты.
А разве их не придется выставлять в любом случае? Просто для продакшен варианта их можно будет увеличить до 10-60 минут, чтобы не использовались стандартные, тем более что библиотека сама умеет реконнектиться.
Зачем их выставлять? Если ping-pong фреймы сервер будет посылать, то соединение может жить сколько угодно.
Всё общение при помощи ws в chrome отображается во вкладке network, после того как произошла смена протокола 101 нужно откртыть этот запрос и увидите пакеты, единственно вкладка с пакетами сама не обновляется — нужно руками нажимать на запрос 101
image
Это не известный баг. Это «сначала открыл сайт, потом отладчик и начал слушать эфир».
Ну у одного моего приятеля, chromium себя ведет именно так, не показывает фреймы в WS. Может он в Gentoo собрал что-то не так.
Открыл хромиум на Gentoo, все работает как надо. Есть шанс что Хром поставили и забыли, а стоило бы обновить, там в последних версиях dev tools хорошо причесали.
Спасибо за тест. За отсутствием аргументов соглашусь с вами. Да и дело было пару месяцев назад. Плюс гента у каждого своя ;-)
Не помню, разрабатывалось это где-то пол года назад. На WAMP я смотел, и что-то мне не понравилось, убей не помню что. Сейчас, очень похоже, они сильно выросли.
Уже год используем в компании SignalR как весьма неплохой враппер для вебсокетов, очень удобная поддержка на уровне JS, конечно годно только для .Net сайтов, но весьма грамотно реализовано (не PR).

Минусы только порты, каждое соединение отъедает порт на сервере, т.е. есть ограничения по пользователям, есть поддержка LoadBalancing но пока все еще весьма в зачаточном состоянии.
А почему на сервере отъедается порт? Разве сервер не слушает один порт, а клиенты на него соединяются? Чую какой-то подвох и костыль в стиле запуск приложения в отдельном воркере и проксирование на него входящих соединений.
Вот что пишут

It's important to understand that this application will be used by a small number of administrative users, so it's not going to be used by thousands of users simultaneously. At most we figure there may be 10 people connected at a time. However, connections are something to consider with SignalR. As cool as this technology is, it's connected technology meaning that each client connected through SignalR is using a persistent and dedicated connection on the Web Server. 10 users or 100 are probably not much of a problem, but thousands of users may bump up against the Web server connection and Windows thread and resource limits eventually. SignalR also includes some scalability features, but these get very complex quickly and if this becomes an issue I personally think that one should reconsider whether SignalR or a real-time connection based interface is the right choice…

там еще память жрется от каждой коннекции, на самомо деле в реальной жизни мы не сталкивались с этой проблемой, так как сайт с этой технологией только для внутреннего использования, однако понятно что для коммерческого использования весьма сыровато еще.
that this application will be used by a small number of administrative users, so it's not going to be used by thousands of users simultaneously.

Ну по моим тестам, топик тянет сотню соединений на воркер не напрягаясь. Больше не тестил. Вебсокет достаточно сложно протестировать тем же питоном. Была мысль написать на NodeJS клиента который будет открывать сокеты, но руки не дошли как-то.
Не, не так ;)

Гляньте tcpkali — генератор нагрузки для TCP и WebSockets серверов!

image
и как гласит caniuse.com WebSocket не поддерживается только в Opera Mini (надо бы провести голосование, как давно кто-либо видел Opera Mini)

К сожалению, caniuse.com гласит еще и IE 8, 9, а для поиска таких клиентов не надо голосование — это все люди, вынужденные пользоваться IE и работающие с XP. И в гос. организациях таких людей немеряно.

Да, я знаю, что с помощью флэша это обходится, но и говорить, что любой браузер поддержит веб-сокет из коробки, к сожалению, можно будет с абсолютно спокойной совестью еще лет через пару.
Ну никто не мешает поставить Firefox на Windows XP, а IE89 уже не поддерживаются Microsoft.
Ох, если бы не мешали…
Если есть крупная организация с большинством терминалов под XP, что означает IE8 максимум + достаточно упёртый безопасник + админ, которому не интересно ставить всем новый браузер == любимая проверка на IE в яваскрипте и использование флешеобразного websocket.js. Это реальная история. Прям сейчас происходит, да.

Я первый буду прыгать от радости, когда эти проблемы вместе с IE до 10-ки канут в Лету, мне нрвятся веб-сокеты в некоторых задачах. Единственное, ради чего я написал свой первый комментарий тут — предостеречь людей, что такое может случиться и иметь в виду, что если они не пропишут в требованиях проекта IE10+, а поставят «любой браузер», то могут сразу же начинать осваивать websocket.js и писать проверки на текущий браузер.
Я на самом деле думал сделать работу через long polling при не поддерживаемости Websocket. Но пока без надобности просто.
Так потому, и не переделал. Вообще я считаю что у библиотеки своя ниша. И отлично что комментарии содержат столько отсылок к другим продуктам. Это прекрасное хранилище информации.
Вы очень вовремя, спасибо за библиотеку. Прям сейчас ковыряю и со старта возникло несколько вопросов:

1. Чтоб завести в require.js пришлось немного похачить wsrpc.js (не видел Q и работал в глобальной области видимости)

2. С запросами к серверу всё понятно, а вот наоборот — не очень. В документации примера не нашёл, поэтому на данный момент всё, что я придумал — это делать запрос к серверу, который инициализирует PeriodicCallback, а на клиенте уже слушать ответы через addRoute. Можно как-то более изящно решить этот вопрос?

Реализую свой stock board — ждите pull-реквестов :)
С запросами к серверу всё понятно, а вот наоборот — не очень.


тут же прямо написано
        return self.socket.call('whoAreYou', callback=self._handle_user_agent)

т.е. вызвать на клиенте whoAreYou и после того, как клиент передаст ответ на это выполнить callback
Не вполне понятно, почему запросы с клиента на сервер идут (с точки зрения клиента) асинхронно, а обратные запросы — синхронно. Для поддержки нормального асинхронного взаимодействия в обе стороны на стороне браузера надо совсем немного изменений.

PS пишу сюда потому что автор куда охотнее отвечает на комментарии тут, чем на пулл-реквесты там :)
Нигде не сказано, что обратные идут синхронно. С точки зрения браузера все асинхронно, на торнаде, как напишет разработчик.

Забыл написать одну особенность, при старте WebSocket инициализирует thread pool, и выполняет все методы в тредах, именно поэтому можно не бояться писать sleep в методах.
Да неужели?
ws.onmessage = function (message) {
				log('WSRPC: ONMESSAGE CALLED (' + self.public.state() + ')');
				trace(message);
				var data = null;
				if (message.type == 'message') {
					try {
						data = JSON.parse(message.data);
						log(data.data);
						if (data.hasOwnProperty('type') && data.type === 'call') {
							if (!self.routes.hasOwnProperty(data.call)) {
								throw Error('Route not found');
							}

							out = {
								serial: data.serial,
								type: 'callback',
								data: self.routes[data.call](data.arguments)
							};
							self.socket.send(JSON.stringify(out));

Мне одному кажется, что callback, записанный в словаре self.routes, в этом месте вызывается синхронно?
Так, а что вы предлагаете?
Сделать setTimeout(0, func) какой в этом смысл, эвент пришел асинхронно с сервера. Или как?
В браузере появились треды?

Я открыт к pull реквестам.
Я предлагаю дать возможность обработчику вернуть не значение, а обещание (promise). PS pull request вы игнорируете уже 15 часов.
Согласен. У меня просто такого кейса не возникало. Заводите issue, сделаю.
(цензура), вы издеваетесь? Я это уже сделал вчера утром.

PS кто-нибудь еще кроме меня видит мой pull request? А то у меня такое ощущение, что на гитхабе появился спам-фильтр…
Нет, я не издеваюсь, но вы сломали обратную совместимость. Теперь вызов может быть только promise.
Почему он может быть только обещанием? Я специально обернул его в Q, чтобы сохранить обратную совместимость.
ок, тогда в чем смысл? как это обещание обернутое в обещание сресолвить?
Функция Q работает по-разному в зависимости от того, что ей подано на вход.

Если ей подано «родное» обещание — она его и возвращает. Если ей подано «чужое» обещание — она возвращает свою обертку над ним. Если ей подана константа — она возвращает обертку над константой.

В итоге, конструкция Q(result).then(reply), выполнит reply сразу же, если result — константа, и дождется result, если result — обещание.
Потому что promise это один из 5 асинхронных паттернов в JS. И ограничивать разработчика в выборе паттерна не хорошо.
Ну вы можете написать свою реализацию клиента, на любом другом из оставшихся 4х. Просто там все в основном написано на promise
Неужели отсутствие асинхронности лучше, чем присутствие хотя бы одного паттерна? Да и библиотека не может поддерживать все 5 сразу. В этой библиотеке уже использовались обещания — вот я и предложил очевидный вариант.
callback это тоже асинхронный паттерн. Не суть. Я скорее о том, что причина появления таких решений вообще она именно в том, что callback это фундаментальный примитив в JS и все «асинхронности» рано или поздно вернутся к нему. И имеяя возможность закинуть callback можно городить что угодно, имея что-то другое, уже сложнее и менее правильно.

Что до конкретно этой библиотеки, то ничего не скажу, даже не вижу в ней смысла.
Ответьте честно, вы попадали когда-нибудь в ситуацию именуемую «callback hell»?
Лично я нет. Зато видел как другие мучаются.
А вы попадали когда-нибудь в ситуацию именуемую «ReferenceError: myVar is not defined»?
В данном случае callback — это не асинхронный паттерн, а название для RPC, инициируемого сервером. До моей модификации этот вызов обязан был завершиться синхронно, что не позволяло, к примеру, по запросу с сервера показать диалог, запрашивающий у пользователя некоторую информацию (точнее, диалог-то показать было можно, но вот отправить введенную пользователем информацию в качестве ответа на этот RPC — уже нет).
Гм, а чем не подошло обычный JSON-RPC over WS? Только обертку осталось сделать, если хотеть с промисами работать на клиенте
Можно переделать на JSON RPC там по сути тоже самое. Да велосипед, но гораздо менее многословный, нежели json rpc.
А мне понравился стандартный интерфейс WS в JS. И ИМХО и хорошо, что это не RPC…
Согласен с вами. Но у сеня ничего больше чата сделать не удалось вот и заверте…
Но у сеня ничего больше чата сделать не удалось вот и заверте…

Простите, что то я вас не понял…
Надо на время коммента смотреть. Видимо в 00:00 компьютер топикстартера постит всё, что не оттправлено, закрывает все вкладки и выключается. :)
Only those users with full accounts are able to leave comments. Log in, please.