Requests: Muitos arquivos abertos

Criado em 5 nov. 2011  ·  81Comentários  ·  Fonte: psf/requests

Estou construindo um gerador de carga básico e comecei a executar os limites do descritor de arquivo, não vi nenhuma documentação relativa a como liberar recursos, então ou estou fazendo errado e os documentos precisam ser atualizados ou as solicitações estão vazando descritores de arquivo em algum lugar (sem suporte para keepalive, estou um pouco confuso sobre o motivo de todos os arquivos serem deixados abertos)

Bug Contributor Friendly

Comentários muito úteis

"Muitos arquivos abertos" é o resultado do bug causado por sockets permanecer em CLOSE_WAIT.
Portanto, ulimit não corrigirá, apenas crie uma solução alternativa.

Todos 81 comentários

Onde você está usando requests.async ?

não, todas as solicitações eram solicitações razoavelmente simples.get / solicitações.post, ainda estou vendo alguns lá

$ lsof | grep localhost | wc -l
110

todos, exceto 4/5 deles, são do formato

Python    82117 daleharvey  123u    IPv4 0xffffff800da304e0       0t0      TCP localhost:61488->localhost:http (CLOSE_WAIT)

Estou um pouco perplexo com isso, para ser honesto.

Vou tentar outra vez para reproduzi-lo de forma confiável, se não puder fechar

Já vi isso acontecendo comigo, mas apenas quando estou usando o módulo assíncrono com mais de 200 conexões simultâneas.

Oi,
Eu tive exatamente o mesmo problema usando requisições e monkey patching com gevent: algumas conexões ficando em CLOSE_WAIT
Talvez seja um problema com gevent assim.

Pode ser um problema de ulimit -n. Experimente com um valor mais alto.

"Muitos arquivos abertos" é o resultado do bug causado por sockets permanecer em CLOSE_WAIT.
Portanto, ulimit não corrigirá, apenas crie uma solução alternativa.

@tamiel como

Vou fazer mais testes o mais rápido possível e tentar consertar.

Eu olhei para ele e parece haver um problema com todas as bibliotecas que usam httplib.HTTPSConnection.

Postou um exemplo aqui:

https://gist.github.com/1512329

Acabei de encontrar um erro muito semelhante ao usar um pool assíncrono apenas com conexões HTTP - ainda estou investigando, mas passar um tamanho de pool para async.map faz com que o erro seja reproduzido rapidamente.

Alguma correção para isso? Isso torna Requests inutilizáveis ​​com gevent ..

É tudo sobre CLOSE_WAIT s. Só tenho que fechá-los. Eu não tenho certeza porque eles ainda estão abertos.

É um problema do urllib3? Ter que fechá-los sozinhos não é uma boa ideia, eu acho.

É mais um problema geral. Podemos manter a conversa aqui.

Ok, só para lhe dar uma perspectiva, estamos tentando mover de httplib2 para solicitações, e não vemos esse problema com httplib2. Portanto, não é um problema geral com certeza.

Por geral, quero dizer que é um problema muito sério que afeta todos os envolvidos.

então, como podemos resolver isso? realmente queremos usar solicitações + descanso daqui para frente

Adoraria saber a resposta para isso.

O vazamento parece ser devido ao tratamento de redirecionamento interno, que faz com que novas solicitações sejam geradas antes que as respostas pendentes sejam consumidas. No teste, acdha @ 730c0e2e2bef77968a86962f9d5f2bebba4d19ec tem uma correção insuficiente, mas eficaz, simplesmente forçando cada resposta a ser consumida antes de continuar.

Isso exigiu mudanças em dois lugares, o que me faz querer refatorar um pouco a interface, mas estou sem tempo para continuar no momento.

