Tuesday, June 6, 2017

Pool de Conexões em Aplicações Web

Aplicações Web que acessam bancos de dados precisam estar preparadas para receber uma avalanche de requisições ou correm sério risco de serem derrubadas. Considere também que estabelecer uma nova nova conexão com o banco de dados para cada requisição do cliente pode ser oneroso e consumir recursos desnecessários tornando esse processo mais lento e diminuindo a performance da aplicação. Imagine, por exemplo, 1 milhão de acessos simultâneos e a própria aplicação ter que ficar abrindo e fechando conexões para cada um...! Esse gargalo pode ser minimizado com uma técnica chamada pooling.

Outra vantagem é que o servidor passa a gerenciar a conexão com o banco de dados, e não mais a aplicação. O software do servidor implementa o conceito de Pool Manager que passa a ser um middleware entre os componentes clientes (uma classe java, uma servlet, EJB, etc) e uma conexão representada com driver JDBC  para algum servidor de banco de dados. Na prática, é criada uma camada adicional entre a aplicação e o servidor de banco de dados, essa camada é o Pool Manager.

O Pool Manager já abre um conjunto de conexões (pool) quando o servidor inicia e, de acordo com o número de requisições à aplicação, procura sempre se antecipar e manter um número maior de conexões disponíveis antes que as mesma sejam solicitadas.

Com o pooling, as operações de conexão são totalmente transparentes à aplicação, de modo que detalhes sobre o banco de dados (usuário, senha, etc), fechamento e abertura de conexões físicas, endereço do servidor de banco de dados, são administradas pelo pool manager. A seguir um exemplo dessa técnica.


Implementando Pooling no servidor GlassFish 4.1 para o MySQL

No MySQL crie um banco de dados chamado Study. Baixe o driver JDBC do MySQL, copie e cole-o no diretório <glassFishHome>/glassfish/lib.

Inicie o servidor GlassFish. Você pode inicia-lo facilmente através de alguma IDE, ou pelo shell (ou prompt) executando o comando abaixo conforme a imagem:
<glassFishHome>/glassfish4.1/bin/ ./asadmin start-domain


Após iniciar o servidor, abra seu browser e acesse a porta 4848, que é o painel de controle visual do servidor:




No menu em árvore à esquerda, navegue por Resources/JDBC/JDBC Connection Pools e clique em New:



No passo 1/2, dê um nome para o seu Pool, escolha javax.sql.Datasource na combobox resource type. E no drive vendor escolha MySql, que é o banco que escolhemos:



Clique em next.
No passo 2/2 marque habilitar ping e agora configuramos os dados da conexão. O painel apresenta duas centenas de parâmetros para informar, mas nosso interesse é apenas nos dados do usuário do banco, senha (se houver), nome do servidor, nome do banco, porta e URL. No meu caso, parâmetros e valores ficaram respectivamente como segue (faça as adpatações necessárias conforme o contexto de seu sistema):

password: 010203
databaseName: study
serverName: localhost
user: root
portNumber: 3306
URL: jdbc:mysql://:3306/study



Clique em Finish. Acesse sua conexão no menu JDBC/Connection Pools e clique em ping para testar a conexão. Se os parâmetros estiverem corretos o ping ira suceder como mostra a imagem:



Agora, no menu esquerdo vá em Resources/JDBC/JDBC Resources e New. Esse será o recurso que será acessado por nossa palicação. Em JNDI Name coloque jdbc/studyDS e na combo box pool name escolha o pool que você acabou de criar, StudyDB e salve.



Agora nosso recurso está disponivel na tabela JDBC Resources do servidor, como jdbc/studyDS. Guarde esse nome pois ele será informado em nossa aplicação.

Acessando o Pool com uma aplicação web

Com uma IDE de su preferência (usei NetBeans 8.2), crie um novo projeto Web Application chamado TestDataSource. Na pasta WEB-INF crie os arquivos web.xml e sun-web.xml. Cole as seguintes tags nos arquivos:

web.xml 
    <distributable/>
    <resource-ref>
        <description>JDBC Connection Pool</description>
        <res-ref-name>jdbc/studyDS</res-ref-name>
        <res-type>javax.sql.ConnectionPoolDataSource</res-type>
        <res-auth>Container</res-auth>
        <res-sharing-scope>Shareable</res-sharing-scope>
    </resource-ref>
sun-web.xml
<sun-web-app>
  <context-root>TestDataSource</context-root>
  <resource-ref>
    <res-ref-name>jdbc/studyDS</res-ref-name>
    <jndi-name>jdbc/studyDS</jndi-name> 
  </resource-ref>
</sun-web-app>
Neste exemplo o componente que irá solicitar conexão ao Pool Manager será uma servlet. Crie uma servlet mapeada para a url NewServlet:
@WebServlet(urlPatterns = {"/NewServlet"})
public class NewServlet extends HttpServlet {

    protected void processRequest(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
      
        response.setContentType("text/html;charset=UTF-8");            

        try {
            PrintWriter out = response.getWriter();          
            Context ctx = new InitialContext();
//nome do recurso no servidor
         DataSource ds = (DataSource) ctx.lookup("java:comp/env/jdbc/studyDS");
//obtem a conexão do pool                       
            Connection connection = ds.getConnection();                       
//saida mostrando o nome do banco de dados acessado           
            out.println(connection.getCatalog());

           connection.close();
        }
        catch (Exception e) {
            e.printStackTrace();
        }
    }

    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {

        processRequest(request, response);
    }

    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {

        processRequest(request, response);
    }
}
Salve, faça o deploy e acesse aplicação: http://localhost:8080/TestDataSource/NewServlet

Se tudo estiver certo, a aplicação mostra o nome do banco de dados, study pelo método getCatalog() do objeto connection:




Repare que em nenhum momento a própria aplicação toma maiores conhecimentos sobre o banco de dados, tais como usuário, senha, driver ou a localização do servidor, ela simplesmente pede uma conexão não importando de onde ou como ela vem, o que representa uma brecha de segurança a menos no sistema.

Existem formas ainda mais otimizadas para acessar o Pool utilizando as especificações JPA e CDI do JavaEE 7 através das anotações @PersistenceContext @Resource!

No comments:

Post a Comment