Бібліотека socketio.
Основи Python и Django. -> Використовуємо веб-сокети з бібліотекою socketio.
Використовуємо веб-сокети з бібліотекою socketio.
Промислові інструменти для вебсокетів http://www.emqx.io/ centrifugo.
Створимо образ для веб-сервера Dockerfile-nginx.
FROM nginx:latest
RUN mkdir /app
COPY ./default.conf /etc/nginx/conf.d/default.conf
Конфігурація віртуального хоста default.conf.
server {
listen 80;
server_name localhost;
location / {
root /app;
try_files $uri /index.html;
}
}
Створимо образ для сокет-серверу Dockerfile-socketio.
FROM python:3.6
ENV PYTHONUNBUFFERED 1
RUN mkdir /app
WORKDIR /app
COPY ./requirements.txt /app/requirements.txt
RUN apt update
RUN pip install -r requirements.txt
CMD python server.py
Створимо збирач контейнерів docker-compose.yaml.
version: '3.5'
services:
socketio:
build:
context: .
dockerfile: Dockerfile-socketio
volumes:
- .:/app
container_name: socket-io-server
ports:
- 8050:5000
socketio-nginx:
build:
context: .
dockerfile: Dockerfile-nginx
volumes:
- .:/app
ports:
- 8088:80
container_name: socket-io-nginx
Встановимо клієнтські залежності.
npm init
npm install jquery socket.io @types/socket.io --save
Створимо index.html
<!DOCTYPE html>
<html>
<head><title>Socket io</title>
<script src="/node_modules/socket.io/client-dist/socket.io.min.js"></script>
<script src="/node_modules/jquery/dist/jquery.min.js"></script>
</head>
<body>
<h1>Test socketio</h1>
<button id="button">Test</button>
<script>
const socket = io('ws://localhost:8050', {transports:['websocket']});
socket.on('connect', () => {
socket.on('message', msg => {
console.log(msg);
})
});
$('#button').on('click',() => {
socket.emit('test_message',{data: 'ping'});
});
</script>
</body>
</html>
Створимо простий сокет-сервер server.py.
import eventlet
import socketio
sio = socketio.Server(cors_allowed_origins='*',async_mode='eventlet')
app = socketio.WSGIApp(sio)
@sio.event
def connect(sid, environ):
print('connect ', sid)
@sio.event
def test_message(sid, data):
print('message ', data)
sio.emit('message', {'data': 'pong'}, broadcast=True, include_self=True)
# sio.emit('my event', {'data': 'foobar'}, room=user_sid)
@sio.event
def disconnect(sid):
print('disconnect ', sid)
if __name__ == '__main__':
eventlet.wsgi.server(eventlet.listen(('', 5000)), app)
Можливості бібліотеки
Простір імен.
При коннект можна вказувати неймспейс.
ws://example.com:8000/chat
І потім із ним працювати.
@sio.event(namespace='/chat')
def my_custom_event(sid, data):
pass
Надсилання повідомлення певному клієнту
sio.emit('my event', {'data': 'foobar'}, room=user_sid)
Додавання з’єднань до кімнати
@sio.event
def begin_chat(sid):
sio.enter_room(sid, 'chat_users')
@sio.event
def exit_chat(sid):
sio.leave_room(sid, 'chat_users')
Після чого стає можливим відправлення повідомлення до кімнати.
sio.emit('my reply', data, room='chat_users', skip_sid=sid)
Одна сполука може бути додана до багатьох кімнат.
Сесія користувача.
Можна зберігати специфічні дані користувача для кожного з’єднання.
@sio.event
def connect(sid, environ):
sio.save_session(sid, {'username': 'Dima'})
@sio.event
def message(sid, data):
session = sio.get_session(sid)
Брати дані сесії можна і так:
with sio.session(sid) as session:
session['username'] = username
Взаємодія із сторонніми процесами.
Часто виникає необхідність передати дані сокет-серверу з інших програм або процесів.
І тому можна використовувати посередника як БД Redis.
pip install redis
Створимо новий контейнер.
version: '3.5'
services:
socketio-server:
...
depends_on:
- "socketio-redis"
...
socketio-redis:
image: "redis:alpine"
ports:
- "6999:6379"
Задамо менеджер повідомлень під час створення об’єкта програми сервера.
import eventlet
eventlet.monkey_patch()
mgr = socketio.RedisManager('redis://socketio-redis:6379/0')
sio = socketio.Server(cors_allowed_origins='*',async_mode='eventlet',client_manager=mgr)
socketio-redis - у докері можна користуватися іменами контейнерів для позначень їх хостів
Створимо переодичну розсилку серверного часу окремому файлі timer.py.
import socketio
import time
from datetime import datetime
mgr = socketio.RedisManager('redis://localhost:6999/0', write_only=True)
while True:
time.sleep(2)
now = datetime.now()
current_time = now.strftime("%H:%M:%S")
mgr.emit('message', data={'time': current_time})
print(current_time)
Зробимо оновлення на клієнта.
<div id="time"></div>
<script>
const socket = io('ws://localhost:8050', {transports:['websocket']});
socket.on('connect', () => {
socket.on('message', msg => {
$('#time').html(msg.time);
})
});
...
Додамо новий контейнер.
....
socketio-server:
...
command: ["python", "server.py"]
socketio-pinger:
build:
context: .
dockerfile: Dockerfile-socketio
volumes:
- .:/app
container_name: socket-io-pinger
depends_on:
- "socketio-redis"
command: ["python", "pinger.py"]
При цьому вилучивши команду запуску програми з Dockerfile-socketio і перенесемо до docker-compose.