Проблемы с производительностью Apache + Django + socket.io (длинный опрос) + прокси
Я пытаюсь настроить веб-сайт с django, apache и socket.io. Благодаря некоторым учебникам и другим вопросам, связанным со стековым потоком, мне удалось заставить все работать, кроме серьезной задержки, которая возникает, когда я отправляю несколько сообщений socket.io за короткий промежуток времени.
Краткая настройка (детальная настройка внизу)
Я использую сервер node.js socket.io за прокси-сервером, настроенным в Apache. Сообщения, отправленные с клиентов socket.io на сервер socket.io, также будут перенаправлены в django, где я хочу зарегистрировать обработчик событий. Например, я хочу вызвать событие, каждый раз, когда клиент присоединяется к определенной комнате, чтобы отправить некоторые исходные данные через socket.io.
Django также может отправлять запросы на сервер socket.io, запускать события socket.io (например, emit), отправлять сообщения всем или определенным клиентам socket.io.
Для отправки и получения сообщений socket.io я использую http длинный опрос.
проблема
Эта пересылка сообщений в django, кажется, немного замедляет связь с socket.io. Например, я реализовал простой эхо-сервер, который работает так:
клиент ---send message
-> прокси apache -> сервер socket.io ---http POST
---> django -> обработчик событий (чтобы отправить сообщение обратно клиенту) ---http POST
---> сервер socket.io -> прокси apache -> клиент
Это работает нормально, если я отправлю только одно сообщение. Но если я отправляю много сообщений в течение короткого времени, задержка становится все больше и больше. Если я отправлю только 10 сообщений подряд, общая задержка составит около 5 секунд, пока последнее эхо-сообщение не вернется к клиенту. Если я отправляю еще больше сообщений, я получаю эффект, что всегда будет получено 3-4 эхо-сообщения подряд. После этого наступает пауза около 2 секунд, пока не появится следующий значок.
Этого не происходит, если я не пересылаю сообщения в django, но отправляю эхо обратно прямо на сервер socket.io.
Конечно, следует ожидать, что этот дополнительный компонент немного замедлит работу, но это отставание кажется довольно серьезным.
Вопрос
Так что я сделал не так? Может быть, мой подход к пересылке сообщений в django по своей сути неверен? Или есть некоторые конфигурации, которые я могу настроить, чтобы ускорить процесс? И откуда это отставание?
Подробная настройка (с выдержками из кода)
Apache 2.4.7
- www.example.com:80
- Обслуживает статические файлы и приложение django
- Является ли прокси для сервера node.js socket.io
виртуальный хост:
<VirtualHost *:80>
ServerName www.example.com
ProxyRequests off
ProxyPass /socket.io http://localhost:8080/socket.io retry=0
ProxyPassReverse /socket.io http://localhost:8080/socket.io
WSGIDaemonProcess www.example.com processes=1 threads=1 display-name=%{GROUP}
WSGIProcessGroup www.example.com
WSGIScriptAlias / /path/to/wsgi.py
DocumentRoot /var/www/example.com/
</VirtualHost>
Сервер Node.js
- работает на 127.0.0.1:8080
- экспресс-заявка
- пересылает входящие сообщения в django (для обработки событий)
- принимает почтовые запросы от django (для запуска событий socket.io, таких как emit / broadcast)
server.js
app = express();
server = http.Server(app);
io = require("socket.io")(server);
server.listen(8080);
app.use(express.bodyParser());
// incoming emit from django will emit a message over socket.io
app.post("/emit/", function (req, res) {
var body = req.body,
message = body.message;
io.emit("message", message);
res.send("everything is ok");
});
// forwarding incoming socket.io message to django
function forward(type, data, sessionId) {
var options, req;
data.sessionid = sessionId;
data = JSON.stringify(data);
console.log(data);
options = {
rejectUnauthorized: false,
host: "www.example.com",
port: 80,
path: "/socket/" + type + "/",
method: "POST",
headers: {
"Content-Type": "application/json",
"Content-Length": data.length
}
};
req = http.request(options, function(res){
res.setEncoding("utf8");
});
req.write(data);
req.end();
}
io.sockets.on("connection", function (socket) {
var sessionId = socket.id;
forward("connect", {}, sessionId);
socket.on("disconnect", function () {
forward("disconnect", {}, sessionId);
});
socket.on("message", function(message) {
forward("message", { message: message }, sessionId);
});
socket.on("join", function (room) {
socket.join(room);
forward("join", { room: room }, sessionId);
});
socket.on("leave", function (room) {
socket.leave(room);
forward("leave", { room: room }, sessionId);
});
});
Джанго
- принимает входящие сообщения с сервера socket.io (http POST)
- запускает обработчик события, если зарегистрирован
- запускает события socket.io (т.е. отправляет / транслирует), отправляя запросы HTTP POST серверу socket.io (127.0.0.1:8080 без прокси)
переслать сообщение в socket.io
def emit(message):
payload = {"message": message}
headers = {"content-type": "application/json"}
requests.post("http://127.0.0.1:8080/emit/", data=json.dumps(payload, cls=DjangoJSONEncoder), headers=headers, verify=False)
просмотр для обработки сообщений socket.io:
class MessageView(View):
def post(self, request, *args, **kwargs):
data = json.loads(request.body)
try:
sessionid = data["sessionid"]
message = data["message"]
//fire function will trigger event handler
fire("message", sessionid, {"message": message})
return HttpResponse("Everything worked :)")
except Exception, e:
return HttpResponseServerError(str(e))
@csrf_exempt
def dispatch(self, *args, **kwargs):
return super(MessageView, self).dispatch(*args, **kwargs)
зарегистрировать обработчик события сообщения:
@on_message
def message_received(sessionid, data):
//emit to all socket.io clients
emit("someone has sent a message")
клиент
- подключается к socket.io через www.example.com:80/socket.io
io.js
socket = io.connect(
"http://www.example.com:80",
{
"multiplex": false,
"transports": ["polling", "xhr-polling", "jsonp-polling"]
}
);
Следующий рисунок должен также проиллюстрировать, как я собирался связать отдельные компоненты друг с другом.