sudo apt-get install redis-server
pip install tornado-redis redis
перед установкой убедитесь что вы активировали виртуальное окружение
test-redis.py
#!/usr/bin/env python
import redis
print('Testing redis connection')
redis_client = redis.Redis(host='localhost', port=6379, db=0)
print("Sending message to channel")
out = redis_client.publish('chat-channel', 'my data')
print(out)
var ws = new WebSocket("ws://localhost:8888/websocket");
ws.onopen = function() {
message = {'action': 'connect', 'message': 'new connection'}
ws.send(JSON.stringify(message));
};
ws.onmessage = function (evt) {
jdata = JSON.parse(evt.data);
if( jdata['action'] == 'set_sign'){
console.log(`Set sign ${jdata['message']}`);
localStorage.setItem('chat-connection-id', jdata['message']);
} else {
$('#message_box').append('<li>'+jdata.message+'</li>');
}
}
$('#submit_button').on('click',function(e){
e.preventDefault();
data = {
'action': 'message',
'message': $('#chat_message').val()
};
ws.send(JSON.stringify(data));
});
import tornadoredis
...
class WebsocketHandler(tornado.websocket.WebSocketHandler):
def __init__(self, *args, **kwargs):
super(WebsocketHandler, self).__init__(*args, **kwargs)
self.listen_redis()
def listen_redis(self):
self.client = tornadoredis.Client()
self.client.connect()
self.client.subscribe('chat-channel')
self.client.listen(self.on_message)
При таком раскладе работать не будет т.к. процесс подписки на канал должен быть асинхронным в цикле событий Tornado. Для этого будем использовать декоратор @tornado.gen.coroutine. Он создает асинхронную корутину (генератор). Это более удобный способ организации последовательности асинхронных действий по сравнению с созданием цепи коллбеков.
Пример асинхронной корутины.
class GenAsyncHandler(RequestHandler):
@gen.coroutine
def get(self):
http_client = AsyncHTTPClient()
response = yield http_client.fetch("http://example.com")
do_something_with_response(response)
self.render("template.html")
Как видно код выглядит синхронно (без колбеков), однако выполняется асинхронно, сохраняя последовательность выполнения.
response = yield http_client.fetch("http://example.com")
После оператора yield код приостановится ожидая получить результат в response.
В нашем же случае мы асинхронно подписываемся на канал и ожидаем результата, после чего слушаем канал.
@tornado.gen.coroutine
def subscribe_redis(self):
self.client = tornadoredis.Client()
self.client.connect()
yield tornado.gen.Task(self.client.subscribe, 'chat-channel')
self.client.listen(self.on_message)
Этот же функционал можно реализовать с помощью обычных колбеков так:
class WebsocketHandler(tornado.websocket.WebSocketHandler):
def __init__(self, *args, **kwargs):
super(WebsocketHandler, self).__init__(*args, **kwargs)
self.subscribe_redis()
def listen_redis(self,rez):
self.client.listen(self.on_message)
def subscribe_redis(self):
self.client = tornadoredis.Client()
self.client.connect()
self.client.subscribe('chat-channel',self.listen_redis)
Теперь можно доработать обработчик POST формы и в нем передавать данные в redis канал.
redis_client = redis.Redis(host='localhost', port=6379, db=0)
...
class FormHandler(tornado.web.RequestHandler):
def post(self):
data = {"action": "onmessage", "message": self.get_argument('message')}
redis_client.publish('chat-channel',json.dumps(data))
self.write("OK")
Далее перепишем код клиента для отправки данных с формы AJAX запросом (без перегрузки страницы).
$('#submit_button').on('click',function(e){
e.preventDefault();
data = {
'action': 'message',
'message': $('#chat_message').val()
};
//ws.send(JSON.stringify(data));
$.post('/submit',data,function(r){
console.log(r);
})
});
Ранее мы передавали сообщение в веб сокет.
Сообщение, которое опускается в сервер из redis выглядит таким объектом.
Message(kind='message', channel='chat-channel', body='{"message": "test", "action": "onmessage"}', pattern='chat-channel')
Для доступа к параметру body используется следующий синтаксис:
message.body["message|action"]