Giter Club home page Giter Club logo

django-websocket-redis's Introduction

django-websocket-redis

Project home: https://github.com/jrief/django-websocket-redis

Detailed documentation on ReadTheDocs.

Online demo: http://django-websocket-redis.awesto.com/

Websockets for Django using Redis as message queue

This module implements websockets on top of Django without requiring any additional framework. For messaging it uses the Redis datastore and in a production environment, it is intended to work under uWSGI and behind NGiNX or Apache version 2.4.5 or later.

Features

  • Largely scalable for Django applications with many hundreds of open websocket connections.
  • Runs a separate Django main loop in a cooperative concurrency model using gevent, thus only one thread/process is required to control all open websockets simultaneously.
  • Full control over this separate main loop during development, so Django can be started as usual with ./manage.py runserver.
  • No dependency to any other asynchronous event driven framework, such as Tornado, Twisted or Socket.io/Node.js.
  • Normal Django requests communicate with this separate main loop through Redis which, by the way is a good replacement for memcached.
  • Optionally persisting messages, allowing server reboots and client reconnections.

If unsure, if this proposed architecture is the correct approach on how to integrate Websockets with Django, then please read Roberto De Ioris (BDFL of uWSGI) article about Offloading Websockets and Server-Sent Events AKA “Combine them with Django safely”.

Please also consider, that whichever alternative technology you use, you always need a message queue, so that the Django application can “talk” to the browser. This is because the only link between the browser and the server is through the Websocket and thus, by definition a long living connection. For scalability reasons you can't start a Django server thread for each of these connections.

Release History

Refer to changelog.rst

Build status

Build Status Downloads

Questions

Please use the issue tracker to ask questions.

License

Copyright © 2015 Jacob Rief.

MIT licensed.

django-websocket-redis's People

Contributors

agdude avatar aorcajo avatar aviau avatar bastiao avatar charleswhchan avatar daniilsezonov avatar depaolim avatar eagle21st avatar erikreed avatar erwinjunge avatar fako avatar gitaarik avatar gtnx avatar iorlas avatar jenda1 avatar joerick avatar jrief avatar keilan avatar lordi avatar manuelzs avatar marcelchastain avatar mlambir avatar nanuxbe avatar oal avatar orn688 avatar roidelapluie avatar silent743 avatar smcdonald-bepress avatar timgates42 avatar yguarata avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

django-websocket-redis's Issues

Redis on unix socket

Currently, ws4redis.publisher.redis_connection_pool supports only Redis on TCP connection. I had to replace the initialization of redis_connection_pool with something like below to get django-websocket-redis to work with Redis on unix socket:

redis_connection_pool = ConnectionPool.from_url('unix://:{0}@{1}?db=0'.format(settings.WS4REDIS_CONNECTION['password'], settings.WS4REDIS_CONNECTION['unix_socket_path']))

Not all published messages are processed

If I publish multiple messages in quick succession in a custom Redis Store's publish_message method, only the first message is received by the browser. I've traced the problem to select() not being triggered for the additional messages due to redis-py using buffered reads within it's PubSub parse_response method. redis-py calls readline on it's socket which will consume multiple messages off its socket but only return the first while leaving any additional messages buffered. Since the subsequent messages have already been read and buffered, select() is (correctly) not being triggered again.

Redis - Information

Hello !

I'm trying to develop a asynchronous application using sockets with Django and i didn't know what to use for that.

I didn't understand what redis stands for in django-websocket-redis, what is the utility of redis in this module of websockets in django ?

Why use this module unstead of NodeJS and SocketIO ? What are the advantages ?

Thanks you.
Sorry for my english.

three way data binding, input flicker

If I open two chrome browsers on the three way example + start entering a name, by about the 8th character it gets confused, continually deleting the last two characters and re-inserting them, so it becomes impossible to interact with the field.

Here is an animated gif of the bug in action
http://imgur.com/3eMv5rc

Here is the log output of that session

Quit the server with CONTROL-C.
[2014-02-17 12:26:23,673 django_runserver] INFO: Websocket support is enabled
[2014-02-17 12:26:25,714 django_runserver] DEBUG: WebSocket request accepted, switching protocols
[2014-02-17 12:26:25,716 wsgi_server] DEBUG: Subscribed to channels: subscribe-broadcast, publish-broadcast
[2014-02-17 12:26:29,756 django_runserver] DEBUG: WebSocket request accepted, switching protocols
[2014-02-17 12:26:29,758 wsgi_server] DEBUG: Subscribed to channels: subscribe-broadcast, publish-broadcast
[17/Feb/2014 12:26:37] "GET /threeway_databinding/ HTTP/1.1" 200 4750
[2014-02-17 12:26:37,729 websocket] DEBUG: Closed WebSocket
[2014-02-17 12:26:37,730 websocket] DEBUG: Failed to write closing frame -> closing socket
[2014-02-17 12:26:37,730 websocket] DEBUG: Closed WebSocket
[2014-02-17 12:26:37,731 websocket] DEBUG: Failed to write closing frame -> closing socket
[2014-02-17 12:26:37,732 websocket] DEBUG: Closed WebSocket
[17/Feb/2014 12:26:37] "GET /ws/threeway_databinding/?subscribe-broadcast&publish-broadcast HTTP/1.1" 101 0
[17/Feb/2014 12:26:37] "GET /static/demo.js HTTP/1.1" 304 0
[17/Feb/2014 12:26:37] "GET /static/js/djng-websocket.js HTTP/1.1" 304 0
[2014-02-17 12:26:38,152 django_runserver] DEBUG: WebSocket request accepted, switching protocols
[2014-02-17 12:26:38,153 wsgi_server] DEBUG: Subscribed to channels: subscribe-broadcast, publish-broadcast
[17/Feb/2014 12:26:43] "GET /threeway_databinding/ HTTP/1.1" 200 4750
[2014-02-17 12:26:43,988 websocket] DEBUG: Closed WebSocket
[2014-02-17 12:26:43,989 websocket] DEBUG: Failed to write closing frame -> closing socket
[2014-02-17 12:26:43,990 websocket] DEBUG: Closed WebSocket
[2014-02-17 12:26:43,990 websocket] DEBUG: Failed to write closing frame -> closing socket
[2014-02-17 12:26:43,991 websocket] DEBUG: Closed WebSocket
[17/Feb/2014 12:26:43] "GET /ws/threeway_databinding/?subscribe-broadcast&publish-broadcast HTTP/1.1" 101 0
[17/Feb/2014 12:26:44] "GET /static/js/djng-websocket.js HTTP/1.1" 304 0
[17/Feb/2014 12:26:44] "GET /static/demo.js HTTP/1.1" 304 0
[2014-02-17 12:26:44,343 django_runserver] DEBUG: WebSocket request accepted, switching protocols
[2014-02-17 12:26:44,344 wsgi_server] DEBUG: Subscribed to channels: subscribe-broadcast, publish-broadcast

