Apache + Django + socket.io(长轮询)+代理的性能问题

我试图用django,apache和socket.iobuild立一个网站。 感谢一些教程和其他stackoverflow的问题,我设法让一切工作,除了一个严重的滞后,发生在短时间内发送多个socket.io消息。

短安装(详细设置在底部)

我在Apache中configuration的代理后面运行一个node.js socket.io服务器。 消息从socket.io客户端发送到socket.io服务器,也将转发到django,我要注册事件处理程序。 例如,我想触发一个事件,每次客户端join某个房间,通过socket.io发送一些初始数据。

Django也可以将请求发送到socket.io服务器,以触发socket.io事件(如emit),将消息发送到所有或某些socket.io客户端。

要发送和接收socket.io消息,我使用http长轮询。

问题

这个消息转发到Django,似乎减慢了socket.io通信相当多。 例如,我实现了一个简单的echo工具,像这样工作:

客户端— send message – > apache代理 – > socket.io服务器— http POST —> django – >事件处理程序(将消息发送回客户端)— http POST —> socket.io服务器 – > apache代理 – >客户端

这工作正常,如果我只发送一条消息。 但是如果我在很短的时间内发出大量的信息,就会有越来越大的滞后。 如果我只连续发送10条消息,总共有5秒的延迟,直到最后一条回显消息返回给客户端。 如果我发送更多的消息,我会得到连续收到3-4个回显消息的效果。 之后,下一个徽章到达之前,停顿约2秒钟。

这不会发生,如果我不转发消息到Django,但直接发送回来的socket.io服务器内。

当然可以预料,这个额外的组件会使事情稍微放慢一些,但是这个延迟似乎相当严重。

那么我做错了什么? 也许我的方法转发消息到Django本质上是错误的? 还是有一些configuration,我可以调整,以加快速度? 这个滞后来自哪里呢?

详细设置(使用代码摘录)

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的post请求(触发socket.io事件,如发送/广播)

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); }); }); 

Django的

  • 接受来自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") 

客户

  • 通过www.example.com:80/socket.io连接到socket.io

io.js

 socket = io.connect( "http://www.example.com:80", { "multiplex": false, "transports": ["polling", "xhr-polling", "jsonp-polling"] } ); 

下面的graphics还应该说明我是如何使单个组件相互通信的。

当前设置