Problemas de desempenho com o Apache + Django + socket.io (long polling) + Proxy

3

Estou tentando criar um site com o django, o apache e o socket.io. Graças a alguns tutoriais e outras perguntas do stackoverflow, consegui fazer tudo funcionar, exceto por um atraso grave, que ocorre quando envio várias mensagens do socket.io dentro de um curto período de tempo.

Configuração Abreviada (configuração detalhada na parte inferior)

Estou executando um servidor node.js socket.io atrás de um proxy configurado no Apache. As mensagens enviadas dos clientes socket.io para o servidor socket.io, também serão encaminhadas para o django, onde eu quero registrar o manipulador de eventos. Por exemplo, eu quero disparar um evento, toda vez que um cliente entra em uma determinada sala, para enviar alguns dados iniciais através do socket.io.

O Django também pode enviar pedidos para o servidor socket.io, para acionar eventos socket.io (como emit), para enviar mensagens para todos ou determinados clientes socket.io.

Para enviar e receber mensagens socket.io, eu uso http long polling.

Problema

Esta mensagem encaminhando para o django, parece retardar bastante a comunicação do socket.io. Por exemplo, eu implementei um simples echo sever que funciona assim:

client --- send message - > proxy do apache - > servidor socket.io --- http POST --- > Django - > manipulador de eventos (para enviar a mensagem de volta ao cliente) --- http POST --- > servidor socket.io - > proxy do apache - > cliente

Isso funciona bem, se eu enviar apenas uma mensagem. Mas se eu enviar muitas mensagens, dentro de um curto período de tempo, haverá um atraso cada vez maior. Se eu enviar apenas 10 mensagens seguidas, haverá um atraso total de cerca de 5 segundos até que a última mensagem de eco retorne ao cliente. Se eu enviar ainda mais mensagens, recebo o efeito de que sempre 3-4 mensagens de eco serão recebidas consecutivamente. Depois disso, há uma pausa de cerca de 2 segundos até que o próximo crachá chegue.

Isso não acontece, se eu não encaminhar mensagens para o django, mas enviar o eco diretamente para dentro do servidor socket.io.

É claro que é de se esperar que esse componente adicional atrase um pouco as coisas, mas esse atraso parece ser bastante severo.

Pergunta

Então, o que eu fiz de errado? Talvez minha abordagem para encaminhar mensagens para o django seja inerentemente errada? Ou existem algumas configurações que eu possa ajustar para acelerar as coisas? E de onde vem essa defasagem?

Configuração detalhada (com trechos de código)

Apache 2.4.7

  • www.example.com:80
  • Serve arquivos estáticos e aplicativo django
  • É proxy para o servidor node.js socket.io

host virtual:

<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>

Servidor Node.js

  • é executado em 127.0.0.1:8080
  • aplicativo expresso
  • encaminha mensagens recebidas para o django (para manipulação de eventos)
  • aceita solicitações de post do django (para acionar eventos socket.io como 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);
    });
});

Django

  • aceita mensagens recebidas do servidor socket.io (http POST)
  • aciona o manipulador de eventos se registrado
  • aciona eventos socket.io (ou seja, emit / broadcast) enviando solicitações http POST para o servidor socket.io (127.0.0.1:8080 sem proxy)

encaminhar mensagem para 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)

vista para lidar com mensagens 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)

registre um manipulador de eventos de mensagem:

@on_message
def message_received(sessionid, data):
    //emit to all socket.io clients
    emit("someone has sent a message")

Cliente

  • conecta-se ao socket.io via www.example.com:80/socket.io

io.js

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

O gráfico a seguir também deve ilustrar como pretendi que os componentes individuais se comunicassem entre si.

    
por basilikum 08.02.2015 / 16:13

0 respostas