399 tem uma correção que funciona bem no meu gerador de carga assíncrona (https://github.com/acdha/webtoolbox/blob/master/bin/http_bench.py) com milhares de solicitações e um baixo fd ulimit

Eu encontrei o mesmo problema ao usar o assíncrono - uma solução alternativa foi fragmentar as solicitações e excluir as respostas / chamar gc.collect

Acredito que hoje encontrei isso me conectando a um servidor licenciado que permite apenas 5 conexões.

Usando o assíncrono, só consegui GET 4 coisas antes de pausar por 60 segundos.

Usando o GET normal com consumo, eu poderia buscar cerca de 150 coisas em série em menos de 40 segundos.

Ainda não fiz meu kludge desde que vi esse problema.

Acabei de receber este erro ao usar ipython e recebi esta mensagem. Isso é apenas fazer uma solicitação de cada vez, mas acho que consegui algo semelhante ao usar async.

ERROR: Internal Python error in the inspect module.
Below is the traceback from this internal error.
Traceback (most recent call last):
    File "/Library/Python/2.7/site-packages/IPython/core/ultratb.py", line 756, in structured_traceback
    File "/Library/Python/2.7/site-packages/IPython/core/ultratb.py", line 242, in _fixed_getinnerframes
    File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/inspect.py", line 1035, in getinnerframes
    File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/inspect.py", line 995, in getframeinfo
    File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/inspect.py", line 456, in getsourcefile
    File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/inspect.py", line 485, in getmodule
    File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/inspect.py", line 469, in getabsfile
    File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/posixpath.py", line 347, in abspath
OSError: [Errno 24] Too many open files

Unfortunately, your original traceback can not be constructed.

Estranhamente, acho que, ao usar apenas o interpretador Python normal, recebo um "Erro máximo de tentativas", mas acho que esse é outro problema comigo fazendo solicitações em todo o mesmo domínio, mas não tenho certeza.

Corri para isso no primeiro projeto que tive onde allow_redirects era True; parece ser causado pelos objetos de resposta de vazamento da cadeia de redirecionamento que não são liberados mesmo com prefetch = True. Isso corrigiu em meu teste inicial:

        [i.raw.release_conn() for i in resp.history]
        resp.raw.release_conn()

Hmmm ..

configuração @acdha :

requests.defaults.defaults['allow_redirects'] = False

antes de fazer qualquer solicitação ainda resulta no mesmo erro, mas acho que esta não é uma opção para minha implementação, pois todas as solicitações que estou fazendo exigirão um redirecionamento = /

@dalanmiller Como você está processando suas respostas? Anteriormente, eu estava usando async.map com um gancho de resposta e _parece_ ser mais estável usando um loop simples sobre async.imap :

for resp in requests.async.imap(reqs, size=8):
    try:
        print resp.status_code, resp.url
    finally:
        [i.raw.release_conn() for i in resp.history]
        resp.raw.release_conn()

@acdha

Eu estava usando um loop for através de uma lista de url e fazendo um request.get on each com minhas configurações e tal.

for u in urls:
    response_list.append(requests.get(u))

Tentei usar o seu colar e ele funciona para cerca de 50 solicitações na minha lista de comprimento de 900, até começar a obter "máximo de erros de novas tentativas excedido com url" para o resto. Este é um erro bastante padrão para atingir o mesmo domínio repetidamente, não?

Ei, eu estava rastreando uma lista enorme de urls, 35k, e recebi o mesmo erro em _algumas_ solicitações.

Estou recebendo urls em blocos de 10, assim:

responses = requests.async.map([requests.async.get(u, params=self.params()) for u in chunk]) # chunk is a list of 10

Em algum lugar na faixa de 20k eu comecei a receber o erro 24, então foi ok até 30k e então novamente.

Mais alguma informação em que você esteja interessado para restringi-la?

requests.async sumiu. Você pode querer considerar a mudança para grequests.

Certo, obrigado. Seria bom mencionar isso na documentação.

Meio que um novato quando se trata de Pull Requests e de escrever documentação, mas eu tentei e enviei. Por favor, comente ou critique :)

https://github.com/kennethreitz/requests/pull/665

Ok, isso acontece mesmo sem usar async, com apenas requests.get, após 6K requisições.

Eu suspeitei disso.

Para mim, o erro 'Muitos arquivos abertos' ocorreu após o download de exatamente 1k arquivos. Minha solução foi desabilitar a propriedade keep-alive, sempre recebendo solicitações em pedaços ( @acdha, obrigado pela dica). lsof -p PID | wc -l mostra um número não crescente de conexões durante a execução.

rsess = requests.session()
rsess.config['keep-alive'] = False

rs = [grequests.get(l, session=rsess) for l in links]

for s in chunks(rs,100):
    responses = grequests.map(s, size=concurrency)
    for r in responses:
        try:
            print(r.status_code, r.url)
        finally:
            r.raw.release_conn()

[1] fragmentação: http://stackoverflow.com/a/312464

Fechando durante a transferência para a correção urllib3.

@kennethreitz Qual é o número do problema do urllib3?

Parece que este é o problema http://bugs.python.org/issue16298

