jrief / django-websocket-redis

Websockets for Django applications using Redis as message queue
http://django-websocket-redis.awesto.com/
MIT License
896 stars 222 forks source link

How to receive data on server side? #175

Open david-de2 opened 8 years ago

david-de2 commented 8 years ago

By the documentation I understand well how to send data from server through websockets, but I don't understand how to receive.

Basically I have client app and I want to send periodically from the client to server status. I don't understand how I can handle receiving messages from the client on server side? What URL I should use on client? Do I need to create django view add this into url.py and receive by someway through sockets?

Sorry for grammar mistakes.

jrief commented 8 years ago

It depends what you want to do. Normally its easier to do it via a classic Ajax request, but consider the overhead of a TCP/IP handshake and teardown. Sending data via the websocket is possible, but then you may either broadcast it via Redis to other ws-connectors or keep it in a Redis data-bucket. A normal Django view then may look into that bucket and do whatever it likes.

Please close, if everything is clear.

david-de2 commented 8 years ago

I going to get status updates (what posts user exploring at the moment). So, I going to storage this posts in Redis. Like: PostId => UserIds. And when some post updated, I'll get all users for this post and send them notification.

So, basically I need to receive data on server side put it in reds. And send updates to clients. By the docs I see that I can only send data. (Just to avoid excess IO file operations and DB calls - ,) but what about redis? If my purpose of using redis is there any way to use django-websocket-redis for receiving messages?

david-de2 commented 8 years ago

I would happy to use RESTful interface to send updates from client. But I going to storage currently visible entities on screen. It would generate a lot of requests to keep server updated.

david-de2 commented 8 years ago

I would glad to do my own push for this project by adding this functionality if it doesn't exists. We could restrict user to use only redis, do you think it makes sense?

david-de2 commented 8 years ago

Could you please tell how can I receive data to put it in Redis data-bucket? Thanks.

nanuxbe commented 8 years ago

@david-de2 if you really want to receive data from your websocket, you can use a custom subscriber http://django-websocket-redis.readthedocs.org/en/latest/installation.html?highlight=subscriber . This is quite dangerous as you have to make sure to wrap any custom code in a try/except but "all you have to do" is override publish_message with something like that

class MySubscriber(RedisSubscriber):
    def publish_message(self, message, expire=None):
        try:
            # custom code goes here
        except Exception:
            # I mean it, catch everything, log it if needed but do catch everything
            pass
        return super(MySubscriber, self).publish_message(message, expire)

Keep in mind that your custom code also has the potential to considerably slow down your websocket so don't do any heavy lifting there. Creating a celery task might be your best option here.

david-de2 commented 8 years ago

nanuxbe, thank you for your response. Are you suggesting to even not put data in redis-bucket, I should do it through celery task, right?

nanuxbe commented 8 years ago

You can put data in a redis bucket (if you configured celery to use redis, data will go through redis anyways) it's a matter of choice. Just don't do any database inserts or things like that which would considerably slow down your your application.

I was thinking of a celery task because the case where I had to use this was for a high load logging API which had to receive thousands of JSON messages a second and they needed to be parsed and inserted in a db.

david-de2 commented 8 years ago

Thank you for your help!

jrief commented 8 years ago

@nanuxbe thanks for explaining this so well. BTW, about celery: I recently discovered that if you use uWSGI, there is a built-in cron and timer. This means that if, for example you use django-post_office, you can send queued mail every 5 seconds by adding

from django.core.management import call_command
import uwsgidecorators

@uwsgidecorators.timer(5)
def send_queued_mail(num):
    call_command('send_queued_mail')

to your wsgi.py file. I therefore do not need celery anymore.

nanuxbe commented 8 years ago

@david-de2 you're welcome

@jrief you're welcome as well. And thanks for the tip I didn't know about that. I think I'm going to be updating a few websites :-)

david-de2 commented 8 years ago

I'm sorry, I can't realise how to get current user (or request) in overridden publish_message of RedisSubscriber. Could you please explain this?

nanuxbe commented 8 years ago

@david-de2 you don't have access to the request at that stage of the cycle (due to how websockets work, ie: the connection is opened once and re-used after that). You do have access to the request during the initial setup of the connection in set_pusub_channels https://github.com/jrief/django-websocket-redis/blob/11b2b0f3981c088b1975e524383ae6132132c3e6/ws4redis/subscriber.py#L23 . If I wanted to have access to the user I would override this method and store the user id (once again, websockets are meant to be lightweight and fast, store as little as you can)

Something along the lines of:

    user_id = None

    def set_pubsub_channels(self, request, channels):
        super(MySubscriber, self).set_pubsub_channels(request, channels)
        try:
            this.user_id = request.user.id
        except AttributeError:
            # Once again if anything were to happen inside your code,
            # it would prevent the connection to work.
            # So make sure to catch everything. 
            pass

I have never tried this approach but that's where I'd start if this was something I wanted to implement.