Um pacote Python para descarregar uma chamada de função para um servidor HTTP em execução no localhost automaticamente usando um decorador. Compatível com multiprocessamento, picles, frasco, fastapi, assíncrono etc.
Imagine um caso de um aplicativo multi -rosqueado ou multiprocessante, onde 1 ou poucas funções são intensivas com recursos fortemente (CPU ou memória), mas as outras funções podem ser executadas em paralelo.
Exemplo - Uma chamada da API seguida de tokenização e classificação usando um grande modelo DL seguido de mais chamadas da API.
Nesse caso, faria sentido criar um servidor (geralmente usando o TorchServe ou o TFServing) para atender solicitações e substituir a chamada de função por uma solicitação de postagem para o servidor.
O ServerHandler cria um servidor síncrono e substitui as chamadas para a função automaticamente durante o tempo de execução.
As solicitações são feitas para 1 instância de um processo executando um http.server.httpServer que executa a função nele.
O AsyncServerHandler também está disponível, o que torna as solicitações de forma assíncrona.
Mesmo chamadas feitas de diferentes processos, threads, multiprocessamento, frasco, fastapi e loops de eventos assíncronos são feitos para o mesmo processo de servidor.
Em geral :
some code with a callable
Pode ser substituído por uma instância de servidorhandler ou asyncserverHandler que aceita o código como uma string em seu primeiro argumento e o nome do chamável como o segundo argumento.
from auto_function_serving . ServerHandler import ServerHandler
callable_name = ServerHandler ( """
some independent code with a callable
""" , "callable_name" )Exemplo :
import module1
import module2
def functionname ( someinput ):
a = module1 . function1 ( someinput )
return module2 . function2 ( a )pode ser substituído por
from auto_function_serving . ServerHandler import AsyncserverHandler
functionname = AsyncServerHandler ( """
import module1
import module2
def functionname(someinput):
a = module1.function1(someinput)
return module2.function2(a)
""" , "functionname" , port = "Any" )Decoradores ( @AsyncserverHandler.Decorator e @ServerHandler.Decorator) e AsyncServerHandler Detalhes em mais uso.
from auto_function_serving . ServerHandler import ServerHandler
callable_name = ServerHandler ( """
some independent code with a callable
""" , "callable_name" , port = None , backend = 'Popen' , wait = 100 , backlog = 1024 )) executa http.server.httpServer.
Os objetos ServerHandler e AsyncServerHandler podem ser carregados e descarregados com picles.
Usa Popen ou multiprocessamento para executar o servidor.
Usa apenas uma única dependência externa (AIOHTTP) e apenas para assíncrona.
http, não https.
escolhe uma porta com base no hash de entrada. (a menos que especificado de outra forma)
Alterações mínimas de código.
Deve ser compatível com quase todas as funções em quase todas as Envs Cpython. (Não tenho certeza de onde pode falhar? Adicione um problema se você encontrar um.)
Os vazamentos ou erros de memória (do servidor) são extremamente improváveis, pois é mínimo, um processo único, um único processo e um componente padrão do python stdlib.
Exceções causam erros 5xx sem fechar o servidor.
Mesmo processos separados farão solicitações para 1 instância do mesmo servidor, a menos que especificado de outra forma. (Porque está procurando um servidor em uma porta específica.).
Pode especificar o contrário, defina a porta como qualquer porta gratuita para que um novo objeto ServerHandler inicie um novo servidor.
Solicitações de postagem HTTP: leves, poucos MS Overhead, confiáveis.
Async é um bom recurso.
agora com testes.
Ter uma série de código como argumento para uma classe não é pitônico, a menos que o decorador seja usado.
A importação de funções internas não é ideal, mesmo quando o decorador é usado.
Solicitações de postagem HTTP: Inseguro, poucas ms.
Exceções dentro do servidor não são enviadas de volta.
Sem lote.
Sem registro embutido. (Poderia ser adicionado). Atraso de inicialização de até alguns segundos para iniciar o servidor. As funções assíncronas não funcionarão no servidor.
Nenhum servidor automático reiniciar, caso o servidor seja fechado.
Pode deixar alguns recursos bloqueados por um tempo (<1min) se não forem fechados corretamente.
Os problemas podem ocorrer se não estiver disponível Popen ou Multiprocessing.
Possíveis erros de assíncronos aninhados com Jupyter ou outro? Por favor, veja o Nest-Asyncio e os problemas.
Avisos de soluções alternativas um tanto hacky (mas legítimas e completamente funcionais).
O fechamento do processo do servidor em Del e Atexit.redister ( del ) falha por algum motivo (testado e improvável).
Use o Pack Packager PiP para instalar auto_function_serving
pip install auto_function_serving O código do servidor é armazenado no ServerHandler.base_code e alguma formatação de string é usada para preencher os espaços em branco.
O processo do servidor é iniciado com Popen (ou multiprocessamento, se especificado). A primeira coisa que faz é importar soquete e vincular a porta - se não estiver disponível, o código para após uma exceção. Portanto, apenas 1 instância do servidor é executada de cada vez em uma máquina.
Sabemos que a função está pronta depois que podemos receber uma solicitação GET válida do servidor.
Entradas e saídas são enviadas como bytes, convertidas para e para objetos usando picles.
Se a porta não for inicializada durante a inicialização (padrão), uma porta de 50000 a 60000 é escolhida por hash o código de entrada para torná -lo independente da fonte de uma função. As colisões de diferentes funções são possíveis, mas improváveis. A colisão da mesma função em vários processos é usada para garantir que apenas 1 processo do servidor seja executado por vez. A porta pode ser especificada, se necessário.
sobrecarga para entrada e saída pequenas (poucos bytes) -
~ 2ms para solicitações com urllib.request
~ 4ms para solicitações assíncronas com aiohttp.clientSession
sobrecarga para entrada e saída grandes
~ 10ms para entrada e saída de 0,5 MB (transferência total de 1 MB).
~ 60ms para entrada e saída de 5 MB (transferência total de 10 MB).
~ 600ms para entrada e saída de 50 MB (transferência total de 100 MB).
Também pode ser usado com o decorador fornecido para funções sem dependências fora da função.
from auto_function_serving . ServerHandler import ServerHandler
@ ServerHandler . decorator
def someheavyfunction ( args , ** kwargs ):
for i in range ( big_number )
someexpensivecomputationAs importações dentro da função funcionarão
from auto_function_serving . ServerHandler import ServerHandler
@ ServerHandler . decorator
def someheavyfunction ( args , ** kwargs ):
import numpy as np from auto_function_serving . ServerHandler import ServerHandler
@ ServerHandler . decorator
def someheavyfunction ( args , ** kwargs ):
if not hasattr ( someheavyfunction , 'RunOnce' ):
global np
import numpy as np
setattr ( someheavyfunction , 'RunOnce' , None )
... etcQuando o Somemodule não tem nenhum carregamento global caro.
from auto_function_serving . ServerHandler import ServerHandler
from somemodule import someheavyfunction
someheavyfunction = ServerHandler . decorator ( someheavyfunction )O endereço IP pode ser alterado definindo o ServerHandler.ip_address (padrão "127.0.0.1") antes de criar uma nova instância.
O AsyncServerHandler também está disponível, que usa AIOHTTP para fazer as solicitações de forma assíncrona, para uso com FASTAPI e outros casos de uso assíncronos.
O AsyncServerHandler tem o mesmo uso que o ServerHandler, exceto que as chamadas precisam ser aguardadas ou usadas com asyncio.run () ou com asyncio.get_event_loop (). Run_until_complete ().
O número de chamadas assíncronas pode ser limitado definindo o AsyncServerHandler.tcpconnector_limit, que controla o limite do TCPConnector (padrão 100). Usar semáforo também é algo a considerar.
Bibliotecas: aipo, TfServing, Torchserve, Flask
Enviando globais e moradores para o executivo
árvores ast
Solicitações de tração são bem -vindas.
Licença Apache 2.0