Mock redis for unit testing

There is any way to mock the redis connection or use the django cache on memory for testing?

We are running unit tests on a machine were we don't have redis installed and I would like to setup the library so we can run the tests.

Any suggestion?

Unsubscribe or close

Is there a way to unsubscribe, change channel or close a connection ?
Something like:

var ws4redis = WS4Redis({
    uri: '{{ WS_URI }}foobar?subscribe-broadcast',
    receive_message: receiveMessage,
    heartbeat_msg: {{ WS4REDIS_HEARTBEAT }}
});
ws4redis.set_uri('{{ WS_URI }}other_channel?subscribe-broadcast');
ws4redis.close();

Install with South

According to the doc I guess ws4redis needs some tables.
As I'm using South, how can I generate the migration ?
Should I add a SOUTH_MIGRATION_MODULES ? Something like

SOUTH_MIGRATION_MODULES = {
    #...
    'ws4redis': 'myproject.migrations',
}

?

Update documentation for sending message

The follow code broke between 0.4.1 and 0.4.2. Is it possible to fix the documentation or keep both interfaces in the code?

from ws4redis.publisher import RedisPublisher

redis_publisher = RedisPublisher(facility='foobar', broadcast=True)

# and somewhere else
redis_publisher.publish_message('Hello World')
/home/vagrant/acc/env/local/lib/python2.7/site-packages/ws4redis/redis_store.pyc in publish_message(self, message, expire)
     94         expire = expire is None and self._expire or expire
     95         if not isinstance(message, RedisMessage):
---> 96             raise ValueError('message object is not of type RedisMessage')
     97         for channel in self._publishers:
     98             self._connection.publish(channel, message)

ValueError: message object is not of type RedisMessage

How to get a list of users subscribed to a channel

Hi, I'm trying to implement a simple multi room chat room.

Currently I'm subscribing to a channel like this:

var room_id = 27
uri: uri + room_id + '?subscribe-broadcast&publish-broadcast&echo',

Only registered Django users can use the chat.

How can I get a list of all the users connected to a channel to generate a user list?

Hope you can help me, thanks

Please test RC for 0.3.0

Version 0.3.0rc1 is currently available on GitHub.
Please also check for spelling and grammatical errors in the updated docs.

Jacob

KeyError: 'HTTP_SEC_WEBSOCKET_KEY'

Hi,

I've deployed successfully django-websocket-redis, working fine. Good job!

The issue here are with certain client, specifically Safari on iOS 5.1 (which is claimed to support WebSockets) raise the error:

Traceback (most recent call last):

File "/usr/local/lib/python2.7/dist-packages/ws4redis/wsgi_server.py", line 72, in call
websocket = self.upgrade_websocket(environ, start_response)

File "/usr/local/lib/python2.7/dist-packages/ws4redis/uwsgi_runserver.py", line 52, in upgrade_websocket
uwsgi.websocket_handshake(environ['HTTP_SEC_WEBSOCKET_KEY'], environ.get('HTTP_ORIGIN', ''))

KeyError: 'HTTP_SEC_WEBSOCKET_KEY'

Request repr() unavailable.

Any hints? Deployed with uwsgi with gevent support, behing nginx.

I can reproduce the issue regularly with the ipad above..

Thanks.

doesn't work with autobahn client

Hi i've tried using django-websocket-redis with a python client based on autobahn. I get this error on "./manage.py runserver":
Other Exception:
Traceback (most recent call last):
File "/home/luogni/.virtualenvs/fyc/src/django-websocket-redis/ws4redis/wsgi_server.py", line 78, in call
message = websocket.receive()
File "/home/luogni/.virtualenvs/fyc/src/django-websocket-redis/ws4redis/websocket.py", line 208, in receive
raise e
error: [Errno 104] Connection reset by peer
Traceback (most recent call last):
File "/usr/lib/python2.7/wsgiref/handlers.py", line 86, in run
self.finish_response()
File "/usr/lib/python2.7/wsgiref/handlers.py", line 127, in finish_response
self.write(data)
File "/usr/lib/python2.7/wsgiref/handlers.py", line 215, in write
self._write(data)
File "/usr/lib/python2.7/socket.py", line 324, in write
self.flush()
File "/usr/lib/python2.7/socket.py", line 303, in flush
self._sock.sendall(view[write_offset:write_offset+buffer_size])
error: [Errno 32] Broken pipe

