sábado, 22 de janeiro de 2011

Joomla! Avançado - 2ª edição



Nas principais livrarias do Brasil, a segunda edição do livro Joomla! Avançado, de minha autoria e do Leonardo Mafra.

A primeira edição do livro Joomla! Avançado foi lançada em setembro de 2009. Em menos de um ano todos os exemplares foram vendidos, superando as expectativas dos autores e da editora Novatec. Ganhou destaque em revistas especializadas como a ComputerWorld (Brasil) e foi eleito o livro do mês pela revista TI Digital (atualmente wide).

Em junho do ano passado, a editora entrou novamente em contato com os autores propondo um trabalho de revisão do livro para uma reimpressão. Diante dessa oportunidade, tanto o editor como os autores resolveram ir além da simples revisão, acrescentando mais três capítulos relacionados a tecnologias voltadas para melhorar a interatividade de sistemas Web com seus usuários.

Para saber mais acesse o site sobre o livro clicando aqui.

Onde comprar?

Além das principais livrarias do país, o livro poderá ser adquirido na própria Novatec clicando aqui.


quinta-feira, 24 de julho de 2008

Teste de capacitação de banco de dados com o JMeter

Um dos fatores mais críticos na utilização de um ambiente de banco de dados diz respeito ao seu desempenho. A alteração ou inclusão de uma ou mais consultas em um sistema legado, a implantação de um novo sistema ou o crescimento inesperado no número de usuários simultâneos são fatores que podem comprometer o desempenho de um SGBD.

No que se refere à alteração de um sistema legado, não é raro a área técnica deparar-se com situações em que uma dada aplicação deixa de responder de forma adequada após uma alteração ou inclusão de uma nova operação de banco de dados. Em geral, isso ocorre quando os testes feitos no novo sistema não consideram o uso intensivo por vários usuários simultaneamente. Nesses casos, a equipe de teste ou de desenvolvimento se limitou às questões funcionais do sistema, sem se preocupar com os aspectos de desempenho.

Já em relação à implantação de um novo sistema, é normal que a área técnica seja questionada quanto à capacidade do servidor de banco de dados suportar ou não uma nova aplicação. Nesse caso, é comum encontrar respostas vagas, principalmente quando não se sabe previamente quais tipos de operações de banco de dados serão utilizados pelo novo sistema.

Por fim, têm-se os fatores externos. Por exemplo, uma aplicação que vinha funcionando sem problemas passa a não responder bem somente porque o número de usuários que acessam o sistema simultaneamente aumentou. Normalmente, isso ocorre quando a aplicação não usa os recursos de banco de dados adequadamente. Nessa situação, o problema fica oculto até que um aumento inesperado de demanda ocorra.

No mercado, é possível encontrar alguns fornecedores que podem, de alguma forma, auxiliar o administrador do banco de dados em teste de capacitação. Alguns exemplos são: a Quest Software (www.quest.com), bmc Software (www.bmc.com), a CA tem o Unicenter NeuMICS Resource Management Capacity Planner Option (www.ca.com), OpenSTA (www.openSTA.org) e Apache (www.apache.org). É bom ressaltar que as ferramentas citadas aqui não têm necessariamente as mesmas funcionalidades. Isto é, algumas são bem mais abrangentes que outras e merecem um estudo detalhado para a adequação de suas necessidades. Também não é objetivo deste artigo fazer uma comparação entre elas, já que isso seria muito difícil, dado o foco que cada uma possui.

Dentre os fornecedores citados acima, será apresentado o JMeter da Apache como ferramenta de teste de capacitação de serviços de banco de dados.

O Apache JMeter

O JMeter é uma aplicação implementada em Java projetada para fazer teste de carga de aplicações Cliente/Servidor. Foi originalmente desenhado para realizar testes em aplicações Web, mas, logo foi expandido para fazer outros tipos de testes, tais como em: servidores de banco de dados (via JDBC), servidores de FTP, JNDI, LDAP e Web Services. Com o JMeter, é possível assegurar se um SGBD está capacitado para suportar uma determinada quantidade de usuários simultâneos e estudar o seu comportamento no que tange à escalabilidade. Neste contexto, entende-se por escalabilidade a capacidade de resposta do sistema em relação à demanda de recursos exigidos dele. Aumentar a escalabilidade de um sistema de banco de dados consiste em expandir sua capacidade de processamento e armazenamento de registros, seja melhorando a capacidade do hardware ou otimizando o uso dos recursos do sistema.

