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

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

我正在尝试使用 django、apache 和 socket.io 建立一个网站。多亏了一些教程和其他 stackoverflow 问题,我设法使一切正常运行,除了在短时间内发送多个 socket.io 消息时发生的严重延迟。

简短设置(详细设置在底部)

我在 Apache 中配置的代理后面运行一个 node.js socket.io 服务器。从 socket.io 客户端发送到 socket.io 服务器的消息也将转发到 django,我想在那里注册事件处理程序。例如,我想在每次客户端加入某个房间时触发一个事件,以通过 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 的方法本质上是错误的?或者我可以调整一些配置来加快速度吗?这种延迟首先是从哪里来的?

详细设置(带代码摘录)

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 事件,如 emit/broadcast)

服务器.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 服务器(127.0.0.1:8080,无需代理)发送 http POST 请求来触发 socket.io 事件(即发出/广播)

将消息转发到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"]
    }
);

下图还说明了我打算如何让各个组件相互通信。

当前设置

相关内容