The error is clear if we look at autobahn log:
2014-03-15 12:04:11+0100 [EchoClientProtocol,client] received HTTP status line in opening handshake : HTTP/1.0 101 Switching Protocols
2014-03-15 12:04:11+0100 [EchoClientProtocol,client] received HTTP headers in opening handshake : {u'content-length': u'0', u'upgrade': u'websocket', u'sec-websocket-accept': u'SBYEiAu41QkehGQrEfRmFxPpPXM=', u'sec-websocket-version': u'13', u'server': u'WSGIServer/0.1 Python/2.7.3', u'connection': u'Upgrade', u'date': u'Sat, 15 Mar 2014 11:04:11 GMT'}
2014-03-15 12:04:11+0100 [EchoClientProtocol,client] failing WebSocket opening handshake ('Unsupported HTTP version ('HTTP/1.0')')

It seems autobahn does a HTTP/1.1 request and django replies with a HTTP/1.0 switching protocol reply. Autobahn doesn't like it nd close the connection. I don't know if this is a django/ws4redis/autobahn/mysetting bug so please forgive me if i filed it on the wrong project! I've tried to remove the HTTP/1.1 check on autobahn and everything works fine but looking at google it seems websocket upgrade header requires http1.1.

receive_message fired when connected

I have this code:

        var ws4redis = WS4Redis({
            uri: '{{ WEBSOCKET_URI }}table?subscribe-broadcast',
            receive_message: function(data) {
                console.log('new item');
            },
            heartbeat_msg: {{ WS4REDIS_HEARTBEAT }}
        });

but this log is showing when load page for first time, or when user reconnect :/
How can i make this work only on ws message?

Cannot override WS4REDIS_EXPIRE in tests

I'm testing an application where the eventual result of a POST to the server is a broadcast. I do not care about message persistence, so I set WS4REDIS_EXPIRE = None in my settings.
I discovered that if you do not have message persistence then the testing method you use in chatserver will hang as the websocket waits for the first message.
Here is (paraphrased) what I have attempted:

    def test_post_broadcasts(self):
        with self.settings(WS4REDIS_EXPIRE=10):
            response = self.client.post('/post_url/')
        websocket_url = self.websocket_base_url + u'/ws/update?subscribe-broadcast'
        ws = create_connection(websocket_url)
        self.assertTrue(ws.connected)
        result = ws.recv()
        self.assertEqual(result, self.message)
        ws.close()
        self.assertFalse(ws.connected)

This didn't work. After further reading I discovered that you store _expire in the RedisStore class so the modification to settings isn't propagated. I could change this code (redis_store.py:22):

    expire = expire is None and self._expire or expire

to something more like:

    expire = settings.get('WS4REDIS_EXPIRE', expire)

but then I noticed that you are using your own settings file, which would also prevent the changes from propagating. It appears to just be a wrapper for some defaults. Can it be eliminated? That would be one possible resolution for this issue, and I'd be happy to make a pull request out of it.

Use with Gunicorn

Is it possible to use django-websocket-redis with Gunicorn instead of uWSGI?

no PONG received in 3 seconds !!!

Found this in my uwsgi-log:

[uwsgi-websocket] no PONG received in 3 seconds !!!
[2014-03-05 21:54:10,398 wsgi_server] WARNING: WebSocketError:
Traceback (most recent call last):
  File "/web/production/virtualenvs/conference/src/django-websocket-redis/ws4redis/wsgi_server.py", line 75, in __call__
    websocket.flush()
  File "/web/production/virtualenvs/conference/src/django-websocket-redis/ws4redis/uwsgi_runserver.py", line 34, in flush
    self.receive()
  File "/web/production/virtualenvs/conference/src/django-websocket-redis/ws4redis/uwsgi_runserver.py", line 31, in receive
    raise WebSocketError(e)
WebSocketError: unable to receive websocket message

Old message being sent on every reload of the page

I'm publishing a simple message like the docs suggests

publisher = RedisPublisher(facility='main', broadcast=True)
publisher.publish_message(RedisMessage('test'))

But every time I reload the page the onmessage function is triggered without sending a new message. I've only tested this using the "development mode" desribed here. That is, I'm running the server directly with python manage.py runserver.

Is this suppose to happen? I'm temporarily working around it by calling redis-cli FLUSHALL with the python subprcoess module on every page load. I'm using Django 1.5 and ws4redis 0.4.2

RedisMessage definition is missing in pypi package

I see the RedisMessage definition here if I clone the project.

The issue I found is that if you 'pip install django-websocket-redis' and look in w4rediss/redis_store.py you won't find RedisMessage.

I see that pip installs from https://pypi.python.org/pypi/django-websocket-redis/0.4.1 and have downloaded the gz file from there and see that the redis_store.py in that package is missing the RedisMessage definition.

It seems that the gz doesn't match exactly what is in the github project.

“subscribe-user” fails when run on nginx+wsgi with django-websocket-redis

I have an instance of django-websocket-redis, installed from pip and configured according to the instructions, that is, I have an instance of nginx and two instances of uwsgi, configured to communicate with nginx via websockets.

Everything seems to work fine when I use websockets with the broadcast option, i.e.

RedisPublisher(faculty=x,broadcast=true).publish_message(RedisMessage(y))

and

ws://{{ host }}/ws/x?subscribe-broadcast&echo