@silvexis poderia muito bem estar relacionado ao bug urllib3, agora estou apenas desejando que alguém tivesse respondido @ piotr-dobrogost: P

Alguém ainda está enfrentando esse problema?

Eu não ouvi nenhum relato sobre isso. Você está?

É um problema de configuração da caixa, não do framework. Observe a configuração do kernel do seu sistema operacional. No BSD, é denominado kern.maxfiles . Há um tópico sobre ulimit em sistemas Linux: http://stackoverflow.com/questions/34588/how-do-i-change-the-number-of-open-files-limit-in-linux
Espero que ajude, e não sei como alterar esse parâmetro no Windows.

Com a ressalva de que ainda estamos executando uma versão mais antiga de solicitações, temos o seguinte código horrível para lidar com isso:

    if self._current_response is not None:
            # Requests doesn't have a clean API to actually close the
            # socket properly. Dig through multiple levels of private APIs
            # to close the socket ourselves. Icky.
            self._current_response.raw.release_conn()
            if self._current_response.raw._fp.fp is not None:
                sock = self._current_response.raw._fp.fp._sock
                try:
                    logger.debug('Forcibly closing socket')
                    sock.shutdown(socket.SHUT_RDWR)
                    sock.close()
                except socket.error:
                    pass

(Eu acho que self._current_response é o objeto de resposta das solicitações)

Hmm, onde está quebrada a cadeia de fechamento? Temos um método Response.close() que chama release_conn() , então o que precisa acontecer em release_conn() para que isso funcione?

@Lukasa, isso foi definitivamente corrigido no urllib3 porque eu fazia parte da discussão. Com uma inclinação para ser conservador em minha estimativa, eu diria que está lá desde os pedidos 1.2.x se não 1.1.x.

Sim, eu acho que isso foi corrigido. A menos que vejamos algo no 1.2.3, continuarei assumindo que isso foi corrigido.

Estou vendo um vazamento CLOSE_WAIT com 2.0.2. Você tem testes de unidade para garantir que não haja regressão no tópico?

Não, nós não. AFAIK urllib3 também não. Você pode reproduzir seu vazamento facilmente?

Usamos solicitação em nosso aplicativo interno desde segunda-feira, e atingimos os 1024 maxfiles hoje.

2 horas após a reinicialização, temos 40 CLOSE_WAIT conforme informado por lsof.

Portanto, acho que seremos capazes de reproduzir em um ambiente de desenvolvimento, sim. Vou mantê-lo em contato

@tardyp também, como você instalou as solicitações? Acho que todos os mantenedores de pacotes do sistema operacional eliminam o urllib3. Se eles não mantiverem isso atualizado e você estiver usando uma versão antiga, essa pode ser a causa. Se você estiver usando o pip, sinta-se à vontade para abrir um novo problema para rastrear em vez de adicionar discussão a este.

Eu instalei com pip, mas eu uso o python 2.6, vi uma correção no python2.7 para
este bug. Você faz um monkeypatch para uma versão mais antiga?

Pierre

Na sexta-feira, 29 de novembro de 2013 às 17:33, Ian Cordasco [email protected] :

@tardyp https://github.com/tardyp também, como você instalou as solicitações? Eu
acho que todos os mantenedores do pacote do sistema operacional eliminam o urllib3. Se eles não
mantenha isso atualizado e você está usando uma versão antiga, que pode ser o
causa em vez disso. Se você estiver usando o pip, sinta-se à vontade para abrir um novo problema para
rastreie isso em vez de adicionar discussão a este.

-
Responda a este e-mail diretamente ou visualize-o em Gi tHubhttps: //github.com/kennethreitz/requests/issues/239#issuecomment -29526302
.

@tardyp abra um novo problema com o máximo de detalhes possível, incluindo se as solicitações que você está fazendo têm redirecionamentos e se você está usando gevent. Além disso, quaisquer detalhes sobre o sistema operacional e um exemplo de como reproduzi-lo seriam fantásticos.

FYI https://github.com/shazow/urllib3/issues/291 foi revertido devido a bugs.

Devemos reabrir isso?
Estou tendo o mesmo problema!

@polvoazul Não tem como ser o mesmo problema, que foi relatado originalmente em 2011, então não acho que a reabertura seja correta. No entanto, se você estiver executando a versão atual de solicitações (2.4.3) e puder reproduzir o problema, abrir um novo problema seria correto.

@Lukasa, preciso de sua ajuda。 eu uso eventlet + solicitações , que sempre criam tantas meias que não conseguem identificar o protocolo。 minhas solicitações são 2.4.3, as solicitações eventlet + causam esse problema?

