É útil para um proxy armazenar em buffer menos dados do que o Linux RCVBUF pode?

1

Estou vendo como proxies HTTP e proxies reversos lidam com problemas lentos de clientes. A idéia é que o servidor upstream tenha apenas slots limitados para clientes e, se o cliente demorar para receber dados, consome um slot por um longo tempo. Um proxy reverso pode ser usado para armazenar em buffer a resposta, liberar o slot anteriormente no upstream e, em seguida, encaminhar lentamente a resposta para o cliente.

Por exemplo, o nginx sugere ativar o buffer de resposta upstream alocando (por padrão) até 8 buffers de 8k cada. Se esses buffers forem preenchidos, ele poderá iniciar o armazenamento em buffer no disco (mas eu desabilitei esse recurso, meus discos estão ocupados o suficiente).

Veja: link

No entanto, fiz várias verificações e parece que o kernel aloca um grande RCVBUF (buffer de recepção) de cerca de 1-4MB. Se upstream envia uma resposta de 2MB enquanto o cliente final não lê nada, os buffers de proxy serão preenchidos em breve, e o buffer do kernel será usado no lugar.

Como o proxy armazenará menos dados do que o kernel, não vejo como isso ajuda a lidar com clientes lentos. Quais podem ser as vantagens de implementar / ativar explicitamente um recurso de buffering no proxy enquanto o kernel faz o suficiente para nós?

Editar: após a primeira resposta, gostaria de dar alguns detalhes sobre o que testei.

  • um programa cliente se conecta ao proxy reverso, aguarda por alguns segundos e começa a ler.
  • o proxy reverso somente armazena em buffer até 8kB na memória do espaço do usuário, depois de um read (), ele registrará o tamanho do buffer de recebimento do soquete,.
  • upstream serve uma resposta HTTP de 2MB (mais cabeçalhos), o log do tempo que demorou entre accept () e close ().

Durante o teste, posso ver que o servidor nunca esperará em um write () e até mesmo chamar close () antes que o cliente lento execute a primeira leitura (). Além disso, o tamanho do buffer de recepção do soquete aumentará e excederá 2 MB: toda a resposta do servidor será armazenada em buffer.

Eu executei os testes com o servidor upstream no mesmo host que o cliente e o proxy e, com o upstream em um host distante, o comportamento observado é o mesmo.

Além disso, eu entendo que o kernel pode usar buffers menores sob pressão de memória, mas isso também afeta o proxy reverso (que pode então ser incapaz de armazenar em buffer a resposta no espaço do usuário).

    
por Martin Richard 05.12.2014 / 16:09

1 resposta

1

I did multiple checks and it seems that the kernel allocates a quite large RCVBUF (receive buffer) of around 1-4MB.

Não por padrão, não. O tamanho é por soquete; Relacionamentos HTTP podem envolver vários soquetes. Não há, no meu caso, um máximo de sistema, a não ser que haja um número máximo (muito alto) de soquetes. De man 7 socket :

SO_RCVBUF

Sets or gets the maximum socket receive buffer in bytes. The kernel doubles this value (to allow space for bookkeeping overhead) when it is set using setsockopt(2), and this doubled value is returned by getsockopt(2). The default value is set by the /proc/sys/net/core/rmem_default file, and the maximum allowed value is set by the /proc/sys/net/core/rmem_max file. The minimum (doubled) value for this option is 256.

Para mim, isso é:

> cat /proc/sys/net/core/rmem_default
212992

208 kB. No entanto, isso varia de acordo com o protocolo. De man 7 tcp :

tcp_rmem (since Linux 2.4)

This is a vector of 3 integers: [min, default, max]. These parameters are used by TCP to regulate receive buffer sizes. TCP dynamically adjusts the size of the receive buffer from the defaults listed below, in the range of these values, depending on memory available in the system.

min: minimum size of the receive buffer used by each TCP socket. The default value is the system page size. (On Linux 2.4, the default value is 4K, lowered to PAGE_SIZE bytes in low-memory systems.) This value is used to ensure that in memory pressure mode, allocations below this size will still succeed. This is not used to bound the size of the receive buffer declared using SO_RCVBUF on a socket.

default: the default size of the receive buffer for a TCP socket. This value overwrites the initial default buffer size from the generic global net.core.rmem_default defined for all protocols. The default value is 87380 bytes. (On Linux 2.4, this will be lowered to 43689 in low-memory systems.) If larger receive buffer sizes are desired, this value should be increased (to affect all sockets). To employ large TCP windows, the net.ipv4.tcp_window_scaling must be enabled (default).

max: the maximum size of the receive buffer used by each TCP socket. This value does not override the global net.core.rmem_max. This is not used to limit the size of the receive buffer declared using SO_RCVBUF on a socket. The default value is calculated using the formula

   max(87380, min(4MB, tcp_mem[1]*PAGE_SIZE/128))

(On Linux 2.4, the default is 87380*2 bytes, lowered to 87380 in low-memory systems).

Esse valor é informado em /proc/sys/net/ipv4/tcp_rmem :

> cat /proc/sys/net/ipv4/tcp_rmem
4096    87380   6291456

Que pode ser confirmado com um pouco de C criando um único soquete TCP:

#include <sys/types.h>
#include <sys/socket.h>
#include <stdio.h>
#include <stdio.h>

int main (int argc, const char *argv[]) {
    int rcvbufsz;
    socklen_t buflen = sizeof(rcvbufsz);
    int fd = socket(AF_INET, SOCK_STREAM, 0);

    if (fd == -1) {
        perror("socket() failed");
        return 1;
    }

    if (getsockopt (
        fd,
        SOL_SOCKET,
        SO_RCVBUF,
        &rcvbufsz,
        &buflen
    ) == -1) {
        perror("getsockopt() failed");
        return 1;
    }

    printf("SO_RCVBUF = %d\n", rcvbufsz);

    return 0;
} 

Relatórios compilados e executados SO_RCVBUF = 87380 , que corresponde à figura de /proc . No entanto, o nginx está livre para ajustar isso para cima, não excedendo /proc/sys/net/core/rmem_max , o que provavelmente é de 208 kB novamente.

O bit sobre como o TCP "ajusta dinamicamente o tamanho do buffer de recepção dos padrões [...] dependendo da memória disponível no sistema" (de man 7 tcp ) também vale a pena reafirmar.

Agora, a carne da sua pergunta ...

Since the proxy will buffer less data than the kernel, I don't see how the it helps to deal with slow clients. What can be the advantages of explicitly implementing/enabling a buffering feature in the proxy while the kernel does enough for us?

Tenha em mente que o buffer discutido acima não é um buffer de espaço de usuário. Geralmente, os aplicativos não fazem nada diretamente, embora seja a origem dos dados lidos. Portanto, os dados no próprio buffer do nginx não estão simultaneamente no buffer do kernel . Está sendo lido disso. A leitura esvazia o buffer. Então, isso em efeito aumenta a quantidade de dados armazenados em buffer por 8 * 8 = 64 kB.

    
por 06.12.2014 / 15:42