However, when I try to send messages to specific users, i.e.

RedisPublisher(faculty=p, users=[u]).publish_message(RedisMessage(q))

and

ws://{{ host }}/ws/p?subscribe-user&echo (being logged in as user u)

then I get no message at all.

This latter setup works when the django ./manage.py runserver is used instead of nginx + uwsgi.

Could anyone explain what could be the reason of this discrepancy, and how I could possibly fix it?

Thanks in advance

django.contrib.messages doesn't work

I imported this package into my django(1.6.5) project, everything works well, but I found django.contrib.messages doesn't work.
In views.py, I call mssages.add_message() function to send a message and in HTML, I use {% if messages %} to read it.
If I revert settings.py and wsgi.py to history version without this package, messages works well.

Could you give me an idea to fix this problem?
Thank you!

Track websockets that are closed by the browser

Hi jrief,

Great job with this project. Are you planning to implement in the future for a way to tell Django when a socket is closed from the client/browser side? I am trying to keep track of the django users that have a websocket connection open at any given time.

Thanks

[uwsgi-websocket] no PONG received in 3 seconds

Implementing ws4redis 0.4.1 with the nginx (1.1.19) and uwsgi (1.9.20) configuration, I see the following in my uwsgi log each time I try to initiate a connection from the browser:
[uwsgi-websocket] no PONG received in 3 seconds !!!
[pid: 928|app: 0|req: 1/1] 0.0.0.0 () {38 vars in 715 bytes} [Wed May 14 16:34:12 2014] GET /ws/foobar?subscribe-broadcast => generated 2 bytes in 8421 msecs (HTTP/1.1 101) 4 headers in 182 bytes (3 switches on core 19)

This is the uwsgi startup call:
uwsgi --http-socket /tmp/web.socket --gevent 20 --http-websockets --workers=2 --master --module wsgi_websocket --chmod-socket=664 --uid www-data --gid www-data --daemonize /var/log/uwsgi/websocket

It's log on boot:
*** Starting uWSGI 1.9.20 (64bit) on [Wed May 14 20:33:57 2014] ***
compiled with version: 4.6.3 on 14 May 2014 19:55:39
os: Linux-3.2.0-60-virtual #91-Ubuntu SMP Wed Feb 19 04:13:28 UTC 2014
nodename: ip-
machine: x86_64
clock source: unix
pcre jit disabled
detected number of CPU cores: 1
current working directory: /
detected binary path: /opt/bitnami/python/bin/uwsgi
setgid() to 33
setuid() to 33
your processes number limit is 4592
your memory page size is 4096 bytes
detected max file descriptor number: 1024

  • async cores set to 20 - fd table size: 1024
    lock engine: pthread robust mutexes
    thunder lock: disabled (you can enable it with --thunder-lock)
    uwsgi socket 0 bound to UNIX address /tmp/web.socket fd 3
    Python version: 2.7.6 (default, Apr 23 2014, 19:19:30) [GCC 4.1.2 20070626 (Red Hat 4.1.2-14)]Python main interpreter initialized at 0x15a7820
    python threads support enabled
    your server socket listen backlog is limited to 100 connections
    your mercy for graceful operations on workers is 60 seconds
    mapped 808032 bytes (789 KB) for 40 cores
    *** Operational MODE: preforking+async ***
    WSGI app 0 (mountpoint='') ready in 1 seconds on interpreter 0x15a7820 pid: 924 (default app)
    *** uWSGI is running in multiple interpreter mode ***
    spawned uWSGI master process (pid: 924)
    spawned uWSGI worker 1 (pid: 928, cores: 20)
    *** running gevent loop engine [addr:0x484070] ***spawned uWSGI worker 2 (pid: 929, cores: 20)

The websocket module:
import os
import gevent.monkey
import redis.connection
redis.connection.socket = gevent.socket
os.environ.update(DJANGO_SETTINGS_MODULE='app.settings')
from django.http import BadHeaderError
from ws4redis.uwsgi_runserver import uWSGIWebsocketServer
application = uWSGIWebsocketServer()

And the javascript: (from the example)
var ws = new WebSocket('ws:///ws/foobar?subscribe-broadcast&publish-broadcast&echo');

