Em um aplicativo ASP.Net, o usuário clica em um botão na página da Web e isso instancia um objeto no servidor por meio do manipulador de eventos e chama um método no objeto. O método vai para um sistema externo para fazer coisas e isso pode demorar um pouco. Então, o que eu gostaria de fazer é executar essa chamada de método em outro thread para que eu possa retornar o controle ao usuário com "Sua solicitação foi enviada". Estou razoavelmente feliz em fazer isso como disparar e esquecer, embora seria ainda melhor se o usuário pudesse continuar pesquisando o status do objeto.
O que não sei é se o IIS permite que meu thread continue em execução, mesmo que a sessão do usuário expire. Imagine, o usuário dispara o evento e nós instanciamos o objeto no servidor e disparamos o método em uma nova thread. O usuário fica satisfeito com a mensagem "Sua solicitação foi enviada" e fecha o navegador. Eventualmente, esta sessão de usuário atingirá o tempo limite no IIS, mas o thread ainda pode estar em execução, funcionando. O IIS permitirá que o thread continue em execução ou irá eliminá-lo e descartar o objeto quando a sessão do usuário expirar?
EDIT: Pelas respostas e comentários, entendo que a melhor maneira de fazer isso é mover o processamento de longa duração para fora do IIS. Além de tudo, isso lida com o problema de reciclagem de appdomain. Na prática, preciso tirar a versão 1 do papel em tempo limitado e trabalhar dentro de uma estrutura existente, portanto, gostaria de evitar a camada de serviço, daí o desejo de apenas disparar o thread dentro do IIS. Na prática, "longa duração" aqui levará apenas alguns minutos e a concorrência no site será baixa, portanto, deve estar tudo bem. Mas, a próxima versão definitivamente precisará ser dividida em uma camada de serviço separada.
Respostas:
Você pode realizar o que deseja, mas normalmente é uma má ideia. Vários blogs ASP.NET e mecanismos CMS adotam essa abordagem, porque desejam ser instaláveis em um sistema de hospedagem compartilhado e não depender de um serviço do Windows que precise ser instalado. Normalmente, eles iniciam um thread de longa execução em Global.asax quando o aplicativo é iniciado e têm esse processo de thread em fila de tarefas.
Além de reduzir os recursos disponíveis para IIS / ASP.NET para processar solicitações, você também tem problemas com o thread sendo eliminado quando o AppDomain é reciclado e, então, você tem que lidar com a persistência da tarefa enquanto ela está em andamento, como bem como reiniciar o trabalho quando o AppDomain voltar a funcionar.
Lembre-se de que, em muitos casos, o AppDomain é reciclado automaticamente em um intervalo padrão, bem como se você atualizar o web.config, etc.
Se você pode lidar com a persistência e os aspectos transacionais de seu thread sendo eliminados a qualquer momento, então você pode contornar a reciclagem de AppDomain tendo algum processo externo que faz uma solicitação em seu site em algum intervalo - para que, se o site for reciclado, você tem a garantia de que ele será reiniciado automaticamente em X minutos.
Novamente, isso normalmente é uma má ideia.
EDITAR: Aqui estão alguns exemplos dessa técnica em ação:
Community Server: Usando Windows Services vs. Thread de Segundo Plano para Executar Código em Intervalos Agendados Criando um Thread de Segundo Plano quando o Site é iniciado pela primeira vez
EDIT (de um futuro distante) - Hoje em dia eu usaria o Hangfire .
fonte
HostingEnvironment.RegisterObject
).QueueBackgroundWorkItem
para registrar facilmente tarefas em segundo plano, você pode querer atualizar sua resposta novamente.Não concordo com a resposta aceita.
Usar um thread de segundo plano (ou uma tarefa, iniciada com
Task.Factory.StartNew
) é bom no ASP.NET. Como acontece com todos os ambientes de hospedagem, você pode querer entender e cooperar com as instalações que regem o desligamento.No ASP.NET, você pode registrar o trabalho que precisa ser interrompido normalmente no desligamento usando o
HostingEnvironment.RegisterObject
método. Veja este artigo e os comentários para uma discussão.(Como Gerard aponta em seu comentário, agora há também
HostingEnvironment.QueueBackgroundWorkItem
que chamaRegisterObject
para registrar um agendador para o item de plano de fundo trabalhar. No geral, o novo método é mais agradável, pois é baseado em tarefas.)Quanto ao tema geral que você costuma ouvir dizer que é uma má ideia, considere a alternativa de implantar um serviço do Windows (ou outro tipo de aplicativo de processo extra):
Observe também que alguns cenários avançados podem até mesmo precisar que o thread de segundo plano seja executado no mesmo espaço de endereço que as solicitações. Vejo o fato de que o ASP.NET pode fazer isso como uma grande vantagem que se tornou possível por meio do .NET.
fonte
Você não gostaria de usar um encadeamento do pool de encadeamentos do IIS para esta tarefa porque isso o deixaria incapaz de processar solicitações futuras. Você poderia examinar as páginas assíncronas no ASP.NET 2.0 , mas essa também não seria a resposta certa. Em vez disso, parece que você se beneficiaria com o Microsoft Message Queuing . Basicamente, você adicionaria os detalhes da tarefa à fila e outro processo em segundo plano (possivelmente um serviço do Windows) seria o encarregado de realizar essa tarefa. Mas o ponto principal é que o processo em segundo plano é completamente isolado do IIS.
fonte
Eu sugeriria usar HangFire para tais requisitos. É um bom mecanismo de fogo e esquecer que funciona em segundo plano, suporta arquiteturas diferentes, confiável porque é apoiado por armazenamento de persistência.
fonte
Há um bom tópico e código de amostra aqui: http://forums.asp.net/t/1534903.aspx?PageIndex=2
Eu até brinquei com a ideia de chamar uma página keep alive no meu site a partir do thread para ajudar a manter o pool de aplicativos ativo. Lembre-se de que, se estiver usando esse método, você precisa de um tratamento de recuperação realmente bom, porque o aplicativo pode ser reciclado a qualquer momento. Como muitos já mencionaram, esta não é a abordagem certa se você tiver acesso a outras opções de serviço, mas para hospedagem compartilhada esta pode ser uma de suas únicas opções.
Para ajudar a manter o pool de aplicativos ativo, você pode fazer uma solicitação ao seu próprio site enquanto o thread está sendo processado. Isso pode ajudar a manter o pool de aplicativos ativo se o processo for executado por muito tempo.
string tempStr = GetUrlPageSource("http://www.mysite.com/keepalive.aspx"); public static string GetUrlPageSource(string url) { string returnString = ""; try { Uri uri = new Uri(url); if (uri.Scheme == Uri.UriSchemeHttp) { HttpWebRequest req = (HttpWebRequest)WebRequest.Create(uri); CookieContainer cookieJar = new CookieContainer(); req.CookieContainer = cookieJar; //set the request timeout to 60 seconds req.Timeout = 60000; req.UserAgent = "MyAgent"; //we do not want to request a persistent connection req.KeepAlive = false; HttpWebResponse resp = (HttpWebResponse)req.GetResponse(); Stream stream = resp.GetResponseStream(); StreamReader sr = new StreamReader(stream); returnString = sr.ReadToEnd(); sr.Close(); stream.Close(); resp.Close(); } } catch { returnString = ""; } return returnString; }
fonte
Começamos esse caminho e funcionou bem quando nosso aplicativo estava em um servidor. Quando queríamos escalar para várias máquinas (ou usar vários w3wp em um web garen), tínhamos que reavaliar e ver como gerenciar uma fila de trabalho, tratamento de erros, novas tentativas e o complicado problema de bloqueio correto para garantir apenas o servidor pega o próximo item.
... percebemos que não estamos no negócio de escrever motores de processamento em segundo plano, então procuramos as soluções existentes e acabamos usando o incrível projeto OSS hangfire
Sergey Odinokov criou uma verdadeira joia que é realmente fácil de começar e permite que você troque o backend de como o trabalho é persistido e enfileirado. Hangfire usa threads de fundo, mas persiste os trabalhos, lida com novas tentativas e dá a você visibilidade na fila de trabalho. Portanto, os trabalhos hangfire são robustos e sobrevivem a todos os caprichos de appdomains sendo reciclados etc.
Sua configuração básica usa sql server como armazenamento, mas você pode trocar por Redis ou MSMQ quando for hora de aumentar a escala. Ele também possui uma excelente interface do usuário para visualizar todos os trabalhos e seus status, além de permitir que você recoloque os trabalhos na fila.
Meu ponto é que, embora seja inteiramente possível fazer o que você deseja em um thread de segundo plano, há muito trabalho para torná-lo escalável e robusto. É bom para cargas de trabalho simples, mas quando as coisas ficam mais complexas, prefiro usar uma biblioteca construída para esse fim em vez de fazer esse esforço.
Para obter mais perspectivas sobre as opções disponíveis, verifique o blog de Scott Hanselman, que aborda algumas opções para lidar com trabalhos em segundo plano no asp.net. (Ele deu uma crítica brilhante ao hangfire)
Também como referenciado por John, vale a pena ler o blog de Phil Haack sobre por que a abordagem é problemática e como interromper o trabalho no encadeamento quando appdomain é descarregado.
fonte
Você pode criar um serviço do Windows para fazer essa tarefa? Em seguida, use o .NET remoting do servidor da Web para chamar o serviço do Windows para fazer a ação? Se for esse o caso, é o que eu faria.
Isso eliminaria a necessidade de retransmissão no IIS e limitaria parte de seu poder de processamento.
Do contrário, eu forçaria o usuário a ficar sentado enquanto o processo é concluído. Dessa forma, você garante que ele seja concluído e não eliminado pelo IIS.
fonte
Parece haver uma maneira com suporte de hospedar trabalhos de longa execução no IIS. Os serviços de fluxo de trabalho parecem projetados para isso, especialmente em conjunto com o Windows Server AppFabric . O design permite a reciclagem do pool de aplicativos, suportando a persistência automática e a retomada do trabalho de longa execução.
fonte
Você pode executar tarefas em segundo plano e elas serão concluídas mesmo após o término da solicitação. Não deixe uma exceção não detectada ser lançada . Normalmente, você deseja sempre lançar suas exceções. Se uma exceção for lançada em um novo thread, ele travará o processo de trabalho do IIS - w3wp.exe , porque você não está mais no contexto da solicitação. Isso também eliminará quaisquer outras tarefas em segundo plano que você tenha em execução, além das sessões com backup de memória em processo, se você as estiver usando. Isso seria difícil de diagnosticar, por isso a prática é desencorajada.
fonte
Basta criar um processo substituto para executar as tarefas assíncronas; ele não precisa ser um serviço do Windows (embora essa seja a abordagem mais ideal na maioria dos casos. MSMQ é mais do que kill.
fonte