Sinto muito @mygoda , mas é impossível saber. Se você não está restringindo o número de solicitações que podem estar pendentes a qualquer momento, então certamente é possível, mas isso é um problema de arquitetura fora do âmbito das solicitações.

@Lukasa obrigado。 acho que meu problema é semelhante a este this meu projeto é pyvmomi . essa conexão é longa. Eu sempre me confundi por que tantos não conseguem identificar a meia

Tendo o mesmo problema agora, rodando 120 threads, causa 100000+ arquivos abertos, alguma solução agora?

@mygoda, você usa períodos incríveis。

@ 1a1a11a _Que_ arquivos você tem abertos? Esse seria um primeiro passo útil para entender este problema.

@ 1a1a11a que versão de solicitações você está usando? Qual versão do python? Qual sistema operacional? Podemos obter alguma informação?

Estou usando a solicitação 2.9.1, python 3.4, ubuntu 14.04, basicamente, estou escrevendo um rastreador usando 30 threads com proxies para rastrear algum site. Atualmente ajustei o limite de arquivo por processo para 655350, caso contrário, ele relatará um erro.

Ainda estou recebendo o erro "Falha ao estabelecer uma nova conexão: [Errno 24] Muitos arquivos abertos" de requests.packages.urllib3.connection.VerifiedHTTPSConnection. "Estou usando Python 3.4, requests 2.11.1 e requests-futures 0.9.7. Agradeço que o request-futures seja uma biblioteca separada, mas parece que o erro vem de solicitações. Estou tentando fazer 180 mil solicitações assíncronas por SSL. Dividi essas solicitações em segmentos de 1000, então só avance para os próximos 1000 quando todos os objetos futuros forem resolvidos. Estou executando o Ubuntu 16.04.2 e meu limite de arquivos abertos padrão é 1024. Seria bom entender o motivo subjacente para este erro. A biblioteca de solicitações cria um arquivo aberto para cada solicitação individual? Em caso afirmativo, por quê? Este é um arquivo de certificado SSL? E a biblioteca de solicitações fecha automaticamente esses arquivos abertos quando o objeto futuro é resolvido?

Requests abre muitos arquivos. Alguns desses arquivos são abertos para certificados, mas são abertos pelo OpenSSL e não por Requests, portanto, não são mantidos. Além disso, as solicitações também abrirão, se necessário, o arquivo .netrc , o arquivo hosts e muitos outros.

Você será melhor atendido usando uma ferramenta como strace para descobrir quais arquivos são abertos. Há uma lista restrita de chamadas de sistema que levam à alocação de descritores de arquivo, portanto, você deve ser capaz de enumerá-los com rapidez. Isso também permitirá que você saiba se há um problema ou não. Mas, sim, eu esperaria que, se você estivesse ativamente fazendo 1000 conexões por HTTPS, então na carga de pico, poderíamos facilmente usar mais de 1000 FDs.

Eu também lutei com esse problema e descobri que usar opensnoop no OS X funcionou muito bem para me permitir ver o que estava acontecendo se alguém tivesse os mesmos problemas.

Também vejo com frequência esse erro ao chamar requests.post(url, data=data) repetidamente para um servidor HTTP (não HTTPS). Executando no Ubuntu 16.04.3, Python 3.5.2, solicitações 2.9.1

O que é data ?

Texto de algumas centenas de kb

Não é um objeto de arquivo?

Não, eu faço uma grande consulta na memória.

Você está executando este código em vários threads?

Não, thread único, postando no localhost

Parece quase impossível para nós estarmos vazando tantos FDs então: deveríamos estar usando repetidamente a mesma conexão TCP ou fechando-a agressivamente. Quer verificar o que seu servidor está fazendo?

Estou tendo esse problema. Python 2.7, solicitações 2.18.4, urllib3 1.22.
Executando código multiencadeado (não multiprocessado). Conectando-se a no máximo 6 URLs de uma vez, criando e fechando manualmente uma nova sessão para cada um.

Estou tendo o mesmo problema em Python 3.5 , requests==2.18.4

@mcobzarenco você tem certeza de que está fechando (implicitamente) a conexão subjacente da resposta? O simples retorno da resposta não encerrará a conexão. Ao ler response.content os dados são realmente lidos e depois disso o socket não vai ficar em CLOSE_WAIT.

Esta página foi útil?
0 / 5 - 0 avaliações