This is the same error as issue 10 (#10) and the connection is indeed dropped when an IOError is caught in flush()

Question: simple chat system

My goal is to create a simple chat system that stores messages inside the database. Requirements:

  1. only 2 users inside the room
  2. user messages have to be stored

@jrief After reading docs and your comments, especially this one #9 (comment), I undestand that your library might not be an appropriate one in my case.

Using your library I see only two ways to do a chat system:

  1. Run an extra script that will monitor changes (user messages) inside a redis database.
  2. Store messages inside a database during socket-to-socket communication. For instance, in your example I can store messages during AJAX calls:
    https://github.com/jrief/django-websocket-redis/blob/master/examples/chatserver/views.py#L29

Can you give an advice how I can create a chat system?
I would appreciate any advice you can give me. Thanks.

Production deployment - nginx & uWSGI

I've been able to successfully use this great library with my dev server.
Now I'm trying to install a server environment on my local machine (before the true one).
I configured 2 vassals in emperor mode for both my project and its websocket applications.
The one for my project works fine, but I seem to receive only HTTP requests on the websocket one (invalid request block size: 21573 (max 4096))
Here is my websocket vassal:

[uwsgi]

chdir           = /my/project/path
module          = wsgi_websocket
virtualenv      = /my/virtualenv/path

workers         = 2
socket          = /tmp/%n.sock
gevent          = 100
http-websockets = true
vacuum          = true

So my current issue is my websocket receives only invalid request block size: 21573 (max 4096) for each ws: queries
Could somebody help me to debug that ?

uWSGI as dependency is a problem

In my use case uwsgi listed in "install_requires" of setup.py is a problem.
I'm running uwsgi apps in emperor mode, and uwsgi is installed system-wide(or inside some custom virtualenv).
You can remove uwsgi from "install_requires" so it will not install uwsgi by default into virtualenv.
Then can be added:

    extras_require={
        'uwsgi':  ['uWSGI>=1.9.20'],
    }

and if uwsgi is needed too, it can be installed by command:
pip install django-websocket-redis[uwsgi]

Chat Example: Trouble using on addresses other than localhost

I run django in a virtualbox, and start my server like:

``python manage.py runserver 0.0.0.0`

Then I browse to 192.168.1.101 and view my django app, however with the chat example the websocket part doesn't work - the chrome debug output is pasted below (note - using a browser in the virtual machine does work).

Log

connection broken (index):54
connection closed (index):61
Trying to get configured shortcut getDocumentSelection.js:51
Array[2]
 getDocumentSelection.js:47
Port name deliciousBookmark getDocumentSelection.js:27
Array[2]
 getDocumentSelection.js:31
Array[2]
 getDocumentSelection.js:35
connection broken (index):54
connection closed (index):61
connection broken (index):54
connection closed (index):61
connection broken (index):54
connection closed (index):61
connection broken (index):54
connection closed (index):61
connection broken (index):54
...[snip]

Connects and Immediately Closes

I've used DWR a few times in production and setup has always been a breeze.... But this time, for some reason I'm stuck.

The short version is that in the browser console, I see the correct ws URL being connected to, followed by Connected! and immediately followed by Connection Closed. The JS script then retries, connects and is immediately disconnected. This loop continues ad infinitum.

I've tried everything. I've had help from the #uwsgi irc channel and everyone said my setup is correct. I've used - and am still using - the exact same setup in a few production sites. The only difference is that this site is hosted on AWS EC2, but not behind ELB.

Running locally under run server, all is well. I'm getting absolutely nothing in any django logs (I see the WS4Redis wsgi_server logs to django.request) and the uwsgi logs show the connection as being successful.

I'm sure I've been through everything. But I'm probably missing something obvious. If anyone can help put me straight, I'd be really grateful!!

Firefox can't establish a connection to the server at ws://172.16.52.136/ws/electricity_usage?subscribe-broadcast.

Hi there,

I started using your project in my own project earlier this week. I've only been able to get it working in debug mode using Django's runserver.

I've placed my Django application behind two uWSGI instances, as suggested in your documentation here: http://django-websocket-redis.readthedocs.org/en/latest/running.html#django-with-websockets-for-redis-behind-nginx-using-uwsgi

In my particular case, I have nginx running in a Docker container, redis running in another container, with my Django + Websockets + uWSGI instances running in a 3rd container. Logically, this is as if I am running these 3 services on 3 different machines.

The configuration is mostly correct, since I'm able to connect to the server as expected. Navigating to http://my_local_ip/ws/ in Firefox yields the message "Client does not wish to upgrade to a websocket", which again, is expected.

When I navigate to a page, the Websockets try to connect, but eventually timeout with the following output in my developer's console:

"Connecting to ws://172.16.52.136/ws/electricity_usage?subscribe-broadcast"
GET http://172.16.52.136/ws/electricity_usage [HTTP/1.1 101 Web Socket Protocol Handshake 8015ms]
Firefox can't establish a connection to the server at ws://172.16.52.136/ws/electricity_usage?subscribe-broadcast. dashboard:698
"Websocket connection is broken!" dashboard:724
"Connection closed"

In the logs for my uWSGI instances, I see the following messages:

[pid: 18|app: 0|req: 4/6] 172.16.52.1 () {42 vars in 777 bytes} [Tue Apr  1 21:03:55 2014] GET /dashboard/ => generated 45418 bytes in 12 msecs (HTTP/1.1 200) 3 headers in 102 bytes (1 switches on core 0)
[uwsgi-websocket] "GET /ws/electricity_usage?subscribe-broadcast" (172.17.0.4) no PONG received in 3 seconds !!!
[pid: 23|app: 0|req: 22/27] 172.17.0.4 () {48 vars in 896 bytes} [Tue Apr  1 21:04:27 2014] GET /ws/electricity_usage?subscribe-broadcast => generated 2 bytes in 8013 msecs (HTTP/1.1 101) 4 headers in 183 bytes (3 switches on core 999)
[uwsgi-websocket] "GET /ws/gas_usage?subscribe-broadcast" (172.17.0.4) no PONG received in 3 seconds !!!

I'm not sure how I can debug this further. I don't believe it's an issue with Redis connectivity, since I see errors crop up in the uWSGI logs if I take Redis down.

Help is appreciated. Thanks!

Missing 'six' dependency

It looks like there is a dependency on the six package in the file ws4redist/websocket.py

It is a minor thing and easily fixed but it comes up when integrate django-websocket-redis and runserver.

django.core.exceptions.ImproperlyConfigured: WSGI application 'ws4redis.django_runserver.application' could not be loaded; could not import module 'ws4redis.django_runserver': No module named six

It might be good if six was a dependency and was installed automatically when you pip install django-websocket-redis.

Werzeug incompatibility

Can't get Werkzeug to run cleanly with this, websocket connection keeps breaking when running
./manage.py runserver_plus

Werkzeug==0.9.6
django-extensions==1.3.8
django-websocket-redis==0.4.1

[2014-07-29 22:04:57,421 django_runserver] DEBUG: WebSocket request accepted, switching protocols [2014-07-29 22:04:57,422 wsgi_server] ERROR: Other Exception: Traceback (most recent call last): File "/mnt/hgfs/conversation-server/ws4redis/wsgi_server.py", line 72, in __call__ websocket = self.upgrade_websocket(environ, start_response) File "/mnt/hgfs/conversation-server/ws4redis/django_runserver.py", line 56, in upgrade_websocket six.get_method_self(start_response).finish_content() AttributeError: 'function' object has no attribute 'im_self' [2014-07-29 22:04:57,424 wsgi_server] INFO: Finish long living response with status code: 192.168.85.1 - - [29/Jul/2014 22:04:57] "GET /ws/foobar?subscribe-broadcast&publish-broadcast&echo HTTP/1.1" 101 -

Messages not pushed to clients within a celery task

Hello,

I have run into an issue with django-websocket-redis and django-celery. I am not sure where the issue lies but this code:

@app.task(name='notify-driver')
def notify_driver(reservation_id):
"""
Task used to notify driver of a pickup by sms or call, depending on configuration specified in the web
and put a new task in the queue to check later the reservation state. see check_reservation

:Param: reservation_id -- Id of the reservation made
"""
from celery.contrib import rdb
rdb.set_trace()

reservation = verify_reservation_not_confirmed(reservation_id)

if reservation is None:
    return

driver_phone_number = reservation.assignedTo.cell_number

try:
    if value_for_key(settings.MSG_TO_DRIVER_MESSAGE_TYPE_PROPERTY_NAME,
                     settings.DEFAULT_MESSAGE_TO_DRIVER_TYPE) == settings.SMS_MESSAGE_TYPE:
        send_sms_to_driver(reservation)
    else:
        send_voice_message_to_driver(driver_phone_number, reservation.id)
except Exception:
    pass

reservation.requestStatus = ReservStatus.objects.get(description=ReservStatus.DRIVER_NOTIFIED)

reservation.save()

# If this was a booking done through the web site then we'll try to reach the passenger.
if reservation.passenger.devOS == Passenger.WEB_DEV_OS and reservation.referenceNumber:
    redis_manager.publish_message(reservation.referenceNumber, {'status': 'FIRST_CONFIRMATION_SENT'})

check_reservation.apply_async([reservation.id], countdown=value_for_key(
    "%s__int" % settings.CANCEL_RESERVATION_COUNTDOWN_PROPERTY_NAME,
    settings.DEFAULT_WAIT_TO_CANCEL_PICKUP))

If I comment out the line reservation.save() the message get pushed to the client. I will appreciate any help on the matter. Please let me know if you need more information.

Bests,

Yoanis.

Is RabbitMQ support possible?

To save time, I will just quot the email exchange I had with jrief on this:

Adam Jorgensen to jacob.rief 

Hi there

I was very excited to discover your Django Websockets library as I am probably going to need to make use of it soon. The catch is, we use RabbitMQ rather than Redis. 

In your opinion, how hard would it be to implement RabbitMQ support? Would you object to me working the project if/when it becomes necessary for me to implement this?

Thanks
Jacob Rief to me 

please re-use the issue tracker to ask this questions, so that it is visible to the public

I never used RabbitMQ, so I can't answer that question.

However, some must features are:

1. the API for accessing RabbitMQ must support gevent/greenlets or any other kind of coroutines, since ws4redis runs in one single process/thread.

2. the message queue must also support some kind of temporary storage, similar to memcached, or any other key-value store, otherwise you can't use ws4redis for all kind of purposes.
Adam Jorgensen to Jacob 

Hi

Thanks for your reply. Based on what you've said, it seems that RabbitMQ support is not possible then as RabbitMQ does not function as a KV store.

I can still create an issue but I do not think it is necessary. It will probably be easier for me to just install and run Redis :-)

Thanks
Jacob Rief to me 

Hi

Thanks for your reply. Based on what you've said, it seems that RabbitMQ support is not possible then as RabbitMQ does not function as a KV store.

I can still create an issue but I do not think it is necessary. It will probably be easier for me to just install and run Redis :-)

please do! You will not remain the only one asking this

Excluding message sender

Is there a way to exclude the sender from the message broadcast subscribed to the same context ?
Would it be interesting to have this option in the audiences ?

Segmentation Fault

I'm trying to get django-websocket-redis working under nginx as suggested in the docs. My django site works fine, but my websocket-related workers keep crashing and no messages are being received by any client. I would be very grateful for any help!!

I'm using the following settings for the websocket-uwsgi.ini file:

[uwsgi]
http-socket = 127.0.0.1:40001
chdir = /home/web/shift/django_project
pythonpath = ../venv/lib/python2.7/site-packages
uid = www_vagrant
master = true
env = DJANGO_SETTINGS_MODULE=core.settings
module = core.websocket_wsgi
processes = 4
threads = 2
http-websockets = true
gevent = 1000

My core.websocket_wsgi looks like this:

import os
from gevent import socket
import redis.connection
from os.path import abspath, dirname
from sys import path
from ws4redis.uwsgi_runserver import uWSGIWebsocketServer

SITE_ROOT = dirname(dirname(abspath(__file__)))
path.append(SITE_ROOT)

redis.connection.socket = socket
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "core.settings")
application = uWSGIWebsocketServer()

The error I get from the uwsgi process:

!!! uWSGI process 6371 got Segmentation Fault !!!
*** backtrace of 6371 ***
uwsgi(uwsgi_backtrace+0x29) [0x46a019]
uwsgi(uwsgi_segfault+0x21) [0x46a1a1]
/lib/x86_64-linux-gnu/libc.so.6(+0x364a0) [0x7f29b9a224a0]
uwsgi(wsgi_req_setup+0x41) [0x41ef21]
uwsgi(py_uwsgi_gevent_main+0x4a) [0x48f04a]
/usr/lib/libpython2.7.so.1.0(PyObject_Call+0x53) [0x7f29ba126053]
../venv/lib/python2.7/site-packages/gevent/core.so(+0x12133) [0x7f29b70c4133]
../venv/lib/python2.7/site-packages/gevent/core.so(ev_invoke_pending+0x57) [0x7f29b70be397]
../venv/lib/python2.7/site-packages/gevent/core.so(ev_run+0x364) [0x7f29b70e0854]
../venv/lib/python2.7/site-packages/gevent/core.so(+0x2f12b) [0x7f29b70e112b]
/usr/lib/libpython2.7.so.1.0(PyEval_EvalFrameEx+0x53a5) [0x7f29ba0815d5]
/usr/lib/libpython2.7.so.1.0(PyEval_EvalCodeEx+0x855) [0x7f29ba0416b5]
/usr/lib/libpython2.7.so.1.0(+0x5c86d) [0x7f29ba04186d]
/usr/lib/libpython2.7.so.1.0(PyObject_Call+0x53) [0x7f29ba126053]
/usr/lib/libpython2.7.so.1.0(+0x12539f) [0x7f29ba10a39f]
/usr/lib/libpython2.7.so.1.0(PyObject_Call+0x53) [0x7f29ba126053]
/usr/lib/libpython2.7.so.1.0(PyEval_CallObjectWithKeywords+0x47) [0x7f29ba1269a7]
../venv/lib/python2.7/site-packages/greenlet.so(+0x3a66) [0x7f29b8817a66]
../venv/lib/python2.7/site-packages/greenlet.so(+0x33b0) [0x7f29b88173b0]
../venv/lib/python2.7/site-packages/greenlet.so(+0x3f36) [0x7f29b8817f36]
/usr/lib/libpython2.7.so.1.0(PyObject_Call+0x53) [0x7f29ba126053]
/usr/lib/libpython2.7.so.1.0(PyEval_CallObjectWithKeywords+0x47) [0x7f29ba1269a7]
/usr/lib/libpython2.7.so.1.0(+0x7c9ac) [0x7f29ba0619ac]
/usr/lib/libpython2.7.so.1.0(PyObject_Call+0x53) [0x7f29ba126053]
/usr/lib/libpython2.7.so.1.0(PyEval_EvalFrameEx+0x3fcd) [0x7f29ba0801fd]
/usr/lib/libpython2.7.so.1.0(PyEval_EvalCodeEx+0x855) [0x7f29ba0416b5]
/usr/lib/libpython2.7.so.1.0(PyEval_EvalFrameEx+0x5420) [0x7f29ba081650]
/usr/lib/libpython2.7.so.1.0(PyEval_EvalCodeEx+0x855) [0x7f29ba0416b5]
/usr/lib/libpython2.7.so.1.0(+0x5c86d) [0x7f29ba04186d]
/usr/lib/libpython2.7.so.1.0(PyObject_Call+0x53) [0x7f29ba126053]
/usr/lib/libpython2.7.so.1.0(+0x12539f) [0x7f29ba10a39f]
/usr/lib/libpython2.7.so.1.0(PyObject_Call+0x53) [0x7f29ba126053]
/usr/lib/libpython2.7.so.1.0(+0x141bbb) [0x7f29ba126bbb]
/usr/lib/libpython2.7.so.1.0(PyObject_CallMethod+0xc1) [0x7f29ba0f3b71]
uwsgi() [0x48ea6e]
uwsgi(uwsgi_ignition+0x11c) [0x46a5bc]
uwsgi(uwsgi_worker_run+0x3af) [0x46ef1f]
uwsgi(uwsgi_run+0x3ab) [0x46f34b]
uwsgi() [0x41e40e]
/lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0xed) [0x7f29b9a0d76d]
uwsgi() [0x41e439]
*** end of backtrace ***
DAMN ! worker 2 (pid: 6369) died :( trying respawn ...
Respawned uWSGI worker 2 (new pid: 6373)
DAMN ! worker 4 (pid: 6371) died :( trying respawn ...
Respawned uWSGI worker 4 (new pid: 6374)
DAMN ! worker 3 (pid: 6372) died :( trying respawn ...
Respawned uWSGI worker 3 (new pid: 6375)

This operation would block forever

I keep getting this error, every other time I refresh this page. I am fairly new to gevent to figure out what is going on. The code that I am implementing is very similar to the one posted in the example. I am using the latest version of django-websockets-redis ( v0.2.1 installed via pypi). I am happy post my code if needed. I am currently testing this in development. (python manage.py runserver)

 [03/Jan/2014 08:54:58] "GET /ws/foobar?subscribe-session&subscribe-user&publish-user&subscribe-broadcast HTTP/1.1" 101 0
 Other Exception:
 Traceback (most recent call last):
 File "build/bdist.linux-x86_64/egg/ws4redis/wsgi_server.py", line 73, in __call__
    redis_store.send_persited_messages(websocket)
 File "build/bdist.linux-x86_64/egg/ws4redis/store.py", line 63, in send_persited_messages
    message = self._connection.get(channel)
 File "/env/local/lib/python2.7/site-packages/redis/client.py", line 705, in get
    return self.execute_command('GET', name)
 File "/env/local/lib/python2.7/site-packages/redis/client.py", line 461, in execute_command
    return self.parse_response(connection, command_name, **options)
 File "/env/local/lib/python2.7/site-packages/redis/client.py", line 471, in parse_response
    response = connection.read_response()
 File "/env/local/lib/python2.7/site-packages/redis/connection.py", line 334, in read_response
    response = self._parser.read_response()
 File "/env/local/lib/python2.7/site-packages/redis/connection.py", line 188, in read_response
    buffer = self._sock.recv(4096)
 File "/env/local/lib/python2.7/site-packages/gevent/socket.py", line 392, in recv
    self._wait(self._read_event)
 File "/env/local/lib/python2.7/site-packages/gevent/socket.py", line 298, in _wait
    self.hub.wait(watcher)
 File "/env/local/lib/python2.7/site-packages/gevent/hub.py", line 341, in wait
    result = waiter.get()
 File "/env/local/lib/python2.7/site-packages/gevent/hub.py", line 568, in get
    return self.hub.switch()
 File "/env/local/lib/python2.7/site-packages/gevent/hub.py", line 331, in switch
    return greenlet.switch(self)
 LoopExit: This operation would block forever

ws4redis for session

Hi,
I would like to know how can I implement this in a session base (ie. subscribe-session, publish-session). I could get the broadcast and groups going but apparently the idea of having users coming to my app, specified their interest in joining the session, but fail to send/receive anything via ws. Please advise.

Unit test documentation broken

examples/chatserver/test_settings.py does not exist as referred to in
https://github.com/jrief/django-websocket-redis/blob/master/docs/testing.rst

cd examples && ./manage.py test chatserver --settings=chatserver.test_settings

Changing to

cd examples && ./manage.py test chatserver --settings=chatserver.tests.settings

The tests run but all fail

Traceback (most recent call last):
  File "/mnt/hgfs/powerof1/src/poo/eval/django-websocket-redis/examples/chatserver/tests/chatclient.py", line 153, in test_subscribe_session
    ws = create_connection(websocket_url, header=header)
  File "/home/michela/env/poo/local/lib/python2.7/site-packages/websocket/__init__.py", line 227, in create_connection
    websock.connect(url, **options)
  File "/home/michela/env/poo/local/lib/python2.7/site-packages/websocket/__init__.py", line 513, in connect
    self.sock.connect(address)
  File "/usr/lib/python2.7/socket.py", line 224, in meth
    return getattr(self._sock,name)(*args)
error: [Errno 111] Connection refused

Binary send support

Hello,

Despite having set binaryType when creating my websocket client-side, ws4redis doesn't seem to support sending binary data.

When I force it to use uwsgi's binary send, it works:

    def send(self, message, binary=None):
        try:
# old line
#           uwsgi.websocket_send(message)
# default to binary send     
            uwsgi.websocket_send_binary(message)
# simple attempt at using the existing parameter didn't work
#            if binary:
#                uwsgi.websocket_send_binary(message)
#            else:
#                uwsgi.websocket_send(message)
        except IOError, e:
            self.close()
            raise WebSocketError(e)

    def close(self, code=1000, message=''):
        self._closed = True

Client side js:

    var ws = new WebSocket…
    ws.binaryType = "arraybuffer";

I'm using nginx==1.6.0,
uWSGI==2.0.6,
ws4redis==0.4.2,
redis==2.10.1,
gevent==1.0,
greenlet==0.4.2,
wsaccel==0.6.2

Redis publish - Wrong number of argument for 'set'

Trying the first example to broadcast a message

from ws4redis.publisher import RedisPublisher
redis_publisher = RedisPublisher(facility='foobar', broadcast=True)
redis_publisher.publish_message('Hello World')

I'm actually receiving the message from subscribed clients but I'm getting this error:

wrong number of arguments for 'set' command
[...]
Exception location my_virtualenv/local/lib/python2.7/site-packages/redis/connection.py in read_response, line 344

(traced from the publish_message() call)

My versions:

Django==1.6.2
django-websocket-redis==0.4.0
redis==2.9.1

Can someone help me to debug that ?

Advice on sending client info back to server

In another issue - #9 (and in the docs) - you mention that sending GPS data back to the server would be a good use of the ability for a client to publish a message back to the server.

Presumably it is acceptable to then trigger e.g. a celery task when the server receives that data? Can you give a very brief example of how to set a subscriber on the server side?

My goal is to send user scroll depths and certain mouse actions back to the server and I'd like to use the web sockets in order to a) avoid multiple AJAX calls per page view, and b) ensure that data is sent back before the user closes the browser. I would then use celery to store this info in order to later produce heat maps etc.

Would you say that this is a reasonable use of the web socket loop?

Thanks again!

Don not access group from the WS loop

this causes a DB access, which must be avoided from inside gevent-loops.
Its better to add that information to the session using a 'user logged in' signal handler.

How to react to messages on the server side?

Hi jrief, thank you for your project.

I seem to understand the example app and roughly, how the redis store works. However, what confuses me is how I can actually receive messages in my django app. Say, a user send something to the server without broadcasting, i.e., simply by connecting to ws = new WebSocket('{{ ws_url }}');. How would I catch that message in my django views.py (for example), do some work and eventually send back some response? All the server to client communication seems to be handled by your app.

Also: I see that there is a fork for Python 3. Are you considering merging it?

Thanks, janoliver

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.