<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>blog.caelum.com.br &#187; nio</title>
	<atom:link href="http://blog.caelum.com.br/tag/nio/feed/" rel="self" type="application/rss+xml" />
	<link>http://blog.caelum.com.br</link>
	<description>blog dos desenvolvedores da Caelum</description>
	<lastBuildDate>Thu, 09 Feb 2012 13:04:59 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.3.1</generator>
		<item>
		<title>Trabalhando com arquivos do Java IO ao NIO 2</title>
		<link>http://blog.caelum.com.br/evolucao-do-java-io-ao-ni/</link>
		<comments>http://blog.caelum.com.br/evolucao-do-java-io-ao-ni/#comments</comments>
		<pubDate>Wed, 10 Aug 2011 21:59:09 +0000</pubDate>
		<dc:creator>Mário Amaral</dc:creator>
				<category><![CDATA[Inovação]]></category>
		<category><![CDATA[Java]]></category>
		<category><![CDATA[arquivos]]></category>
		<category><![CDATA[assincrono]]></category>
		<category><![CDATA[File]]></category>
		<category><![CDATA[filesystem]]></category>
		<category><![CDATA[java 7]]></category>
		<category><![CDATA[java.io]]></category>
		<category><![CDATA[java.nio]]></category>
		<category><![CDATA[nio]]></category>

		<guid isPermaLink="false">http://blog.caelum.com.br/?p=4511</guid>
		<description><![CDATA[Durante anos, trabalhar com arquivos em Java foi muito trabalhoso. Precisávamos conhecer e interagir com diversas classes do pacote java.io a fim de realizar tarefas simples, como ler um arquivo ou simplesmente copiá-los de uma pasta para outra. Alguns projetos open source sugiram para facilitar essas tarefas, como o Commons-IO da Apache. Conforme a linguagem <a href="http://blog.caelum.com.br/evolucao-do-java-io-ao-ni/#more-4511'" class="more-link">more &#187;</a>]]></description>
			<content:encoded><![CDATA[<p style="float:right; margin:0 0 10px 15px; width:240px;">
		<img src="http://caelum.wpengine.netdna-cdn.com/wp-content/uploads/2011/08/diretório.png" width="240" />
		</p><p>Durante anos, trabalhar com arquivos em Java foi muito trabalhoso. Precisávamos conhecer e interagir com diversas classes do pacote <code>java.io</code> a fim de realizar tarefas simples, como ler um arquivo ou simplesmente copiá-los de uma pasta para outra. Alguns projetos open source sugiram para facilitar essas tarefas, como o <a title="Commons IO" href="http://commons.apache.org/io/">Commons-IO</a> da Apache.</p>
<p>Conforme a linguagem foi evoluindo, estas tarefas foram se tornando mais fáceis de serem realizadas sem a necessidade de bibliotecas externas, graças aos recursos incorporados na API padrão. Vamos acompanhar como foi parte dessa evolução, usando o exemplo de copiar uma árvore de diretórios.</p>
<p>A seguir, vemos a árvore de diretórios que iremos copiar:</p>
<p><a href="http://caelum.wpengine.netdna-cdn.com/wp-content/uploads/2011/08/diret%C3%B3rio.png"><img class="alignnone size-medium wp-image-4530" title="arvore de diretórios" src="http://caelum.wpengine.netdna-cdn.com/wp-content/uploads/2011/08/diret%C3%B3rio-300x239.png" alt="" width="300" height="239" /></a></p>
<p>É uma arvore de arquivos comum. Antes de copiar a árvore inteira, vamos ver o código para copiar um arquivo sozinho:</p>
<pre class="brush: java; title: ; notranslate">
// Arquivos que iremos copiar
File origem = new File(&quot;/home/caelum/Java/vraptor.zip&quot;);
File destino = new File(&quot;/home/caelum/Java/vraptor-copia.zip&quot;);

// abrimos os streams para leitura/escrita
FileInputStream fis = new FileInputStream(origem);
FileOutputStream fos = new FileOutputStream(destino);

// Obtém os canais por onde lemos/escrevemos nos arquivos
FileChannel inChannel = fis.getChannel();
FileChannel outChannel = fos.getChannel();

// copia todos o conteúdo do canal de entrada para o canal de saída
outChannel.transferFrom(inChannel, 0, inChannel.size());

// fecha os streams/channels usados...
</pre>
<p>Cada <code>FileInputStream</code>/<code>FileOutputStream</code> recebe um objeto do tipo <code>File</code>, que representa o local do arquivo no disco. Após isso criamos canais por onde copiaremos o conteudo do arquivo de origem para o de destino. Usar <code>FileChannels</code> para realizar a cópia pode ser bem mais eficiente do que se lessemos/escrevessemos utilizando os antigos <em>streams</em> diretamente. Essa API não-blocante foi adicionada ao Java 1.4, em 2001.</p>
<p>Para copiar a estrutura de diretórios é muito mais simples, basta chamar o método <code>mkdirs</code> da classe <code>File</code>, que cria o diretório e toda a árvore necessária:</p>
<pre class="brush: java; title: ; notranslate">
File novoDiretorio = new File(&quot;/home/caelum-copia/Java&quot;);
novoDiretorio.mkdirs(); //cria o diretório &quot;caelum-copia&quot; e o diretório &quot;Java&quot; dentro da pasta /home/
</pre>
<p>Mas isso só cria o diretório vazio, sem nenhum arquivo dentro. Para copiar o conteúdo, primeiro precisamos saber o que há dentro dele, para isso usamos o método <code>File.listFiles</code>, que nos retorna um array com todas as entradas do diretório, tanto arquivos quanto subdiretórios:</p>
<pre class="brush: java; title: ; notranslate">
File raiz = new File(&quot;/home/mario/Documents/caelum/Java&quot;);
File[] files = raiz.listFiles();

for (File file : files) {
  // copia os arquivos / subdiretórios
}
</pre>
<p>Precisamos repetir esta operação para cada subdiretório encontrado. A melhor solução neste caso é realizar uma invocação recursiva para copiar o conteúdo de cada subdiretório encontrado.</p>
<pre class="brush: java; title: ; notranslate">
public void copiarArquivos(File origem, File destino) {
    // verifica se estamos tentando copiar um diretório
    if (origem.isDirectory()) {
      File[] files = origem.listFiles();

      for (File file : files) {
        // invocacao recursiva de cada item de dentro do diretorio
        copiaArquivos(file, destinoEquivalente);
      }
    } else {
      // copia o arquivo usando os streams/channels
    }
}
</pre>
<p>Juntando tudo que vimos até aqui, temos um método de cópia de diretórios. Não é um código simples, pois além de usar recursão para realizar a cópia dos subdiretórios, ainda envolve várias diferentes classes na cópia dos arquivos. Também deveria ser refatorado para 2 ou 3 métodos.</p>
<p>Existem diversos outros problemas com as antigas classes do <code>java.io</code>. Podemos pegar como exemplo a classe <code>File</code>, que tem alguns dos métodos para manipulação de arquivos, mas a maioria desses métodos não lança exceptions quando falham, e podem funcionar diferentemente em cada sistema operacional.</p>
<p>Vamos tentar deletar um diretório que possui arquivos dentro. O código segue abaixo:</p>
<pre class="brush: java; title: ; notranslate">
File root = new File(&quot;/home/caelum&quot;);
boolean deletou = root.delete(); // retorna false...
</pre>
<p>Sabemos que o diretório não foi removido pelo retorno do método, mas é dificil saber o motivo, se é um problema de acesso, se o diretório que não está vazio, se não existe, etc., ficando para o programador a responsabilidade de descobrir o que aconteceu.</p>
<p>A partir do Java 7, foi introduzida uma nova API no pacote <a title="java.nio.file" href="http://download.oracle.com/javase/7/docs/api/index.html?java/nio/file/package-summary.html" target="_blank">java.nio.file</a>, visando reduzir a complexidade das operações realizadas em arquivos. A nova classe <code>Files</code> possui diversos métodos utilitários para manipular arquivos, lançando exceptions quando ocorre algum erro na operação, possibilitando ao desenvolvedor entender de forma rápida o motivo da falha. Os métodos de <code>Files</code> utilizam a classe <code>Path</code>, uma nova interface utilizada para representar entradas de arquivos/diretórios no sistema operacional.</p>
<p>O código para deletar o diretório ficaria da seguinte maneira:</p>
<pre class="brush: java; title: ; notranslate">
Path rootPath = Paths.get(&quot;/home/caelum&quot;);
Files.delete(rootPath); // o que acontece aqui?
</pre>
<p>A classe <code>Paths</code> é uma fábrica de <code>Path</code>. O resultado desse código, é uma exception:</p>
<pre class="brush: java; title: ; notranslate">
Exception in thread &quot;main&quot; java.nio.file.DirectoryNotEmptyException: /home/caelum
</pre>
<p>Vamos alterar nosso método <code>copiaArquivos</code> para utilizar as novas classes do Java 7:</p>
<pre class="brush: java; title: ; notranslate">
public void copiarArquivos(Path origem, Path destino) throws IOException {
  // se é um diretório, tentamos criar. se já existir, não tem problema.
  if(Files.isDirectory(origem)){
    Files.createDirectories(destino);

    // listamos todas as entradas do diretório
    DirectoryStream entradas = Files.newDirectoryStream(origem);

    for (Path entrada : entradas) {
      // para cada entrada, achamos o arquivo equivalente dentro de cada arvore
      Path novaOrigem = origem.resolve(entrada.getFileName());
      Path novoDestino = destino.resolve(entrada.getFileName());

      // invoca o metodo de maneira recursiva
      copiarArquivos(novaOrigem, novoDestino);
    }
  } else {
    // copiamos o arquivo
    Files.copy(origem, destino);
  }
}
</pre>
<p>A estrutura do algoritmo é a mesma utilizada anteriormente, mas agora o número de classes envolvidas é muito menor. Para copiar um arquivo, apenas chamamos o método <code>Files.copy</code>, não precisando mais trabalhar com <em>streams</em> e <em>channels</em> nesses casos.</p>
<p>Ainda é trabalhoso percorrer um diretório inteiro dessa maneira, mas podemos utilizar a nova interface <a title="FileVisitor" href="http://download.oracle.com/javase/7/docs/api/index.html?java/nio/file/FileVisitor.html"><code>FileVisitor</code></a>, uma implementação do pattern <a title="Visitor" href="http://en.wikipedia.org/wiki/Visitor_pattern">Visitor</a>, para percorrer toda a árvore de diretórios de maneira mais simples. Os métodos dessa interface são</p>
<ul>
<li><code>preVisitDirectory</code> Chamado antes de entrar em um diretório</li>
</ul>
<ul>
<li><code>postVisitDirectory</code> Chamado depois de passar por todos arquivos/subdiretórios de um diretótio</li>
</ul>
<ul>
<li><code>visitFile</code> Chamado para cada arquivo</li>
</ul>
<ul>
<li><code>visitFileFailed</code> Chamado se acontecer alguma exception em algum dos outros métodos</li>
</ul>
<p>Todos os métodos retornam <code>FileVisitResult</code>, um <code>enum</code> com os as opções para continuar ou não percorendo a árvore de diretórios.</p>
<p>Vamos então implementar um <code>FileVisitor</code> para copiar nosso diretório. Nem sempre precisamos de todos os métodos da interface; neste caso, podemos estender a classe <code>SimpleFileVisitor</code> e apenas sobreescrever os métodos que realmente necessitamos:</p>
<pre class="brush: java; title: ; notranslate">
public class CopiadorDeArquivos extends SimpleFileVisitor {
  private Path origem;
  private Path destino;

  public CopiadorDeArquivos(Path origem, Path destino) {
    this.origem = origem;
    this.destino = destino;
  }

  public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs)
          throws IOException {
    copiaPath(dir);
    return FileVisitResult.CONTINUE;
  }

  public FileVisitResult visitFile(Path file, BasicFileAttributes attrs)
          throws IOException {
    copiaPath(file);
    return FileVisitResult.CONTINUE;
  }

  private void copiaPath(Path entrada) throws IOException {
    // encontra o caminho equivalente na árvore de cópia
    Path novoDiretorio = destino.resolve(origem.relativize(entrada));
    Files.copy(entrada, novoDiretorio);
  }
}
</pre>
<p>Na classe acima, usamos apenas dois métodos, o <code>visitFile</code>, chamado para copiar cada arquivo existente na árvore, e o <code>preVisitDirectory</code>, chamado para criar o diretório. Para executar a cópia, faríamos:</p>
<pre class="brush: java; title: ; notranslate">
Path origem = Paths.get(&quot;/home/caelum&quot;);
Path destino = Paths.get(&quot;/home/caelum-copia&quot;);

Files.walkFileTree(origem, new CopiadorDeArquivos(origem, destino));
</pre>
<p>O método <code>walkFileTree</code> recebe o diretório que será percorido, e um <code>FileVisitor</code>, que irá passar por todos os arquivos.</p>
<p>Apesar de ter tornado alguma operações mais simples, essa nova API de arquivos possui mais métodos estáticos e a maioria recebe uma instância de <code>Path</code>, separando comportamento e dados. Talvez com o futuro Java 8, <a href="http://blog.caelum.com.br/trabalhando-com-closures-no-java-8/">as closures e defender methods</a> tornarão a interface <code>Path</code> mais amigável, sem depender tanto de métodos estáticos.</p>
<p>Além das classes apresentadas, ainda tivemos outros acréscimos na API de NIO, como <a title="leitura de streams de forma assíncrona" href="http://www.ibm.com/developerworks/java/library/j-nio2-1/index.html">leitura/escrita de streams de forma assíncrona</a>, serviços para <a title="monitorar alterações em diretórios" href="http://download.oracle.com/javase/tutorial/essential/io/notification.html">monitorar alterações em diretórios</a> e métodos para ler <a title="metadados de arquivos" href="http://download.oracle.com/javase/tutorial/essential/io/fileAttr.html">metadados de arquivos</a> de maneira simplificadas. Essas novidades são conhecidas como NIO2. Com o passar das versões, o Java traz cada vez mais para suas APIs internas ferramentas necessárias para o dia a dia.</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.caelum.com.br/evolucao-do-java-io-ao-ni/feed/</wfw:commentRss>
		<slash:comments>10</slash:comments>
		</item>
		<item>
		<title>JavaEE 6: contexto assíncrono das Servlets para o Ajax push</title>
		<link>http://blog.caelum.com.br/javaee-6-contexto-assincrono-das-servlets-para-o-ajax-push/</link>
		<comments>http://blog.caelum.com.br/javaee-6-contexto-assincrono-das-servlets-para-o-ajax-push/#comments</comments>
		<pubDate>Mon, 27 Sep 2010 22:18:05 +0000</pubDate>
		<dc:creator>Paulo Silveira</dc:creator>
				<category><![CDATA[Java]]></category>
		<category><![CDATA[ajax]]></category>
		<category><![CDATA[ajax reverso]]></category>
		<category><![CDATA[escalabilidade]]></category>
		<category><![CDATA[jee6]]></category>
		<category><![CDATA[nio]]></category>
		<category><![CDATA[servlets]]></category>

		<guid isPermaLink="false">http://blog.caelum.com.br/?p=3067</guid>
		<description><![CDATA[Desenvolvedores de backend que precisam de muita escalabilidade já conhecem os truques de IO não-blocante há muito tempo: selector, poll e epoll no Linux, kqueue no MacOS e BSD e I/O Completion Ports no Windows. A palestra do Renato Lucindo no QConSP abordava questões e soluções que podiam ser implementadas com essas abordagens. Curiosamente são <a href="http://blog.caelum.com.br/javaee-6-contexto-assincrono-das-servlets-para-o-ajax-push/#more-3067'" class="more-link">more &#187;</a>]]></description>
			<content:encoded><![CDATA[<p style="float:right; margin:0 0 10px 15px; width:240px;">
		<img src="http://caelum.wpengine.netdna-cdn.com/wp-content/uploads/2010/09/ajax.jpg" width="240" />
		</p><p><img src="http://caelum.wpengine.netdna-cdn.com/wp-content/uploads/2010/09/ajax-150x150.jpg" alt="" title="ajax" width="150" height="150" align="left" class="size-thumbnail wp-image-3246" />Desenvolvedores de backend que precisam de muita escalabilidade já conhecem os truques de IO não-blocante há muito tempo: selector, poll e epoll no Linux, kqueue no MacOS e BSD e I/O Completion Ports no Windows. A <a href="http://blog.lucindo.com.br/2010/09/27/qcon-sp-2010/">palestra do Renato Lucindo no QConSP</a> abordava questões e soluções que podiam ser implementadas com essas abordagens. Curiosamente são padrões e implementações já bastante antigos, e agora surgem novamente com força. Mas onde o IO não-blocante e estratégias de conexão podem nos ajudar numa aplicação web do dia a dia? No Ajax reverso.</p>
<p>Considerando o exemplo trivial de ter de atualizar o valor de um produto em leilão a cada alteração de preço, podemos fazer com que o browser dispare uma requisição Ajax a cada 5 segundos, e, caso haja alguma modificação no preço, o servidor vai responder o valor novo e via javascript atualizamos esse valor. Essa é a implementação mais básica de ajax reverso, o polling, dando a impressão para o usuário de que a aplicação está atualizada com o servidor. A desvantagem aqui é que num intervalo de cerca de 5 segundos, você pode estar vendo dados <em>stale</em>, o que é aceitável para grande maioria das aplicações.</p>
<p>Para  algo mais sofisticado, precisamos realmente fazer um push do servidor: quando houver dados, mandamos para o cliente. Muitos se referem as técnicas de Ajax push como comet, e são duas as mais usadas: fazer um long polling, e o servidor segura a requisição aberta por alguns segundos ou até ter algum dado para enviar; ou fazer um verdade streaming, mantendo a conexão aberta por tempo indeterminado, e ir enviando os dados novos para o cliente na medida que houver. Em ambos os casos podemos facilmente implementar o cliente, mas como fazer no servidor?</p>
<p>No servidor, precisamos manter a socket aberta com o cliente. Em uma implementação ingênua, teremos, para cada <code>HttpServletResponse</code>, uma thread em <code>waiting</code>. Quando o evento que estamos esperando for disparado, fazemos o <code>notify</code>/<code>notifyAll</code> necessário (ou ainda usar <code>BlockingQueue</code>s). Essa implementação de <em>thread-per-request</em> é bastante cara no Java, tanto pelo footprint de memória quanto por criar native threads. Quando o número de clientes aumentar, a grande quantidade de threads vai atrapalhar bastante o desempenho do sistema, mesmo se praticamente todas as conexões estiverem ociosas, que é um caso bastante frequente para o nosso cenário.</p>
<p>Em muitos casos o melhor será usar algumas poucas (muitas vezes apenas uma) threads que façam o event loop e verifiquem se algum <code>SocketChannel</code> daquele <code>Selector</code> está pronto para leitura/escrita, e realize a operação correspondente caso necessário. Implementar isso com <code>java.nio</code> não é difícil, e mais ainda, os servlet containers começaram a implementar essa estratégia e fornecer abstrações bem mais simples para não ter de trabalhar com event loops nem máquinas de estado. O <a href="http://tomcat.apache.org/tomcat-6.0-doc/aio.html">Tomcat 6 já trazia uma abstração para Comet</a> com diversos callbacks para o estado de cada socket, enquanto <a href="http://docs.codehaus.org/display/JETTY/Continuations">o Jetty 6 já fazia o mesmo através de uma implementação limitada de continuations</a>, porém bastante simples. Em ambos a ideia era fornecer uma maneira de suspender e continuar o processamento daquela requisição, e que, enquanto o processamento estivesse suspenso, aquela thread não entrasse em estado blocked, podendo voltar a processar outras requisições que possuíssem sockets prontas para ler/escrever sem contenção.</p>
<p>Desde novembro de 2009 não precisamos mais adotar uma solução específica de servlet container: na <a href="http://jcp.org/aboutJava/communityprocess/final/jsr315/index.html">Servlet 3</a> temos suporte ao que foi batizado de contexto assíncrono. Se precisamos fazer um push para todos os clientes conectados, basta avisarmos ao container que não queremos que a requisição termine ao fim do método <code>service</code> (e, portanto, do <code>doGet</code>, <code>doPost</code>, etc). Fazemos isso com um simples <code>req.startAsync()</code>. Depois adicionamos o contexto devolvido em uma coleção, para poder fazer o broadcast do evento que os clientes estão todos aguardando.</p>
<pre class="brush: java; title: ; notranslate">
	private Queue&lt;AsyncContext&gt; clients = new ConcurrentLinkedQueue&lt;AsyncContext&gt;();

	protected void doGet(HttpServletRequest req, HttpServletResponse arg1)
			throws ServletException, IOException {
		AsyncContext ctx = req.startAsync();
		ctx.setTimeout(3000000);
		clients.add(ctx);
		System.out.println(&quot;novo cliente conectou.&quot; );
	}
</pre>
<p>Você já pode rodar esse teste com seu servlet container (<a href="http://tomcat.apache.org/download-70.cgi">Tomcat 7</a> e <a href="http://dist.codehaus.org/jetty/">Jetty 8</a> possuem suporte). Para isso falta <a href="http://blog.caelum.com.br/java-ee6-comecando-com-as-servlets-3-0/">configurar a Servlet através de anotações</a> para que ela tenha suporte a chamada assíncrona:</p>
<pre class="brush: java; title: ; notranslate">
@WebServlet(urlPatterns = { &quot;/subscribe&quot; }, asyncSupported = true)
public class ChatServlet extends HttpServlet {
</pre>
<p>Se você fizer uma requisição para <code>/subscribe</code> do seu servidor, perceberá que o browser ficará em aguardo de uma resposta, já que o servidor não encerrou a requisição. Queremos disparar uma mensagem para todos os clientes que estiverem esperando nosso push (que tiver feito o &#8220;<em>subscribe</em>&#8220;). Para isso, vamos fazer com que o post em <code>/subscribe</code> envie uma mensagem. Por enquanto vamos adicionar essa mensagem no fim de nossa fila, com um id próprio:</p>
<pre class="brush: java; title: ; notranslate">
	private BlockingQueue&lt;String&gt; messages = new LinkedBlockingQueue&lt;String&gt;();
	private AtomicInteger contador = new AtomicInteger();

	protected void doPost(HttpServletRequest req, HttpServletResponse arg1)
			throws ServletException, IOException {
		System.out.println(&quot;enviando mensagem para todos cliente&quot;);
		messages.add(String.format(&quot;mensagem número %d %n&quot;, contador.incrementAndGet()));
	}
</pre>
<p>Precisamos que alguém seja responsável por verificar se há mensagens novas na fila e, caso positivo, enviar essa mensagem a todos os clientes da nossa lista de <code>AsyncContext</code>. Faremos isso numa thread que será iniciada durante a criação de nossa servlet, e ficará esperando por mensagens novas no método blocante <code>BlockingQueue.take</code>:</p>
<pre class="brush: java; title: ; notranslate">
public void run() {
	while (true) {
		final String message = messages.take();
		for (final AsyncContext ctx : clients) {
			public void run() {
				PrintWriter writer = ctx.getResponse().getWriter();
				writer.println(message);
				writer.flush();
			}
		}
	}
}
</pre>
<p>Removi o tratamento das checked exceptions por uma questão de legibilidade, e o <a href="http://github.com/peas/asyncservlets-test/blob/master/src/main/java/br/com/caelum/chat/ChatServlet.java">código completo desta ChatServlet pode ser encontrada aqui</a>.</p>
<p>E como realizar um teste com milhares de clientes? Isso pode ser um problema: caso você utilize alguma biblioteca  que faça  <em>thread-per-request</em>, não vai conseguir abrir muitos clientes simultaneamente, dado o número excessivo de threads criadas. Você vai precisar fazer um cliente usando <code>java.nio</code> puro, ou ainda em conjunto com alguma biblioteca http que utilize a API não-blocante. Esse é o caso das novas versões do <a href="http://hc.apache.org/httpcomponents-core-ga/index.html">Apache Http Core</a>, e <a href="http://github.com/peas/asyncservlets-test/blob/master/src/main/java/br/com/caelum/chat/NHttpClient.java">aqui há um pequeno teste de exemplo</a>.</p>
<p>Você ficará bastante impressionado ao ver que uma máquina caseira pode suportar mais de 5 mil clientes simultâneos com o Jetty 8, e que só vai parar nesse número provavelmente pelo <a href="http://www.cyberciti.biz/faq/linux-increase-the-maximum-number-of-open-files/">limite padrão de arquivos abertos</a> no seu sistema operacional.</p>
<p>Economizar recursos caros, como threads, conexões e descritores de arquivos, é sempre uma preocupação. Na Servlet 3 conseguimos balancear esses recursos, minimizando o uso de memória e processamento, porém pagando um pouco mais alto no uso de descritores abertos, mas que ajudam muito nesse cenário de ajax reverso com push.</p>
<p>Expressivos agradecimentos ao <a href="http://blog.lucindo.com.br/">Renato Lucindo</a> e <a href="http://zenmachine.wordpress.com/">Gleicon Moraes</a> pelo aprendizado, diversas explicações, conversas e correções.</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.caelum.com.br/javaee-6-contexto-assincrono-das-servlets-para-o-ajax-push/feed/</wfw:commentRss>
		<slash:comments>10</slash:comments>
		</item>
		<item>
		<title>Melhorando o GUJ: Jetty, NIO e load balancing</title>
		<link>http://blog.caelum.com.br/melhorando-o-guj-jetty-nio-e-load-balancing/</link>
		<comments>http://blog.caelum.com.br/melhorando-o-guj-jetty-nio-e-load-balancing/#comments</comments>
		<pubDate>Fri, 27 Jun 2008 10:41:36 +0000</pubDate>
		<dc:creator>Fabio Kung</dc:creator>
				<category><![CDATA[Arquitetura]]></category>
		<category><![CDATA[Caelum]]></category>
		<category><![CDATA[escalabilidade]]></category>
		<category><![CDATA[guj.com.br]]></category>
		<category><![CDATA[httpd]]></category>
		<category><![CDATA[jetty]]></category>
		<category><![CDATA[load balancing]]></category>
		<category><![CDATA[nginx]]></category>
		<category><![CDATA[nio]]></category>
		<category><![CDATA[reverse proxy]]></category>
		<category><![CDATA[servlet container]]></category>
		<category><![CDATA[tomcat]]></category>

		<guid isPermaLink="false">http://blog.caelum.com.br/2008/06/27/melhorando-o-guj-jetty-nio-e-load-balancing/</guid>
		<description><![CDATA[Durante boa parte da vida do GUJ.com.br, na sua segunda versão (screenshot acima), o site sofreu diversas quedas e passou por muitos períodos de lentidão, mesmo depois de ter migrado para um servidor dedicado. A grande verdade é que por um bom tempo ficamos devendo a devida atenção ao deployment do GUJ. Sempre que um <a href="http://blog.caelum.com.br/melhorando-o-guj-jetty-nio-e-load-balancing/#more-225'" class="more-link">more &#187;</a>]]></description>
			<content:encoded><![CDATA[<p><center><a href='http://caelum.wpengine.netdna-cdn.com/wp-content/uploads/2008/06/guj2.png' title='GUJ2'><img style="display:inline" src='http://caelum.wpengine.netdna-cdn.com/wp-content/uploads/2008/06/guj2.png' alt='GUJ2' width="500" height="132" border="none" /></a></center>
<p/>
<p>Durante boa parte da vida do <a href="http://www.guj.com.br">GUJ.com.br</a>, na sua segunda versão (screenshot acima), o site sofreu diversas quedas e passou por muitos períodos de lentidão, mesmo depois de ter migrado para um servidor dedicado. A grande verdade é que por um bom tempo ficamos devendo a devida atenção ao deployment do GUJ. Sempre que um problema acontecia alguém simplesmente reiniciava o servidor, sem investigar as causas reais do problema com profundidade.</p>
<p>Dada a relação próxima que a Caelum sempre teve com o site, já que dois dos fundadores do GUJ são também os fundadores da Caelum, resolvemos assumir de vez a posição de <strong>criadores</strong> do GUJ. A Caelum agora é a <strong>patrocinadora e mantenedora oficial</strong> do GUJ. Recentemente andamos gastando algum tempo, tentando acabar de vez com estes problemas que o GUJ a tanto tempo sofre. Felizmente, a melhora já é bem perceptível!</p>
<p>Há algum tempo atrás, o GUJ ficava esporadicamente muito lento. Para resolver estes problemas de lentidão, o primeiro passo foi conseguir um servidor dedicado, pago pelos anúncios espalhados pelo site. Já faz tempo que o GUJ roda neste servidor dedicado e desde então a performance se tornou quase sempre aceitável.</p>
<p>Porém, o servidor dedicado não resolveu todos os problemas, já que <code>java.lang.OutOfMemoryError</code> sempre foi o principal problema enfrentado pelo GUJ. Sempre desconfiamos que o culpado poderia ser o código do próprio GUJ, ou até do JFórum, que deveriam conter algum <a href="http://en.wikipedia.org/wiki/Memory_leak">vazamento de memória</a>.</p>
<p>Alguns desenvolvedores da Caelum já tiveram ótimas experiências passadas com o <a href="http://jetty.mortbay.org">Jetty</a>, que é um excelente servidor web e servlet contêiner. Foi, inclusive, um dos servidores Java pioneiros a usar conectores <a href="http://java.sun.com/javase/6/docs/api/java/nio/package-summary.html">NIO (java.nio)</a>. O Jetty foi desenhado para ser embutido em outras aplicações Java e portanto é extremamente leve. Consome bem menos memória que o Tomcat, seu concorrente mais conhecido, e não deixa nada a desejar nas outras características.</p>
<p>O servidor dedicado do GUJ tem 2GB de memória RAM e o Tomcat estava configurado inicialmente para ter um heap máximo de 768MB (<code>-Xmx768M</code>). A primeira tentativa foi aumentar o heap máximo (<code>-Xmx1024M</code>), mas mesmo assim o temível <code>OutOfMemoryError</code> insistia em aparecer.</p>
<p>Resolvemos então dar uma chance ao Jetty. Já que ele consome menos memória, acreditamos que os <code>OutOfMemoryError</code> demorariam mais a aparecer. Logo ao subir, o jetty ocupa 4% da memória do servidor. O impressionante é que em duas semanas no ar, o uso total de memória não passou de 12%. Na verdade, o uso de memória estabilizou em 9% do total, porém recentemente fizemos alguns testes de carga no servidor do guj, com mais do que o dobro do número de conexões que o guj recebe hoje nos períodos de pico. Isto fez com que o uso de memória do Jetty pulasse para 12%. Uma diferença <strong>enorme</strong> da quantidade usada pelo Tomcat, que chegava facilmente a 80%.</p>
<p><center><a href="http://caelum.wpengine.netdna-cdn.com/wp-content/uploads/2008/06/top-guj.png" title='uso de memória do Jetty'><img src='http://caelum.wpengine.netdna-cdn.com/wp-content/uploads/2008/06/top-guj.png' alt='uso de memória do Jetty' width="480" height="117" border="none" /></a></center>
<p/>
<p>Já temos algum tempo rodando, sem problemas de memória e com o jetty estável usando 9-12% do total da memória do servidor. Já estamos até pensando em descartar a possibilidade de existir um vazamento de memória no código do GUJ ou do JForum. Isto tornaria o Tomcat culpado pelos problemas de memória!</p>
<p>A possibilidade de vazamentos de memória no Tomcat (estávamos com a versão 6.0.14, sem o APR e com Linux Kernel 2.6.22) deste tamanho é um pouco assustadora. Com a base de usuários que o Tomcat tem, muito possivelmente alguém já teria pego este problema muito antes de nós. Provavelmente o problema é de alguma configuração mal feita no Tomcat do GUJ.</p>
<p>Fato é que o Jetty foi uma tentativa que deu certo e o problema está aparentemente resolvido. O Jetty mostrou um uso mais alto de CPU que o Tomcat, chegando a picos de 60% da capacidade total de processamento do servidor, que possui 2 processadores. Através dos testes de carga que fizemos, temos percebido que o Jetty usa bastante CPU para responder diversas requisições simultâneas. Isso não chega a ser um problema, já que os dois processadores que o servidor possui são mais do que suficientes para atender a quantidade de requisições por segundo que o GUJ recebe hoje.</p>
<p>Apesar de termos <a href="http://jira.codehaus.org/browse/JETTY-256">algumas suspeitas</a>, ainda não investigamos a razão do alto uso de CPU. Este é um ponto a ser abordado, caso o GUJ tenha problemas com disponibilidade de processamento algum dia.</p>
<p>Mesmo não representando um problema hoje, esta situação preocupa já que o MySQL rodando na mesma máquina também costuma usar bastante CPU. Isto pode se tornar um problema em algum dia que tenha um pouco mais de requisições por segundo do que o comum, já que o uso de CPU chegava as vezes perto do limite. Felizmente, grande parte das requisições ao servidor do GUJ são para conteúdo estático: imagens, JavaScripts, arquivos CSS, download de pdfs (dos artigos), entre outros. Todo esse conteúdo estático era servido pelo Jetty, contribuindo para o alto uso de CPU. Resolvemos então tentar um <a href="http://en.wikipedia.org/wiki/Reverse_proxy">proxy reverso</a> na frente do Jetty, especificamente para servir este conteúdo estático.</p>
<p><center><img src='http://caelum.wpengine.netdna-cdn.com/wp-content/uploads/2008/06/reverse_proxy.png' alt='proxy reverso servindo conteúdo estático' /></center>
<p/>
<p>Existem diversas alternativas de <a href="http://en.wikipedia.org/wiki/Reverse_proxy">proxy reverso</a> e a primeira a ser considerada quase sempre é o conhecido servidor web <a href="http://httpd.apache.org/">Apache Httpd</a>, com a adição do <a href="http://httpd.apache.org/docs/2.0/mod/mod_proxy.html">mod_proxy</a>. É uma excelente solução e existe bastante documentação para fazer tudo funcionar. No entanto, faz um tempo que eu já estava querendo testar o servidor <a href="http://nginx.net/">russo</a> <a href="http://wiki.codemongers.com/Main">Nginx</a>, tão falado pelo pessoal da <a href="http://engineyard.com/">Engine Yard</a>.</p>
<p>A desvantagem do Nginx para o Apache Httpd é a documentação não tão extensa. Esse é um grande problema para a comunidade do Nginx, que tem se esforçado em traduzir grande parte do que está escrito em russo. Apesar disso, o Nginx é impressionante e superou todas as nossas expectativas. Além de ser extremamente rápido, consome <strong>pouquíssimos</strong> recursos. Cada um dos processos (1 master + 5 workers) consome na maior parte do tempo apenas <strong>1%</strong> de CPU e <strong>0.2%</strong> da memória disponível do nosso servidor. Incrível!</p>
<p>O conteúdo estático do GUJ agora é todo servido pelo Nginx; as requisições nem chegam ao Jetty. Além disso, o Nginx, como um bom proxy reverso, oferece diversas otimizações. Uma essencial para o GUJ é o preenchimento automático dos cabeçalhos HTTP de cache, sugerindo aos browsers que façam cache do conteúdo estático. Agora o consumo de CPU diminuiu consideravalmente.</p>
<p><center><a href='http://caelum.wpengine.netdna-cdn.com/wp-content/uploads/2008/06/nginx.png' title='uso de recursos do nginx no GUJ'><img src='http://caelum.wpengine.netdna-cdn.com/wp-content/uploads/2008/06/nginx.png' alt='uso de recursos do nginx no GUJ' /></a></center>
<p/>
<p>Não fizemos nenhum comparativo científico de performance, mas a melhora dos tempos de resposta do GUJ está visível. Frequentemente tenho a sensação de que o site está <em>&#8220;voando&#8221;</em>.</p>
<p>Configuramos também no Nginx a <a href="http://www.nginx.eu/nginx-rrd.html">exibição de algumas estatísticas de acesso</a>; tão valiosas ao GUJ. É <strong>assustador</strong> como o padrão de acesso se repete a cada dia, e a cada semana. Repare como o gráfico de requisições por segundo é praticamente <strong>idêntico</strong> para cada dia (com exceção do sábado e domingo, que são parecidos entre si).</p>
<p><center><a href='http://caelum.wpengine.netdna-cdn.com/wp-content/uploads/2008/06/guj-requests-week.png' title='requisições por segundo no GUJ, em uma semana'><img src='http://caelum.wpengine.netdna-cdn.com/wp-content/uploads/2008/06/guj-requests-week.png' alt='requisições por segundo no GUJ, em uma semana' border="none" width="558" height="160"/></a></center>
<p/>
<p>Temos também o interesante gráfico de conexões por segundo, que mostra relação média aproximada de <strong>3 requisições por conexão</strong>.</p>
<p><center><a href='http://caelum.wpengine.netdna-cdn.com/wp-content/uploads/2008/06/guj-connections-week.png' title='conexões por segundo no GUJ, em uma semana'><img src='http://caelum.wpengine.netdna-cdn.com/wp-content/uploads/2008/06/guj-connections-week.png' alt='conexões por segundo no GUJ, em uma semana' border="false" width="558" height="190"/></a></center>
<p/>
<p>Note que a legenda do gráfico está errada, já que deveria mostrar <em>&#8220;connections/sec&#8221;</em>. O pequeno pico que dá para ver no gráfico aparece por causa de alguns testes de carga que fizemos neste dia. Pode ser desconsiderado.</p>
<p>Fizemos ainda algumas experiências com múltiplos servidores Jetty por trás do Nginx, funcionando também como <a href="http://en.wikipedia.org/wiki/Load_balancing_(computing)">balanceador de carga</a>. Espero em breve postar sobre nossa experiência em geral com balanceamento de carga e alguns outros truques que pudemos testar nesta experiência do GUJ e em alguns clientes.</p>
<p>Aproveitando o post, o <a href="http://www.guj.com.br/posts/list/94487.java">GUJ recentemente comemorou</a> a marca de <strong>meio milhão de mensagens</strong>. É um orgulho poder fazer parte desta comunidade, e mais ainda por poder torná-la cada vez melhor.</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.caelum.com.br/melhorando-o-guj-jetty-nio-e-load-balancing/feed/</wfw:commentRss>
		<slash:comments>28</slash:comments>
		</item>
	</channel>
</rss>

