Django: уведомления JSON с использованием Redis PubSub, Node.js & Socket.io

Я столкнулся с этой статьей: http://maxburstein.com/blog/realtime-django-using-nodejs-and-socketio/

Который управлял мной в несколько правильном направлении.

В настоящее время у меня есть передний конец iOS и задний конец Django. Я использую Gunicorn для передачи данных в приложение переднего конца. Связь между моим приложением iOS и моей поддержкой основана на REST. Я просто посылаю JSON туда и обратно. Я не обслуживаю никаких веб-страниц. Только ответы JSON.

Я реализовал простую модель публикации и комментариев:

class Post(models.Model):
         user = models.ForeignKey(User)
         blog = models.CharField(max_length=5000)

class Comment(models.Model):
         comment = models.CharField(max_length=140)
         user = models.ForeignKey(User)
         post_id = models.ForeignKey(Post)
         created_at = models.DateTimeField(auto_now_add=True)

Пользователи могут создавать записи в блогах, а другие пользователи могут комментировать их. Поэтому, если userX имеет сообщение в блоге и комментарии пользователя на нем. Я хотел бы уведомить userX, что userY прокомментировал его/ее сообщение.

Раньше я полагался на pyAPNS для уведомления пользователей; оболочка python, использующая Twisted для отправки уведомлений APNS, но если userX отключает push-уведомления для моего приложения, тогда пользовательский интерфейс не сможет получать уведомления в приложении. Поэтому мне не повезло.

Мне больше всего нравятся уведомления в приложении. Я все равно хотел бы, чтобы userX получал живые обновления, пока он находится в приложении.

Django может опубликовать сообщение на канал в Redis, когда пользователь делает запрос POST. Node.js будет подписаться на этот канал, и socket.io отправит его этому конкретному пользователю.

Вот урезанная версия my views.py, где создается объект комментария. Я отправляю идентификатор пользователя, который сделал комментарий, идентификатор сообщения и идентификатор пользователя, который сделал запись в блоге. Пользователь отправит запрос на этот URL с помощью json: http://example.com:8000/upload-comment/

def UploadComment(request):
         data  = json.loads(request.body)
         redis_server = redis.Redis(host='12.345.678.9', port=6379, db=0, password='mypassword')
         newComment = Comment()
         newComment.comment = data['comment']
         newComment.user_id = data['user_id']
         newComment.post_id = data['post_id']
         newComment.save() 
         PostOwner = data['post_owner_id'] #id of the blog post owner
         # Need to send a notification to PostOwner
         response_data = []
         response_data.append(
                 {'send_notifcation_to': PostOwner
                  'action': 'comment'
                  'comment_by': newComment.user.username)}
         redis_server.publish("notifications", json.dumps(response_data))
         return HttpResponse('Request Successful')

Node.js Реализация (согласно статье Максима Бурштейна выше)

var http = require('http');
var server = http.createServer().listen(4000);
var io = require('socket.io').listen(server);

И это насколько я понимаю:( Я знаю, что это довольно грустно, но у меня много вопросов. Как я могу подписаться на Node.js на удаленный сервер Redis, который я опубликовал с Django? многие клиенты могут подключиться к этому сокету? существует ли ограничение? - это сокет, созданный для каждого клиента, или каждый клиент слушает в одном и том же сокете? Могу ли я отправлять данные json через этот сокет одному конкретному клиенту? Я знаю, что это один большой мамонт почты, но мне нужна отчаянная помощь. Если бы я ничего не понимал, пожалуйста, дайте мне знать, чтобы я мог отредактировать вопрос. Спасибо!

Ответы

Ответ 1

Я бы определенно не использовал node.js для этого. Вы можете обрабатывать websockets в Python просто отлично, с Celery или gevent или что-то еще.

Просто создайте поток, который регистрируется в Redis и прослушивает новые сообщения.

Когда пользователь подключается, поместите сокет в хэш-код слабого значения, проиндексированный именем пользователя; когда приходит сообщение для них, найдите имя пользователя назначения в этом хэше и отправьте его. Слабая ценность, потому что она очищается после себя, когда пользователь отключается. Простой, действительно.

Я также отправлял новые сообщения на сервер, используя websocket вместо HTTP-PUT.

Ответ 3

Похоже, вы должны использовать что-то вроде RabbitMQ http://www.rabbitmq.com/devtools.html там node.js и многое другое.

Проверьте это, он должен поднять настроение:)

Ответ 4

Я также использую замечательную библиотеку, называемую django-websocket-redis.

Хотя он не использует gunicorn, он использует uWSGI, у которого не должно быть слишком много накладных расходов при переходе на. Просто следуйте документам, они довольно полные.

https://django-websocket-redis.readthedocs.org/en/latest/

Взяв некоторый код и реализуя его с помощью django-websockets-redis:

## Make a json representation of your model available.

class Comment(models.Model):
     comment = models.CharField(max_length=140)
     user = models.ForeignKey(User)
     post_id = models.ForeignKey(Post)
     created_at = models.DateTimeField(auto_now_add=True)

    def as_json(self):
        return dict(
        id=self.id,
        comment=self.comment,
        created_at=self.created_at.strftime('%m/%d/%Y'),
        post_id=self.post_id
        user=self.user
        )

Затем в ваших представлениях, когда вы делаете запрос, сохраните json-представление для объекта redis, на который вы подписаны... вы можете сделать это в любом месте, будь то ваш сериализатор или просмотр, но я оставлю это вам.

def UploadComment(request):
     data  = json.loads(request.body)

     ## In django-websockets-redis, this portion below is registered in the settings.py file, therefore i am commenting this out. 
     ## redis_server = redis.Redis(host='12.345.678.9', port=6379, db=0, password='mypassword')

     newComment = Comment()
     newComment.comment = data['comment']
     newComment.user_id = data['user_id']
     newComment.post_id = data['post_id']
     newComment.save() 
     PostOwner = data['post_owner_id'] #id of the blog post owner
     # Need to send a notification to PostOwner

     ## Need to turn this into a string before posting to redis
     json_comment = str(newComment.as_json())

     ## Create a facility based on the PostOwner ID. The PostOwner will always be subscribed to this channel. 
     RedisPublisher(facility=PostOwner, broadcast=True).publish_message(json_comment)

     return HttpResponse('Request Successful')

Теперь, когда у нас есть бэкэнд, о котором мы заботимся, на визе front-vis vie JavaScript ваш пользователь подпишится на Facility на основе его user.id:

jQuery(document).ready(function($) {
    var ws4redis = WS4Redis({
    uri: '{{ WEBSOCKET_URI }}{{ PostOwner.id }}?subscribe-broadcast&publish-broadcast&echo',
    receive_message: receiveMessage,
    heartbeat_msg: {{ WS4REDIS_HEARTBEAT }}
});

// receive a message though the Websocket from the server
function receiveMessage(msg) {
    //This is where you would retrieve the JSON string from the Redis server, and subsequently manipulate the DOM with appends, Toastr Notifications, Etc...
}