O JMeter é uma ferramenta “Open Source” distribuído sobre a licença ASF (veja: http://jakarta.apache.org/jmeter/license.html e http://www.apache.org/licenses/) e seu download pode ser feito diretamente do site da Apache http://jakarta.apache.org/site/downloads/downloads_jmeter.cgi.

Instalação do JMeter

A versão mais recente do JMeter, usada na época da elaboração deste artigo, é a 2.1.1. Para sua instalação serão necessários os seguintes requisitos:

1.

JDK 1.4 ou superior corretamente instalado. Você poderá baixar o ambiente Java diretamente na Sun Microsystem pelo endereço http://java.sun.com/j2se/1.5.0/download.jsp.
2.

Um sistema operacional que execute uma JVM compatível com o ambiente descrito acima. Exemplo: Microsoft Windows (98, NT, 2000, XP), Solaris, AIX, Linux, etc.
3.

Dependendo da carga que será testada, um ou mais equipamentos com boa capacidade de processamento e memória.


O JMeter vem empacotado no formato zip ou tgz (tar). Optamos por fazwer o download no formato zip por ser mais conhecido pela maioria dos usuários.

O segundo passo na instalação do JMeter é descompactar o arquivo em uma pasta. Para ilustrar esse procedimento, optou-se por descompactar o JMeter no diretório raiz do disco C em um ambiente Microsoft. O nome da pasta foi jmeter (C:\jmeter). A Figura 1 ilustra a disposição de diretório após a instalação do JMeter.


Figura 1. Disposição de pastas após a instalação do JMeter.


A instalação do JMeter se limita aos procedimentos descritos acima. Isto é, nenhum ícone ou atalho é criado. Sua utilização será descrita no item a seguir.

Executando o JMeter

No Windows, execute o arquivo jmeter.bat localizado na pasta bin do diretório de instalação do JMeter, conforme ilustrado na Figura 2 (exemplo: c:\jmeter\bin\jmeter.bat). No Linux e outros ambientes UNIX, execute o script jmeter localizado no diretório bin de instalação (exemplo: /usr/local/jmeter/bin/jmeter). A Figura 3 ilustra a execução do JMeter no Windows.


Figura 2. Execução do JMeter no Microsoft Windows.


Após um duplo clique no arquivo jmeter.bat, o programa será apresentado, conforme mostrado na Figura 3.


Figura 3. Apresentação da tela principal do JMeter.


Caso o JMeter não funcione conforme descrito anteriormente, verifique se o seu ambiente Java está instalado corretamente.

Preparando o ambiente para teste de carga de banco de dados

Para usar o JMeter em um teste de stress no banco de dados, é necessária a instalação do driver JDBC implementado para o SGBD que você utiliza. A maioria dos grandes fornecedores de SGBD fornece o seu próprio driver JDBC. Somente para citar alguns, têm-se: IBM DB2, ORACLE, SQL Server, Sybase, PostgreSQL, Firebird e MySQL.

Será visto neste artigo a utilização do JMeter para testes no PostgreSQL. Os mesmos conceitos servem para qualquer outro SGBD, bastando para tanto, a correta instalação do driver JDBC do SGBD a ser usado.

Instalação do driver JDBC do PostgreSQL

Os passos para instalação do drive JDBC do PostgreeSQL são:

1.

Faça o download do JDBC para o PostgreSQL no seguinte site: http://jdbc.postgresql.org/. Na época da elaboração deste artigo, a versão mais estável era 8.1 Build 405. O arquivo baixado foi “postgresql-8.1-405.jdbc3.jar”.


2.

Copie para o diretório c:\jmeter\lib o arquivo postgresql-8.1-405.jdbc3.jar. Se o ambiente de banco de dados for um Oracle, por exemplo, copie os arquivos ojdbc14.jar e classes12.jar para o diretório c:\jmeter\lib. Esses arquivos são distribuídos juntamente com o Oracle.


Uma vez instalado o driver JDBC, o JMeter já estará pronto fazer testes no servidor de banco de dados. Mas antes disso, é necessário elaborar e implantar um plano de teste.

Elaboração do plano de teste

Um plano de teste consiste em um conjunto de passos que serão executados com o objetivo de simular o uso do sistema e coletar métricas dessa execução. Por exemplo:

*

Simular quinze usuários;
*

Cada usuário enviará cinco requisições (comando SQL) ao servidor de banco de dados;
*

Repetir o teste, para cada usuário, cinco vezes;
*

Fazer análise do relatório.

Implementando o plano de teste no JMeter

Antes de implementar o plano de teste descrito no item anterior, será criado um banco de dados no servidor PostgreSQL denominado “educação”, conforme o script descrito na Listagem 1.


Listagem 1. Scripts de Criação de um Banco de Dados para demonstração.

CREATE USER educacao PASSWORD 'educ123'

CREATEDB CREATEUSER

VALID UNTIL 'infinity';


CREATE DATABASE educacao

WITH OWNER = educacao

ENCODING = 'UNICODE'

TABLESPACE = pg_default;


CREATE SCHEMA educacao

AUTHORIZATION educacao;


CREATE TABLE aluno

(

cod_aluno serial NOT NULL,

nis varchar(16),

nome varchar(60),

sexo char(1),

endereco varchar(60),

municipio varchar(30),

fone varchar(30),

filiacao varchar(120),

datanasc date,

CONSTRAINT aluno_pkey PRIMARY KEY (cod_aluno)

)

WITH OIDS;

ALTER TABLE aluno OWNER TO educacao;


O passo seguinte é popular a tabela aluno. Alguns exemplos de registros a serem inseridos nesta tabela estão descritos na Listagem 2.


Listagem 2. Registros a serem inseridos na tabela ALUNOS.

INSERT INTO aluno (nis, nome, sexo, endereco, municipio, fone, filiacao, datanasc) VALUES ( NULL, 'PAULO VICTOR CAVALCANTE DE LIMA GOMES', 'M', NULL, 'SOBRAL', NULL, NULL, NULL);

INSERT INTO aluno (nis, nome, sexo, endereco, municipio, fone, filiacao, datanasc) VALUES ( NULL, 'PAULO DE OLIVEIRA RIBEIRO', NULL, '14', NULL, NULL, 'JOSELIA DE OLIVEIRA', '1995-07-01');

INSERT INTO aluno (nis, nome, sexo, endereco, municipio, fone, filiacao, datanasc) VALUES ( NULL, 'JOSÉ HENRIQUE FERREIRA LOPES', NULL, '15', NULL, NULL, 'ANGELA MARIA FERREIRA LOPES', '1996-12-01');


Finalmente, para implementar no JMeter o plano de teste acima, será necessário usar os seguintes elementos: Thread Group, JDBC Request e Graph Results.
Adicionando um Thread Group

Em um Thread Group é possível informar a quantidade de usuário que você deseja simular e o comportamento desses usuários quanto à utilização de requisições ao SGBD. A Figura 4 ilustra a função de adicionar um Thread Group. Para isso, clique com o botão direito do mouse sobre “Test Plan” ou no Menu Edit  Add  Thread Group.


Figura 4. Adicionando um Thread Group no Jmeter.


Em seguida, uma tela será apresentada para configurar o Thread Group. Preencha os parâmetros do formulário conforme ilustrado na Figura 5.


Figura 5. Preenchendo os parâmetros de um Thread Group.


A Tabela 1 apresenta a descrição dos parâmetros de um Thread Group.


Tabela 1. Parâmetros de um Thread Group.

Parâmetro


Descrição

Name


Nome do Thread Group.

Action to be taken after a Sampler error


Indica que procedimento deve ser feito em caso de erro durante uma requisição do teste. As opções são:



Number of Threads


Cada Thread pode ser considerada como sendo um usuário que irá executar as requisições SQL. Por isso, na Figura 5 definimos o valor 15, que é a quantidade de usuários no nosso plano de teste.

Ramp Up Period


Diz ao JMeter quantos segundos ele tem para colocar todas as Threads em execução. De acordo com a Figura 5, o JMeter levará 15 segundos para executar as 15 Threads lançando uma Thread por segundo.

Loop Count: Forever


Diz ao JMeter o número de vezes que o Thread Group irá executar. Caso a opção Forever for selecionada, o Thread Group será executado indefinidamente. Na Figura 5 definimos 5 loops de acordo com nosso plano de teste.

Scheduler


Quando selecionado, permite agendar um horário de Início e Fim da execução do teste.

Adicionando requisições JDBC (JDBC Request)

A Figura 6 ilustra a navegação do menu do JMeter para adicionar uma requisição (consulta SQL) ao banco de dados, Add  Sampler  JDBC Request. É importante observar a hierarquia onde esses elementos são criados. Neste caso, observe que o JDBC Request está sendo criado embaixo do Thread Group denominado Teste Postgres.


Figura 6. Adicionando requisição de Banco de Dados.


A Figura 7 mostra a consulta SQL que será disparada contra o SGBD. Observe que a consulta preenchida no campo “Query:” simplesmente conta a quantidade de alunos por sexo.


Figura 7. Construção de uma requisição JDBC.


A Tabela 2 apresenta a descrição dos parâmetros do JDBC Request.


Tabela 2. Parâmetros do JDBC Request.

Parâmetro


Descrição

Name


Nome da requisição.

Variable Name


Não usado em nosso teste. É o nome de uma variável que será associada a um pool de conexão.

Query Type


Informa se a requisição será uma consulta de seleção ou uma atualização

Query


É a consulta propriamente dita.


Para concluir, é necessário estabelecer uma conexão com o banco de dados. Para isso, certifique-se que o driver JDBC adequado está instalado no diretório lib do JMeter. O item a seguir mostrará como fazer uma conexão com o banco de dados via JDBC.

Configurando uma conexão JDBC

A Figura 8 ilustra a navegação do menu para adicionar uma conexão com o banco de dados via JDBC. Observe mais uma vez a hierarquia dos elementos criados. Neste caso, a conexão JDBC está abaixo da requisição denominada “Aluno por Sexo”.


Figura 8. Navegação do menu do JMeter para criar uma conexão com o banco de dados.


Os parâmetros necessários para conexão com o banco estão na Tabela 3.


Tabela 3. Parâmetros necessários para conexão JDBC.

Database URL


É a string de conexão com o SGBD. No PostgreSQL segue a seguinte regra: jdbc:postgresql://:porta/

JDBC Driver Class


A classe do driver JDBC: no Postgres é org.postgresql.Driver

Username


nome do usuário

Password


senha


A Figura 9 ilustra o preenchimento dos parâmetros de conexão para o banco de dados criado pelos scripts apresentados no item “Implementando o Plano de Teste no JMeter”.


Figura 9. Parâmetros de conexão com o banco de dados.


A Tabela 4 apresenta a descrição dos principais parâmetros de uma conexão JDBC.


Tabela 4. Parâmetros de uma conexão JDBC.

Parâmetro


Descrição

Name


Nome dado à conexão. É possível criar várias conexões para um único teste.

Variable Name


É o nome dado a uma variável relacionada ao pool de conexão. É possível usar vários pools de conexão, cada um ligado por uma variável.

Não foi utilizado em nosso teste.

Max Number of Connection


Número máximo de conexões admitidas em um pool.

Pool timeout


Tempo máximo em milissegundos que uma conexão deverá ser estabelecida. Ultrapassado esse tempo, uma exceção será disparada (erro).

Validade Query


É uma consulta usada para validar uma conexão.

Database URL


É a string de conexão com o banco de dados.

JDBC Driver Class


Nome completo da classe onde o driver JDBC está implementado. No nosso caso, a biblioteca postgresql-8.1-405.jdbc3.jar possui a classe org.postgresql.Driver implementada.

Username


Nome do usuário criado para conexão com o banco de dados.

Password


Senha do usuário para conexão com o banco de dados.


Os demais parâmetros (não descritos na Tabela 4) devem conter os valores sugeridos pelo JMeter (default).

Até este ponto, já é possível disparar contra o banco as requisições configuradas. No entanto, é necessário coletar as informações enviadas para análise futura. O Item a seguir mostrará como isso pode ser feito.

Gerando resultados para análise

O JMeter possui vários tipos de relatórios para análise dos testes. Será visto nos itens a seguir dois tipos de saída, Graph Results e Aggregate Report.


Adicionando Graph Results

O Graph Results apresenta uma visão gráfica das respostas das requisições feitas ao banco de dados. Dentre várias observações, destaca-se o número de requisições atendidas por minuto (throughput). A Figura 10 ilustra a navegação do menu para adicionar o elemento Graph Results.


Figura 10. Adicionando elementos para análise (Listener Graph Results).


Após a seleção do item do Menu Graph Results, a tela ilustrada na Figura 11 será apresentada.


Figura 11. Layout do Graph Results.



Neste ponto, já é possível executar o teste e verificar algumas métricas como: throughput (requisições por minuto) e tempo médio de resposta de uma requisição em milissegundos (Average). No entanto, como complemento, o item a seguir configura o Aggregate Report com a finalidade detalhar os resultados para análise.


Adicionando Aggregate Report

O Aggregate Report coleta e apresenta em forma de planilha os dados referentes às requisições disparadas contra o banco de dados. Dentre as colunas apresentadas, destacam-se: a quantidade de requisições disparadas, o tempo de resposta médio, máximo e mínimo das requisições, a quantidade de requisições atendidas por segundo, a taxa de erro e a taxa de transferência de dados em KB/Sec.

A Figura 12 ilustra a navegação do menu para adicionar o elemento Aggregate Report.


Figura 12. Adicionando elementos para análise (Listener – Aggregate Report).


A Figura 13 ilustra o resultado de um Aggregate Report.


Figura 13. Layout do Aggregate Report.


Uma vez configurado o Graph Results e o Aggregate Report, podemos agora executar o teste.

Execução do teste

A Figura 14 mostra a execução do plano de teste a partir do menu principal do JMeter.


Figura 14. Execução do plano de teste.

Após iniciar a execução, é possível acompanhar a evolução dos testes visualizando os relatórios. Para isso, clique em Análise 1 – Gráfico (Nome dado à saída Graph Results. Veja Figura 11) ou em Análise 2 – Relatório (Nome dado à saída Aggregate Report. Veja Figura 13).

Adicionando mais operações de banco de dados no plano de teste

Suponha que cada usuário executará por transação mais de uma operação de banco de dados. Isto é, cada requisição feita por um usuário, na realidade demandará duas consultas ao banco de dados. Até o momento foi mostrada uma requisição com somente uma consulta sendo disparada ao banco (veja Figura 7). Deseja-se agora aumentar a carga de uma requisição adicionando mais uma operação. A Figura 15 ilustra a criação de uma segunda consulta, denominada “Aluno Data Nasc Nula”.


Figura 15. Adicionando mais uma consulta ao plano de teste.


Observe que a consulta (campo “Query:” do formulário acima) selecionará todos os aluno cuja data de nascimento (datanasc) contenha o valor nulo (null).

Para concluir, adicione também mais uma conexão com o banco de dados, conforme já mostrado na Figura 9.

Execute novamente o teste conforme mostrado no item anterior. A seguir, tem-se a análise dos resultados do teste.

Análise dos resultados

Uma vez executado o teste, clique nas saídas “Análise 1 – Gráfico” ou “Análise 2 – Relatório”. A Figura 16 apresenta em um formato gráfico a evolução dos principais indicadores de desempenho coletados no teste (“Análise 1 – Gráfico”).


Figura 16. Analise do resultado – Visão Gráfica.


Observe que o tempo médio de resposta (linha azul) é de 1752 ms e que das 150 requisições, esse tempo se manteve relativamente constante ao longo de todo o teste. Outra observação importante é o “Throughput”, isto é, o número de requisições atendidas em um dado intervalo de tempo. No gráfico acima temos 435,4136 requisições atendidas por minuto (aproximadamente 7,3 requisições por segundo).

A Figura 17 apresenta os mesmos dados no formato de planilha. Observe a Tabela 5, descrevendo o significado de cada coluna (“Análise 2 – Relatório”).


Figura 17. Relatório de Desempenho.


Fazendo uma análise de todos os testes, tem-se na última linha da tabela da Figura 17 (TOTAL) o resultado apresentado na Tabela 5.


Tabela 5. Interpretação dos resultados.

#Samplers


150 execuções.

Averange


Tempo médio de resposta em ms foi 1752.

Median


Mediana foi 1081.

90% line


Indica que 90% das requisições foram atendidas em um tempo menor que o indicado na coluna (5247 para “Alunos por Sexo” e 3595 para “Aluno Data Nasc Nula”).

Min


Menor tempo de resposta em ms foi 90.

Max


Maior tempo de resposta em ms foi 10575.

Error%


Todas as requisições foram atendidas (sem erro de requisição).

Throughput


Número de requisições atendidas por segundo foi 7,3.

KB/sec


Quantidade em KB transferidos por segundo 0,19.

Visualizando a resposta do servidor

Para visualizar a resposta do servidor, o JMeter fornece um relatório denominado “View Results Tree”. Para adicioná-lo, clique com o botão direito do mouse em cima do Thread Group “Teste Postgres” e proceda conforme ilustrado na Figura 18, selecionando a opção do Menu “View Results Tree”.


Figura 18. Seqüência do Menu para adicionar visualização da resposta do servidor.


Observe que mais um componente denominado “View Results Tree” foi adicionado em seu Teste de Capacitação (veja a Figura 19).

Execute novamente o teste conforme mostrado no item “Execução do Teste” e observe o resultado mostrado em “View Results Tree”.

Ainda na Figura 19, veja no formulário as requisições enviadas e as respostas do servidor de banco de dados após a execução do teste. Observe as consultas “Alunos por Sexo” e “Aluno Data Nasc Nula” listadas no quadro central do formulário. É possível clicar sobre cada item do referido quadro e verificar o que foi enviado ao servidor (clicando na aba Request) e o que ele retornou (clicando na aba Response data). Isso é muito útil para constatar se o retorno de cada consulta é realmente o esperado.


Figura 19. Comando enviado (Requisição: Aluno Data Nasc Nula).


A aba “Response data” apresenta o resultado da consulta “Aluno Data Nasc Nula” (ver Figura 20).


Figura 20. Resultado da consulta (Response data: Aluno Data Nasc Nula).

Boas práticas

1.

Analise o plano de execução das consultas utilizadas no plano de teste antes mesmo de executá-lo. Isso poderá ajudar na avaliação do custo de cada consulta utilizada e sugerir ajustes no SGBD ou na própria consulta com o objetivo de reduzir os recursos utilizados.


Exemplo: Para a consulta “Aluno por Sexo”


select sexo, count(*) as "Qtd Aluno"

from educacao.aluno group by sexo;



foi apresentado o seguinte plano de execução:



HashAggregate (cost=536.90..537.40 rows=200 width=7)

-> Seq Scan on aluno (cost=0.00..509.60 rows=5460 width=7)


Já para a consulta “Aluno Data Nasc Nula”


select count(*) as "Qtd Aluno"

from educacao.aluno

where datanasc is null;


tem-se o plano abaixo:


Aggregate (cost=509.67..509.67 rows=1 width=0) (actual time=102.897..102.899 rows=1 loops=1)

-> Seq Scan on aluno (cost=0.00..509.60 rows=28 width=0) (actual time=0.031..94.261 rows=2673 loops=1)

Filter: (datanasc IS NULL)

Total runtime: 103.122 ms


É muito importante para o DBA saber interpretar o plano de execução de uma consulta de banco de dados. Para o PostgreSQL versão 8.1, recomenda-se a leitura do capítulo 13 do manual que vem com a própria distribuição.


2.

Dimensione com cuidado a quantidade de “Threads”. Lembre-se que os recursos do hardware que você está utilizando para teste são limitados. Uma quantidade de Thread acima da capacidade do hardware poderá causar imprecisão nos resultados;
3.

Caso você necessite simular uma carga maior que a capacidade de seu hardware, execute o JMeter remotamente. Isto é, você poderá distribuir a quantidade de “Threads” necessários em vários computadores cliente para “estressar” o Servidor de Banco de Dados. Para fazer isso siga os seguintes passos:
1.

Inicie o JMeter Server (exemplo: c:\jmeter\bin\jmeter-server.bat) em todos os computadores que você deseja utilizar para “estressar” o Servidor de Banco de Dados.
2.

Selecione um computador para centralizar a monitoração do teste e adicione o IP de cada host que executa o jmeter-server no arquivo de propriedades jemeter.propertiers (exemplo: c:\jmeter\bin\jemeter.propertiers). A Listagem 3 apresenta o arquivo jmeter.properties informando o IP de três hosts: o localhost (127.0.0.1), o 192.168.0.101 e 192.168.0.101.


Listagem 3. Adicionando IPs ao arquivo jmeter.properties.

# Remote Hosts - comma delimited

remote_hosts=127.0.0.1, 192.168.0.101, 192.168.0.100

#remote_hosts:localhost:1099,localhost:2010


3.

No computador que irá centralizar a monitoração, inicie o JMeter da mesma forma apresentada no item “Executando o JMeter” no início deste artigo (exemplo: c:\jmeter\bin\jmeter.bat). A Figura 21 apresenta o JMeter configurado para executar o teste em três computadores.


Figura 21. Usando mais de um computador para aplicar um teste de carga.


É possível iniciar o teste de duas formas: A primeira é clicando em “Remote Start” e em seguida em um dos endereços IP apresentados na lista. Esse processo permite a seleção de parte dos computadores para execução do teste. A segunda é clicando em “Remote Start All”. Esse processo irá iniciar automaticamente o teste em todos os computadores registrados.

Todos os dados necessários para execução do teste são enviados e recebidos usando o protocolo RMI (Remote Method Invocation).


Conclusão

Conhecer com antecedência a capacidade de demanda de um sistema de banco de dados é fundamental para a disponibilidade das informações de uma organização. Vimos neste trabalho que o JMeter é uma ferramenta que pode ser usada para simular o uso intenso do servidor de banco de dados e fornecer respostas que podem ajudar na prevenção de perda de performance e até mesmo indisponibilidade dos serviços.

Referências e Links Interessantes

*

Apache Jakarta Project: http://jakarta.apache.org/jmeter/usermanual/index.html.
*

Java Boutique: http://javaboutique.internet.com/tutorials/JMeter/.
*

Apache JMeter WIKI: http://wiki.apache.org/jakarta-jmeter/.
*

IBM:

http://www-128.ibm.com/developerworks/db2/library/techarticle/0303bhogal/0303bhogal.html.

*

SCHÖNIG, Geschwinde, PostgreSQL Developer´s HandBook, SAMS, 2002.

sexta-feira, 22 de fevereiro de 2008

PROGRAMAÇÃO C - APONTADORES E VETORES


Considerações sobre Apontadores e Vetores em C


Talvez a maior dificulade em um curso de linguagem C seja a compreenção quanto ao uso de ponteiros e vetores (arrays). Ao contrário da maioria das linguagens, existe em C, uma forte relação entre apontadores e vetores. Tão forte que apontadores e vetores deveriam ser discutidos juntos. Em C, qualquer operação que possa ser feita com subscritos de um vetor pode ser feita com apontadores. Assim, o objetivo deste texto será mostrar essa relação.

Considere a declaração abaixo:

// Programa 1

void main()

{

int a[10];

}

Essa declaração define um vetor de escopo local de tamanho 10, isto é, um bloco com 10 variáveis do tipo int consecutivos chamados a[0],a[1],...,a[9]. A figura abaixo tenta mostrar, de forma hipotética, como seria a disposição de memória para o vetor <a>. O símbolo ? representa um valor desconhecido, pois, no momento em que uma variável de escopo local ganha vida, ou seja, é instanciada, o seu valor inicial é desconhecido.


?

?

?

?

?

?

?

?

?

?

Conteúdo

0

1

2

3

4

5

6

7

8

9

Subscrito



A notação a[i] refere-se ao elemento da i-ésima posição do vetor.


Considere o programa abaixo. Note que ele declara o vetor <a> e atribui o valor 0 (zero) para cada elemento.


// Programa 2

void main()

{

int a[10];

int I;

for ( I = 0; I <>

a[I] = 0;

}


Observe o que aconteceu com os valores do vetor <a> após a sua execução:


0

0

0

0

0

0

0

0

0

0

Conteúdo

0

1

2

3

4

5

6

7

8

9

Subscrito





Considere agora a possibilidade de fazer a mesma operação utilizando ponteiros.

Para tanto, vamos ver uma breve introdução a ponteiros em C.

Em C, um ponteiro é uma variável que contém um endereço de memória, ou seja, no lugar de armazenar um valor do tipo “Quantidade de Alunos Matriculados”, ou algo que o valha, ele é usado para apontar para um endereço de memória previamente alocado.


Vamos analisar o programa a seguir:


// Programa 3

void main()

{

int *pa;

int b = 10;

pa = &b;

*pa = 6;

printf(“\nO valor de b eh: %d\n”,b);

}


  1. A intenção da declaração int *pa foi de declarar uma variável que irá apontar para uma posição de memória que será representada como inteiro (int), ou seja, <pa> é um ponteiro para um tipo int.

  2. A variável <b>, declarada como inteira, poderá ser a posição de memória desejada para ser manipulada pelo ponteiro <pa>. Note que o valor inicial de b é 10;

  3. Para atribuir o endereço da variável b ao ponteiro pa, usamos o operador &, ou seja, pa = &b. Isso significa: Atribua a <pa> o endereço de memória de <b>.

  4. Posso agora manipular o endereço de memória que <pa> aponta da seguinte forma: *pa = 6. Isso pode ser interpretado como: Coloque o valor 6 na célula de memória apontada por <pa>, ou seja, se <pa> aponta para <b>, logo b será igual a 6 após a execução dessa instrução.


O uso de ponteiros em C deve ser bastante racional. O menor erro na manipulação com ponteiros poderá causar resultados inesperados. Usando o exemplo anterior, se no lugar de *pa = 6 fosse usado pa = 6, o programa não atingiria o seu propósito.


Voltando com a idéia inicial com relação a vetores. Se <pa> for uma apontador para um inteiro, declarado por


int *pa;


E <a> for um um vetor de inteiros, declarado por


int a[10];

então a atribuição

pa = &a[0];

ou

pa = a;

define <pa> de modo que aponte para o elemento zero do vetor <a> ; isto é, <pa> contém o endereço de a[0].


Continuando com o raciocínio e como já foi mostrado no início, a figura abaixo representa a disposição de memória para o vetor <a>. Note a relação entre subscritos do vetor a e o ponteiro <pa>.












Conteúdo

0

1

2

3

4

5

6

7

8

9

Subscrito

pa+0

pa+1

pa+2

pa+3

pa+4

pa+5

Pa+6

pa+7

pa+8

pa+9

Aritimética de Ponteiro



O índice 0 poderá ser representado por (pa+0) ou simplesmente pa;

O índice 1 poderá ser representado por (pa+1);

O elemento I-ésimo do vetor <a> poderpa ser representado por (pa+I).


Considere o trecho de código abaixo:


// Programa 4

void main()

{

int *pa;

int a[10];

int x;


pa = a; // ou pa = &pa[0]


x = *pa;

}

x = *pa

copiará o conteúdo de a[0] em x.




Se <pa> aponta para o primeiro elemento vetor <a>, então podemos concluir que pa + 1 apontará para o próximo elemento do vetor <a>.


Considere o programa a seguir:


// Programa 5

#include

#include


void mostre( int *, int );


void main()

{

int a[10];

int *pa;

int i;


for ( i = 0; i <>

mostre(a,10);


pa = &a[0]; // pa aponta para o primeiro elemento


for ( i = 0; i <>


mostre(a,10);

}


void mostre(int *vetor, int tamanho )

{

int i;

for ( i = 0; i <>

printf("\n%d",vetor[i]);

}

No programa 5, exemplo acima, a primeira estrutura for utiliza a operação de subscrito para fazer atribuição ao vetor <a> (exe.: a[i] = 10 ). Isso, dependendo da capacidade que o compilador tem para otimizar código, poderá resultar em custo adicional de processamento. Para ilustrar, considere que o tamanho do tipo inteiro utilizado seja 4 Bytes, ou seja, o nosso compilador C representa o tipo int com 4 bytes.


4 Bytes

4 Bytes

4 Bytes

4 Bytes

4 Bytes

4 Bytes …


0

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

i=0

i=1

i=2

i=3

i=4

i=5 …

pa + 0

pa + 1

pa + 2

pa + 3

pa + 4

pa + 5 …



Sabendo-se que a unidade de endereçamento da maioria dos computadores modernos é o Byte, para endereçar o quarto valor inteiro, índice 3, do vetor <a> será feita a seguinte aritmética:


(Base do vetor) + (tamanho em bytes do tipo de dados) * (o Índice)

Ou seja,

Para endereçar a[3] temos:

a + 4 * 3.


Para o exemplo acima, isso significa que terei que deslocar 12 Bytes (4 * 3) a partir do endereço base do vetor <a> para chegar ao Índice 3 ( 4° elemento do vetor <a>).

Como você pôde notar no exemplo dado, foi necessária uma operação de multiplicação para endereçar a posição do vetor desejada. Sabendo-se que uma operação de multiplicação custa muito mais caro que uma operação de adição para uma CPU, operações com subscritos poderá resultar em um tempo adicional, considerando um vetor de grande dimensão.


Ao contrário, se no lugar de usar operações com subscrito, for utilizado ponteiros, o resultado final será o mesmo, porém, o tempo poderá ser sensivelmente menor para vetores de grandes dimensões. Isso em função da aritmética utilizada para ponteiros que, no nosso caso é diferente da utilizada pelos subscritos, ou seja, se <pa> for um ponteiro para inteiros de 4 bytes e apontar para a base do vetor <a>, então pa = pa + 1 ou pa++, fará o ponteiro <pa> deslocar 4 Bytes, fazendo assim, <pa> apontar para o próximo elemento do vetor. Dessa forma, para acesso seqüencial a um vetor, poderá ser mais barato utilizar um ponteiro.


Voltando ao programa 5, a segunda estrutura for, faz uso do ponteiro <pa> para manipular o vetor <a>. Nesse caso, o compilador irá gerar um código sem operações de multiplicação, tornando assim, o código final mais eficiente para esse tipo de problema.


Você notará essa forte relação entre apontadores e vetores, quando usar as funções de manipulação de strings (strlen, strcpy, etc). A manipulação de uma string, vetor de char em C, será por meio de ponteiro ou vetor de acordo com a conveniência.


Concluindo, devido a relação muito próxima entre ponteiros e vetores, o programador C deverá ser capaz de discernir quando usar um ou outro. Muitas vezes será mais conveniente utilizar subscritos para operar com vetores, sobretudo quando o tempo de execução e o tamanho do código gerado não são críticos. O uso de ponteiros tende para um código mais otimizado, porém, poderá deixar seu código em C menos legível além de poder comprometer a robustez do seu programa caso o ponteiro seja utilizado de forma errada.


Referências Bibliográficas:


Kernighan Brian W., Dennis Ritchie M, C A Linguagem de Programação Padrão ANSI, Editora CAMPUS

Aaron M., Yedidyah Langsan e Moshe J. Augenstein, Data Structures Using C, Editora Prentice-Hall


domingo, 17 de fevereiro de 2008

Métodos Nativos em Java

UTILIZAÇÃO DE MÉTODOS NATIVOS EM JAVA

Entenda como escrever métodos nativos em C e utiliza-los em Java




Por Ricardo Lima Caratti

Introdução


Durante o desenvolvimento de uma aplicação em Java, é possível que se tenha a necessidade de executar um código escrito em outra linguagem. Isso pode ocorrer por vários motivos, dentre eles destacam-se: aproveitamento de código previamente escrito, utilização de funcionalidades do sistema operacional, acesso ao hardware e até mesmo melhoria de performance. Java possui várias formas de fazer isso. Por exemplo, execução dos métodos exec() da classe Java.lang.Runtime, utilização de CORBA ou RMI. O objetivo deste artigo é mostrar uma outra forma, além das descritas acima, para fazer comunicação de uma aplicação Java com código escrito em linguagem C.


A linguagem Java possui a palavra chave native, que é um modificador utilizado somente para métodos. O modificador native indica que o corpo do método encontra-se fora do Java Virtual Machine (JVM). Um código nativo é escrito, geralmente, em C ou C++ e compilado para um tipo de hardware específico. O exemplo a seguir mostra como utilizar código nativo em Java.


class ChamadaCodigoNativo {

static {

System.loadLibrary("CodigoNativo");

}

public native int Soma(int a, int b);

}

Arquivo 1: ChamadaCodigoNativo.java



class Exemplo1 {

public static void main(String[] args) {

int a, b, c;

ChamadaCodigoNativo nativo =

new ChamadaCodigoNativo();

a = 10;

b = 11;

c = nativo.Soma(a,b);

System.out.println("Resultado: "+c);

}

}

Arquivo 2: Exemplo1.java


Note na implementação da classe ChamadaCodigoNativo, o trecho do código estático. Ele será executado uma única vez no momento em que a classe for instanciada.

A chamada System.loadLibrary(“CodigoNativo”) tenta carregar a biblioteca de ligação dinâmica CodigoNativo.dll no caso do ambiente Windows ou CodigoNativo.so no caso do ambiente UNIX. Para tanto, é claro, ele deve estar implementada.


Note também que a chamada do método Soma não difere de um método normal em Java.



Vantagens e desvantagens da utilização de código nativo.


Vantagens


Aproveitamento de código legado: Talvez seja a principal razão para utilização de código nativo. Em muitas situações é melhor utilizar um código já testado e funcionando que recodifica-lo para Java.


Eficiência: Embora a tecnologia utilizada nos compiladores Java esteja, a cada dia, melhorando em favor da eficiência, códigos nativos, em geral, executam mais rápidos que códigos Java.


Desvantagens:


Programa dependente de plataforma: Essa é a principal desvantagem em usar código nativo, pois, sabe-se que uma das principais características do Java é a portabilidade. Nesse caso, essa é a maior razão para evitar código nativo.


Problema de robustez: O JVM não protege a aplicação de código nativo mal escrito, isto é, uso inadequado de ponteiros, operações de I/O etc.


Coletor de lixo (garbage collection): O uso de alocação dinâmica de memória no método nativo, não trabalha em conjunto com o garbage collection do JVM. Exemplo: utilizar a função malloc() do C no método nativo.

O Java Native Interface (JNI)


Considerando uma real necessidade de utilização de código nativo, o Java disponibiliza o JNI como parte do JDK. O JNI possibilita uma interface segura entre o Java e outra linguagem. A figura 1 mostra como isso é feito.


Figura 1 - Utilização do JNI como interface do Java com código nativo


Por meio do JNI, é possível utilizar métodos nativos para:


*

Ter acesso a variáveis membros de classes em Java;
*

Criar e manipular objetos em Java. Isso inclui arrays e strings;
*

Manipular exceções;
*

Executar cheque de tipo em tempo de execução;
*

Utilizar threads.



Tratamento de tipos de dados primitivos entre Java e métodos nativos


Como foi mostrado na figura 1, o JNI é o elo entre o programa Java e os métodos nativos. Para tanto, ele precisa mapear os tipos de dados Java para os tipos de dados nativos. A tabela 1 mostra o mapeamento dos tipos primitivos e os tipos nativos equivalentes.


Tipo em Java


Tipo em C


Tamanho em bits

Void


void


-

Byte


jbyte


8

Char


jchar


16

short


jshort


16

Int


jint


32

Long


jlong


64

Float


jfloat


32

double


jdouble


64

Tabela 1 – Integração de tipos primitivos Java para tipos nativos equivalentes.


Os tipos: jbyte, jchar, jshort, jint, jlong, jfloat, jdouble e outros, estão definidos em jni.h.


Para manipulação de array o JNI usa jarray para representar uma referência a arrays em Java. Seguindo a mesma idéia, existe o jstring para manipulação de string. A manipulação de array e strings em métodos nativos requer procedimentos extras e não serão tratados aqui. Para mais informações consulte as referências indicadas neste artigo.



Os seis passos necessários


Passo 1: Implementar o código em Java;


Considere as classes ChamadaCodigoNativo.java e Exemplo1.java implementadas no início deste artigo.



Passo 2: Compilar código Java


Compilado as classes


javac ChamadaCodigoNativo.java

javac Exemplo1.java


Note que até o momento não foi feito nada além do de costume.


Passo 3: Utilizar o utilitário javah


javah –jni ChamadaCodigoNativo


O comando acima criará um arquivo chamado ChamadaCodigoNativo.h. Esse arquivo será utilizado mais adiante na implementação do código em C. A listagem a seguir mostra o conteúdo desse arquivo.


/* DO NOT EDIT THIS FILE - it is machine generated */

#include

/* Header for class ChamadaCodigoNativo */


#ifndef _Included_ChamadaCodigoNativo

#define _Included_ChamadaCodigoNativo

#ifdef __cplusplus

extern "C" {

#endif

/*

* Class: ChamadaCodigoNativo

* Method: Soma

* Signature: (II)I

*/

JNIEXPORT jint JNICALL Java_ChamadaCodigoNativo_Soma

(JNIEnv *, jobject, jint, jint);


#ifdef __cplusplus

}

#endif

#endif


Arquivo 3: ChamadaCodigoNativo.h



Note o cometário da primeira linha indicado que o arquivo ChamadaCodigoNativo.h não deve ser alterado.


Note também a denominação data para o método Soma no código nativo. Isto é, a composição do nome da função é dada pelo literal Java, o nome do pacote (se for o caso), o nome da classe e o nome do método. Portanto, a função Soma em C será implementada com o nome Java_ChamadaCodigoNativo_Soma. Os dois primeiros argumento, JNIEnv * e jobject, devem sempre ser declarados, mesmo que não sejam utilizados. Veja o passo a seguir para ver como isso funciona.



Passo 4: Implementar o método nativo


Para implementar o método Soma em C, deve-se usar a mesma nomenclatura prototipada no arquivo ChamadaCodigoNativo.h. A listagem a seguir mostra o código em C (arquivo CodigoNativo.c).


#include

#include "ChamadaCodigoNativo.h"


JNIEXPORT jint JNICALL Java_ChamadaCodigoNativo_Soma(JNIEnv *env, jobject this, jint a, jint b)

{

return a + b;

}

Arquivo 4: CodigoNativo.c


Passo 5: Criando uma biblioteca de ligação dinâmica


Na codificação da classe ChamaCodigoNativo, foi utilizado bloco estático como mostrado abaixo.


static {

System.loadLibrary("CodigoNativo");

}


Como dito anteriormente, este código será executado no momento em que a classe for instanciada. A chamada a System.loadLibrary() é responsável pela carga da biblioteca de ligação dinâmica ou biblioteca compartilhada (shared library).


Para construir uma biblioteca de ligação dinâmica você deverá instruir o compilador C.


O exemplo a seguir mostra a criação de uma biblioteca de ligação dinâmica (DLL) no ambiente Microsoft Windows utilizando o código em C implementado no Passo 4. O compilador utilizado é Microsoft Visual C++ 6.0.


cl -Ic:\java\include -Ic:\java\include\win32

-LD CodigoNativo.c -FeCodigoNativo.dll


Observações sobre as chaves de compilação:


-Ic indica o caminho onde serão encontrados os arquivos de cabeçalho do JNI;

-LD indica que o código gerado será uma (DLL);

-Fe indica o nome do arquivo a ser gerado.


Exemplo utilizando o Sistema Operacional UNIX Solaris.


cc -G -I/usr/local/java/include -I/usr/local/java/include/solaris \

CodigoNativo.c -o CodigoNativo.so


Exemplo utilizando o compilador C da GNU no ambiente Linux.


gcc –fPIC –c CodigoNativo.c -I/usr/local/java/include -I/usr/local/java/include/solaris

gcc –shared –Wl, -soname, libCodigoNativo.so.1 –o libCodigoNativo.so.1.0 CodigoNativo.o


Para os dois últimos exemplos, é assumido que o java está instalado no caminho /usr/local/java.


Para mais detalhes sobre a forma de compilar e criar bibliotecas compartilhadas consulte o manual do compilador disponível em cada plataforma.


Passo 6: Execução do Programa


A execução do programa é feita normalmente, ou seja, como um programa Java comum.


java Exemplo1


A saída do programa será:


Resultado: 21



Resumo


É possível fazer aplicações Java se comunicar com código escrito em outra linguagem. As principais técnicas são:


*

Interprocess communication (IPC);
*

Objetos distribuídos (CORBA);
*

Java Native Interface (JNI).



Você deve usar métodos nativos em Java quando:


*

Você já possui uma biblioteca escrita em uma outra linguagem (código legado) e deseja utiliza-la com suas aplicações em Java;
*

Você deseja utilizar uma funcionalidade dependente de plataforma que a biblioteca padrão do Java não possui;
*

Você precisa implementar uma funcionalidade onde o tempo é um fator crítico.


As principais implicações do uso de métodos nativos são:


*

Problemas quanto a portabilidade;
*

Problema em toda aplicação no caso de código nativo mal escrito.



Referências


Wall, Watson, and Whitis. Linux Programminig, Sams, 1999.


Avenue, Garcia, Java Native Interface Specification Release 1.1, Java Software, MountainView, CA, USA, 1997

Caratti, Ricardo Lima, Revista do Linux, Ano I, Nº 5, Maio 2000, Artigo: Bibliotecas Compartilhadas.


Darwin, Ian, Java Cookbook, O’Reilly, 2001


http://java.sun.com/docs/books/tutorial/native1.1/

http://java.sun.com/docs/books/jni/html/intro.html#1811


MSDN Library Visual Studio6.0, Compiler Reference.