NIO Evented with Play

Este artigo discutirá brevemente em um nível muito alto por que você consideraria a escolha de um modelo de programação com eventos com E / S sem bloqueio para lidar com chamadas para serviços da web externos.

Há uma discussão muito boa sobre servidores da Web com eventos e com threads aqui .

Node.js é um exemplo de um modelo de programação NIO com eventos, onde é comum ver um código como este:

var http = require('http');
var fs = require('fs');
var index = fs.readFileSync('index.html');

http
.createServer(function (req, res) {
res
.writeHead(200, {'Content-Type': 'text/plain'});
res
.end(index);
}).listen(3000);

console
.log("Executing before the server starts up")

Neste exemplo, o servidor escuta na porta 3000 as solicitações de entrada e o faz ao não bloquear o thread principal de execução, o que permite que a última instrução seja impressa no console.

Um processo simples do Node.js pode lidar com muitas solicitações simultaneamente, empregando IO sem bloqueio, que libera recursos. No entanto, não é verdadeiramente paralelo porque o código Javascript só será executado por um único thread a qualquer momento. MRI Ruby tem uma restrição semelhante ao empregar um processo Global Interpreter Lock por Ruby , que, como o Javascript, o impede de atingir o paralelismo dentro de um processo.

Para uma discussão mais aprofundada das diferenças entre simultaneidade e paralelismo, assista à palestra de Rob Pike .

Rails with Unicorn é um exemplo de servidor web síncrono encadeado que emprega um modelo trabalhador com bloqueio de IO . Usar o Unicorn para processar muitas chamadas de serviço da web que podem consumir muito tempo não é uma abordagem escalonável, pois os trabalhadores ficarão em gargalos com clientes lentos.

Uma das principais vantagens de usar Scala é que ele roda na JVM e tem acesso a todas as bibliotecas e estruturas implementadas em Java.

Um exemplo disso é a biblioteca NIO Netty do Java . Play é uma estrutura da web Java / Scala MVC popular que usa essa biblioteca para implementar um servidor da web com eventos, fornecendo uma solução interessante para aplicativos com muitas chamadas externas de serviço da web e / ou aplicativos com uso intensivo de CPU devido à maneira subjacente da JVM de lidar com paralelismo e simultaneidade.

O Play combinado com os recursos do Scala fornece alguns recursos interessantes para escrever código altamente simultâneo e paralelo. Por exemplo, você pode escrever um código como este:

def fanOut = Action.async {
val svcFuture1
= WS.get("https://my.service1")
val svcFuture2
= WS.get("https://my.service2")
val svcFuture3
= WS.get("https://my.service3")

val infoFuture
=
for {
svc1
svcFuture1
svc2
svcFuture2
svc3
svcFuture3
} yield Ok(presentInfo(svc1, svc2, svc3))

val resultFuture
=
infoFuture recover
{
case e: Exception =>
InternalServerError(e.getMessage)
}

println
("I will likely print before controller replies to client")

resultFuture

}

que simula uma ação do controlador Play chamada fanOutque receberá uma solicitação e chamará três serviços da web externos. Assim que todos os serviços forem concluídos e retornarem uma resposta, as respostas serão processadas e uma resposta será retornada ou, se algum serviço falhar, uma resposta com a mensagem de exceção do primeiro serviço que falhou será retornada ao cliente. Todas as solicitações de serviço da web acontecerão em paralelo e de maneira não bloqueadora, onde o thread principal fica liberado para processar quaisquer outras solicitações de entrada no servidor.200 OK500 InternalServerError

O Play evita muitos dos problemas de trabalhar com código assíncrono, como retornos de chamada, causando o conhecido inferno de retorno de chamada predominante em muitas outras linguagens. Scala faz isso fazendo uso de conceitos funcionais importantes, como composição futura monádica neste exemplo.

Conclusão

Esse tipo de modelo permite que o Play forneça uma abordagem escalonável para implementar um aplicativo que pode lidar com muitas solicitações externas de maneira eficiente, enquanto mantém a maior parte da complexidade do seu código.

Embora considerar a adoção de uma nova linguagem possa ser uma decisão difícil como é o caso do Scala, onde existe uma forte curva de aprendizado, vale a pena examinar se seu aplicativo pode se beneficiar ao considerar um paradigma diferente de programação que pode ser mais adequado para o seu domínio do problema.

Para ler mais sobre a filosofia do Play, visite este link .

Referências