Seguem algumas dicas para efetuar um tuning de performance da versão JBoss-as-7.1.1.Final Community em um Linux Debian 7.6.
Dentro do arquivo $JBOSS_home/bin/standalone.conf, você pode efetuar as seguintes configurações:
- Memória: uma configuração para um servidor de pequeno porte para uma máquina com memória física de 4Gb seria:
JAVA_OPTS="-Xms2g -Xmx2g -XX:PermSize=256m -XX:MaxPermSize=512m"
- GC Paralelo: caso a máquina tenha vários processadores é interessantes habilitar o GC paralelo e dividir as regiões de memória.
# Paralell GC
JAVA_OPTS="$JAVA_OPTS -server -XX:+UseParallelGC -XX:+UseParallelOldGC -
XX:SurvivorRatio=2 -XX:NewRatio=2"
- Linux HugePages: Você pode também habilitar o HugePages no sistema operacional e habilitar o uso de large pages na JVM:
Veja aqui como habilitar o hugepages para o debian: https://wiki.debian.org/Hugepages. Basicamente é criar um grupo no linux, criar o ponto de montagem, configurar os parametros do kernel e adicionar um usuário ao grupo criado, ou seja, o usuário que irá subir o jboss.
Para uma máquina de 4Gb de memória física, temos os seguintes valores para o Hugepages:
Arquivo /etc/sysctl.conf:
# Total memory
kernel.shmmax = 4294967295
# Ceil(shmmax/PAGE_SIZE)
kernel.shmall = 1048576
# Allocate n * 2048kb (Hugepagesize) for HugePageTables
vm.nr_hugepages = 1536
Ainda no standalone.conf, para habilitar o uso do Hugepages na JVM, você precisa usar o parâmetro abaixo:
# Large Pages
JAVA_OPTS="$JAVA_OPTS -XX:+UseLargePages"
Dentro do standalone.xml, temos:
- Compressão do tráfego: o mais recomendável é que você utilize um servidor web como Apache Web Server para receber as requisições e direcione para o jboss, mas mesmo assim é interessante verificar como compactar alguns tipos de arquivos:
<system-properties>
<property name="org.apache.coyote.http11.Http11Protocol.COMPRESSION" value="on"/>
<property name="org.apache.coyote.http11.Http11Protocol.COMPRESSION_MIME_TYPES" value="text/html,text/xml,text/plain,text/css,text/javascript,text/json,application/x-javascript,application/javascript,application/json"/>
<property name="org.apache.coyote.http11.Http11Protocol.COMPRESSION_MIN_SIZE" value="1024"/>
</system-properties>
- Logger: diminua os níveis de logger do jboss, e se for o caso, remova o CONSOLE handler dele, para a maioria dos casos, você pode só diminuir o nível para WARN e deixar INFO para as mensagens de inicialização do JBoss:
<logger category="org.jboss.as">
<level name="INFO"/>
</logger>
...
<root-logger>
<level name="WARN"/>
</root-logger>
- Thread Pool: configure um thread pool para receber as requisições web, estimando a quantidade de usuários concorrentes que você espera que aquele servidor suporte. Abaixo segue uma configuração para suportar em torno de 200 usuários concorrentes.
<subsystem xmlns="urn:jboss:domain:threads:1.1">
<bounded-queue-thread-pool name="http-executor" blocking="true">
<core-threads count="200" />
<queue-length count="1000" />
<max-threads count="300" />
<keepalive-time time="5" unit="seconds"/>
</bounded-queue-thread-pool>
<subsystem xmlns="urn:jboss:domain:web:1.1" default-virtual-server="default-host" native="true">
<connector name="http" protocol="HTTP/1.1" scheme="http" socket-binding="http"
executor="http-executor" enable-lookups="false"
max-connections="300" max-post-size="2048" max-save-post-size="4096"
/>
- Mail/Default: caso seu servidor não utilize o mail-session do JBoss, você pode remover a extension, o subsystem e o socket-binding conforme abaixo. Isso irá deixar a inicialização do servidor mais rápida.
<extension module="org.jboss.as.mail"/>
...
<subsystem xmlns="urn:jboss:domain:mail:1.0">
<mail-session jndi-name="java:jboss/mail/Default">
<smtp-server outbound-socket-binding-ref="mail-smtp"/>
</mail-session>
</subsystem>
...
<outbound-socket-binding name="mail-smtp">
<remote-destination host="localhost" port="25"/>
</outbound-socket-binding>
- JBoss Web Native: instale os connectors nativos para uma melhor performance das conexões HTTP.
Abaixo segue o link para fazer download dos binários já compilados para cada plataforma:
http://jbossweb.jboss.org/downloads/jboss-native-2-0-10.html
Descompacte dentro da $JBOSS_HOME/bin/native:
ls -l $JBOSS_HOME/bin/native/lib*-1.so
Habilite dentro do arquivo standalone.xml:
<subsystem xmlns="urn:jboss:domain:web:1.1" default-virtual-server="default-host" native="true">
- Deployment Scanner: desabilite a checagem do scanner, assim ele não irá fazer hot-deploy de arquivos. Isso pode ser um falha de segurança e diminui o overhead no disco. Com a configuração abaixo, ele só irá fazer o deploy durante a inicialização do JBoss:
<subsystem xmlns="urn:jboss:domain:deployment-scanner:1.1">
<deployment-scanner path="deployments" relative-to="jboss.server.base.dir" scan-interval="0"/>
</subsystem>
- Compilation JSP e arquivos estátivos: desabilite a checagem para compilação das JSPs e desabilite a listagem de recursos estáticos:
<subsystem xmlns="urn:jboss:domain:web:1.1" default-virtual-server="default-host" native="false">
<configuration>
<static-resources listings="false" sendfile="49152"
file-encoding="utf-8" read-only="true" webdav="false" max-depth="3" disabled="false" />
<jsp-configuration
development="false"
check-interval="1"
disabled="false"
display-source-fragment="true"
dump-smap="false"
error-on-use-bean-invalid-class-attribute="false"
generate-strings-as-char-arrays="false"
java-encoding="UTF8"
keep-generated="true"
mapped-file="true"
modification-test-interval="4"
recompile-on-fail="false"
scratch-dir=""
smap="true"
source-vm="1.6"
tag-pooling="true"
target-vm="1.6"
trim-spaces="false"
x-powered-by="true"/>
</configuration>
- Data Source: estime e configure a quantidade de conexões que a aplicação terá. A maioria das aplicações usam OpenSessionInView, ou seja, cada request utiliza somente uma conexão de banco, pensando assim, podemos estimar o tamanho do pool, com o mesmo tamanho do thread pool, assim para 200 usuários concorrentes, teríamos:
<datasources>
<datasource jndi-name="java:jboss/datasources/ExemploDS" pool-name="ExemploDS" enabled="true" use-java-context="true" use-ccm="false">
<connection-url>....</connection-url>
<driver>....</driver>
<pool> <min-pool-size>100</min-pool-size>
<max-pool-size>200</max-pool-size>
<prefill>true</prefill>
<use-strict-min>false</use-strict-min>
</pool>
<statement>
<track-statements>true</track-statements>
<prepared-statement-cache-size>50</prepared-statement-cache-size>
<share-prepared-statements />
</statement>
<timeout>
<blocking-timeout-millis>10000</blocking-timeout-millis>
<idle-timeout-minutes>2</idle-timeout-minutes>
<set-tx-query-timeout />
<query-timeout>30</query-timeout>
<use-try-lock>30</use-try-lock>
</timeout>
Lembre-se de verificar se o banco suporta essa quantidade de conexões. Para Oracle, você pode utilizar o select abaixo:
select name, value from v$parameter
where name in ('processes', 'sessions', 'transactions');
- Validação da conexão: habilite também as validações de conexão. Isso evita problemas na aplicação devido oscilações na rede. Abaixo seguem alguns exemplos:
Oracle
<validation>
<valid-connection-checker class-name="org.jboss.jca.adapters.jdbc.extensions.oracle.OracleValidConnectionChecker"/>
<stale-connection-checker class-name="org.jboss.jca.adapters.jdbc.extensions.oracle.OracleStaleConnectionChecker"/>
<exception-sorter class-name="org.jboss.jca.adapters.jdbc.extensions.oracle.OracleExceptionSorter"/>
</validation>
Postgresql:
<validation>
<valid-connection-checker class-name="org.jboss.jca.adapters.jdbc.extensions.postgres.PostgreSQLValidConnectionChecker" />
<exception-sorter class-name="org.jboss.jca.adapters.jdbc.extensions.postgres.PostgreSQLExceptionSorter" />
</validation>
Aqui temos a listagem completa para cada banco de dados:
http://www.ironjacamar.org/doc/userguide/1.0/en-US/html/ex_datasources.html
- Usuário jboss-as: Crie um usuário específico para o efetuar o start e shutdown do JBoss. Você pode utilizar os arquivos que vem dentro do jboss para configurar isso no linux:
$JBOSS_HOME/bin/init.d/
- jboss-as.conf
- jboss-as-standalone.sh
Copie o jboss-as.conf para /etc/jboss-as/
cp $JBOSS_HOME/bin/init.d/jboss-as.conf /etc/jboss-as/.
Copie o jboss-as-standalone.sh para dentro de /etc/init.d/
cp $JBOSS_HOME/bin/init.d/jboss-as-standalone.sh /etc/init.d/jboss-as
Execute o chkconfig para adicioná-lo a configuração:
chkconfig --add jboss-as
- DataSource: usuário e senha: não deixe exposto o usuário e senha da conexão de banco no standalone.xml. Você pode utilizar um security-domain com o SecureIdentity, que é um login-module que vem dentro do picketbox, que é a biblioteca de segurança do JBoss, e gerar uma senha criptografada.
Aqui você tem mais informações sobre o picketbox: http://picketbox.jboss.org/
Crie o security-domain:
<subsystem xmlns="urn:jboss:domain:security:1.1">
<security-domains>
<security-domain name="ExemploDSSecurity" cache-type="default">
<authentication>
<login-module code="SecureIdentity" flag="required">
<module-option name="username" value="dbuser"/>
<module-option name="password" value="2afa5c8be6f4b2ec"/>
</login-module>
</authentication>
</security-domain>
Altere o pool para usar o security-domain criado:
<subsystem xmlns="urn:jboss:domain:datasources:1.1">
<datasources>
<datasource jndi-name="java:jboss/datasources/ExemploDS" pool-name="ExemploDS" enabled="true" use-java-context="true" use-ccm="false">
<connection-url>.....</connection-url>
<driver>...</driver>
<security>
<security-domain>ExemploDSSecurity</security-domain>
</security>
Para gerar a senha, crie o seguinte script:
$JBOSS_HOME/bin/makepasswd.sh
----------
#!/bin/bash
read -s -p "password: " PASS
## Build ClassPath for command
CP=
CP=${CP}:$JBOSS_HOME/modules/org/picketbox/main/picketbox-4.0.7.Final.jar
CP=${CP}:$JBOSS_HOME/modules/org/jboss/logging/main/jboss-logging-3.1.0.GA.jar
echo ""
java -classpath ${CP} org.picketbox.datasource.security.SecureIdentityLoginModule ${PASS}
------------
Dê a permissão necessária:
chmod +x makepasswd.sh
Execute e ele irá gerar uma senha que você irá substituir no parâmetro do módulo:
<module-option name="password" value="2afa5c8be6f4b2ec"/>
- JConsole: o JBoss tem um protocolo específico para conexão via JMX, então para conectar via jconsole, você precisa utilizar os pacotes que vem dentro do JBoss.
Em outra máquina com o JBoss, utilize o script abaixo para inicializar o jconsole:
$JBOSS_HOME/bin/jconsole.sh
Utilize a seguinte URL:
service:jmx:remoting-jmx://{host_name}:{port}
A porta padrão para do JBoss é a 9999.
Em outro momento, iremos explicar como utilizar ferramentas específicas como JProfiler, Zorka e Zabbix, AppDynamics para efetuar um monitoramento mais efetivo.
Aqui tem mais informações de como utilizar o jconsole:
https://developer.jboss.org/wiki/UsingJconsoleToConnectToJMXOnAS7
- JBoss Management: para habilitar o bind das interfaces de rede da aplicação de gerenciamento do JBoss para todos IPs disponíveis na máquina, você pode alterar a seguinte configuração:
<interface name="management">
<any-address/>
</interface>
<interface name="public">
<any-address />
</interface>
Lembre-se de configurar seu firewall para não permitir conexões na porta 9999 fora da sua rede, ela é a porta da interface de management da configuração acima.
Configurações da JVM
Dentro do arquivo $JBOSS_home/bin/standalone.conf, você pode efetuar as seguintes configurações:
- Memória: uma configuração para um servidor de pequeno porte para uma máquina com memória física de 4Gb seria:
JAVA_OPTS="-Xms2g -Xmx2g -XX:PermSize=256m -XX:MaxPermSize=512m"
- GC Paralelo: caso a máquina tenha vários processadores é interessantes habilitar o GC paralelo e dividir as regiões de memória.
# Paralell GC
JAVA_OPTS="$JAVA_OPTS -server -XX:+UseParallelGC -XX:+UseParallelOldGC -
XX:SurvivorRatio=2 -XX:NewRatio=2"
Configurações do Linux
- Linux HugePages: Você pode também habilitar o HugePages no sistema operacional e habilitar o uso de large pages na JVM:
Veja aqui como habilitar o hugepages para o debian: https://wiki.debian.org/Hugepages. Basicamente é criar um grupo no linux, criar o ponto de montagem, configurar os parametros do kernel e adicionar um usuário ao grupo criado, ou seja, o usuário que irá subir o jboss.
Para uma máquina de 4Gb de memória física, temos os seguintes valores para o Hugepages:
Arquivo /etc/sysctl.conf:
# Total memory
kernel.shmmax = 4294967295
# Ceil(shmmax/PAGE_SIZE)
kernel.shmall = 1048576
# Allocate n * 2048kb (Hugepagesize) for HugePageTables
vm.nr_hugepages = 1536
Ainda no standalone.conf, para habilitar o uso do Hugepages na JVM, você precisa usar o parâmetro abaixo:
# Large Pages
JAVA_OPTS="$JAVA_OPTS -XX:+UseLargePages"
Configurações do JBoss
Dentro do standalone.xml, temos:
- Compressão do tráfego: o mais recomendável é que você utilize um servidor web como Apache Web Server para receber as requisições e direcione para o jboss, mas mesmo assim é interessante verificar como compactar alguns tipos de arquivos:
<system-properties>
<property name="org.apache.coyote.http11.Http11Protocol.COMPRESSION" value="on"/>
<property name="org.apache.coyote.http11.Http11Protocol.COMPRESSION_MIME_TYPES" value="text/html,text/xml,text/plain,text/css,text/javascript,text/json,application/x-javascript,application/javascript,application/json"/>
<property name="org.apache.coyote.http11.Http11Protocol.COMPRESSION_MIN_SIZE" value="1024"/>
</system-properties>
- Logger: diminua os níveis de logger do jboss, e se for o caso, remova o CONSOLE handler dele, para a maioria dos casos, você pode só diminuir o nível para WARN e deixar INFO para as mensagens de inicialização do JBoss:
<logger category="org.jboss.as">
<level name="INFO"/>
</logger>
...
<root-logger>
<level name="WARN"/>
</root-logger>
- Thread Pool: configure um thread pool para receber as requisições web, estimando a quantidade de usuários concorrentes que você espera que aquele servidor suporte. Abaixo segue uma configuração para suportar em torno de 200 usuários concorrentes.
<subsystem xmlns="urn:jboss:domain:threads:1.1">
<bounded-queue-thread-pool name="http-executor" blocking="true">
<core-threads count="200" />
<queue-length count="1000" />
<max-threads count="300" />
<keepalive-time time="5" unit="seconds"/>
</bounded-queue-thread-pool>
<subsystem xmlns="urn:jboss:domain:web:1.1" default-virtual-server="default-host" native="true">
<connector name="http" protocol="HTTP/1.1" scheme="http" socket-binding="http"
executor="http-executor" enable-lookups="false"
max-connections="300" max-post-size="2048" max-save-post-size="4096"
/>
- Mail/Default: caso seu servidor não utilize o mail-session do JBoss, você pode remover a extension, o subsystem e o socket-binding conforme abaixo. Isso irá deixar a inicialização do servidor mais rápida.
<extension module="org.jboss.as.mail"/>
...
<subsystem xmlns="urn:jboss:domain:mail:1.0">
<mail-session jndi-name="java:jboss/mail/Default">
<smtp-server outbound-socket-binding-ref="mail-smtp"/>
</mail-session>
</subsystem>
...
<outbound-socket-binding name="mail-smtp">
<remote-destination host="localhost" port="25"/>
</outbound-socket-binding>
- JBoss Web Native: instale os connectors nativos para uma melhor performance das conexões HTTP.
Abaixo segue o link para fazer download dos binários já compilados para cada plataforma:
http://jbossweb.jboss.org/downloads/jboss-native-2-0-10.html
Descompacte dentro da $JBOSS_HOME/bin/native:
ls -l $JBOSS_HOME/bin/native/lib*-1.so
Habilite dentro do arquivo standalone.xml:
<subsystem xmlns="urn:jboss:domain:web:1.1" default-virtual-server="default-host" native="true">
- Deployment Scanner: desabilite a checagem do scanner, assim ele não irá fazer hot-deploy de arquivos. Isso pode ser um falha de segurança e diminui o overhead no disco. Com a configuração abaixo, ele só irá fazer o deploy durante a inicialização do JBoss:
<subsystem xmlns="urn:jboss:domain:deployment-scanner:1.1">
<deployment-scanner path="deployments" relative-to="jboss.server.base.dir" scan-interval="0"/>
</subsystem>
- Compilation JSP e arquivos estátivos: desabilite a checagem para compilação das JSPs e desabilite a listagem de recursos estáticos:
<subsystem xmlns="urn:jboss:domain:web:1.1" default-virtual-server="default-host" native="false">
<configuration>
<static-resources listings="false" sendfile="49152"
file-encoding="utf-8" read-only="true" webdav="false" max-depth="3" disabled="false" />
<jsp-configuration
development="false"
check-interval="1"
disabled="false"
display-source-fragment="true"
dump-smap="false"
error-on-use-bean-invalid-class-attribute="false"
generate-strings-as-char-arrays="false"
java-encoding="UTF8"
keep-generated="true"
mapped-file="true"
modification-test-interval="4"
recompile-on-fail="false"
scratch-dir=""
smap="true"
source-vm="1.6"
tag-pooling="true"
target-vm="1.6"
trim-spaces="false"
x-powered-by="true"/>
</configuration>
- Data Source: estime e configure a quantidade de conexões que a aplicação terá. A maioria das aplicações usam OpenSessionInView, ou seja, cada request utiliza somente uma conexão de banco, pensando assim, podemos estimar o tamanho do pool, com o mesmo tamanho do thread pool, assim para 200 usuários concorrentes, teríamos:
<datasources>
<datasource jndi-name="java:jboss/datasources/ExemploDS" pool-name="ExemploDS" enabled="true" use-java-context="true" use-ccm="false">
<connection-url>....</connection-url>
<driver>....</driver>
<pool> <min-pool-size>100</min-pool-size>
<max-pool-size>200</max-pool-size>
<prefill>true</prefill>
<use-strict-min>false</use-strict-min>
</pool>
<statement>
<track-statements>true</track-statements>
<prepared-statement-cache-size>50</prepared-statement-cache-size>
<share-prepared-statements />
</statement>
<timeout>
<blocking-timeout-millis>10000</blocking-timeout-millis>
<idle-timeout-minutes>2</idle-timeout-minutes>
<set-tx-query-timeout />
<query-timeout>30</query-timeout>
<use-try-lock>30</use-try-lock>
</timeout>
Lembre-se de verificar se o banco suporta essa quantidade de conexões. Para Oracle, você pode utilizar o select abaixo:
select name, value from v$parameter
where name in ('processes', 'sessions', 'transactions');
- Validação da conexão: habilite também as validações de conexão. Isso evita problemas na aplicação devido oscilações na rede. Abaixo seguem alguns exemplos:
Oracle
<validation>
<valid-connection-checker class-name="org.jboss.jca.adapters.jdbc.extensions.oracle.OracleValidConnectionChecker"/>
<stale-connection-checker class-name="org.jboss.jca.adapters.jdbc.extensions.oracle.OracleStaleConnectionChecker"/>
<exception-sorter class-name="org.jboss.jca.adapters.jdbc.extensions.oracle.OracleExceptionSorter"/>
</validation>
Postgresql:
<validation>
<valid-connection-checker class-name="org.jboss.jca.adapters.jdbc.extensions.postgres.PostgreSQLValidConnectionChecker" />
<exception-sorter class-name="org.jboss.jca.adapters.jdbc.extensions.postgres.PostgreSQLExceptionSorter" />
</validation>
Aqui temos a listagem completa para cada banco de dados:
http://www.ironjacamar.org/doc/userguide/1.0/en-US/html/ex_datasources.html
Configurações de Segurança
- Usuário jboss-as: Crie um usuário específico para o efetuar o start e shutdown do JBoss. Você pode utilizar os arquivos que vem dentro do jboss para configurar isso no linux:
$JBOSS_HOME/bin/init.d/
- jboss-as.conf
- jboss-as-standalone.sh
Copie o jboss-as.conf para /etc/jboss-as/
cp $JBOSS_HOME/bin/init.d/jboss-as.conf /etc/jboss-as/.
Copie o jboss-as-standalone.sh para dentro de /etc/init.d/
cp $JBOSS_HOME/bin/init.d/jboss-as-standalone.sh /etc/init.d/jboss-as
Execute o chkconfig para adicioná-lo a configuração:
chkconfig --add jboss-as
- DataSource: usuário e senha: não deixe exposto o usuário e senha da conexão de banco no standalone.xml. Você pode utilizar um security-domain com o SecureIdentity, que é um login-module que vem dentro do picketbox, que é a biblioteca de segurança do JBoss, e gerar uma senha criptografada.
Aqui você tem mais informações sobre o picketbox: http://picketbox.jboss.org/
Crie o security-domain:
<subsystem xmlns="urn:jboss:domain:security:1.1">
<security-domains>
<security-domain name="ExemploDSSecurity" cache-type="default">
<authentication>
<login-module code="SecureIdentity" flag="required">
<module-option name="username" value="dbuser"/>
<module-option name="password" value="2afa5c8be6f4b2ec"/>
</login-module>
</authentication>
</security-domain>
Altere o pool para usar o security-domain criado:
<subsystem xmlns="urn:jboss:domain:datasources:1.1">
<datasources>
<datasource jndi-name="java:jboss/datasources/ExemploDS" pool-name="ExemploDS" enabled="true" use-java-context="true" use-ccm="false">
<connection-url>.....</connection-url>
<driver>...</driver>
<security>
<security-domain>ExemploDSSecurity</security-domain>
</security>
Para gerar a senha, crie o seguinte script:
$JBOSS_HOME/bin/makepasswd.sh
----------
#!/bin/bash
read -s -p "password: " PASS
## Build ClassPath for command
CP=
CP=${CP}:$JBOSS_HOME/modules/org/picketbox/main/picketbox-4.0.7.Final.jar
CP=${CP}:$JBOSS_HOME/modules/org/jboss/logging/main/jboss-logging-3.1.0.GA.jar
echo ""
java -classpath ${CP} org.picketbox.datasource.security.SecureIdentityLoginModule ${PASS}
------------
Dê a permissão necessária:
chmod +x makepasswd.sh
Execute e ele irá gerar uma senha que você irá substituir no parâmetro do módulo:
<module-option name="password" value="2afa5c8be6f4b2ec"/>
Configurações de Monitoramento
- JConsole: o JBoss tem um protocolo específico para conexão via JMX, então para conectar via jconsole, você precisa utilizar os pacotes que vem dentro do JBoss.
Em outra máquina com o JBoss, utilize o script abaixo para inicializar o jconsole:
$JBOSS_HOME/bin/jconsole.sh
Utilize a seguinte URL:
service:jmx:remoting-jmx://{host_name}:{port}
A porta padrão para do JBoss é a 9999.
Em outro momento, iremos explicar como utilizar ferramentas específicas como JProfiler, Zorka e Zabbix, AppDynamics para efetuar um monitoramento mais efetivo.
Aqui tem mais informações de como utilizar o jconsole:
https://developer.jboss.org/wiki/UsingJconsoleToConnectToJMXOnAS7
- JBoss Management: para habilitar o bind das interfaces de rede da aplicação de gerenciamento do JBoss para todos IPs disponíveis na máquina, você pode alterar a seguinte configuração:
<interface name="management">
<any-address/>
</interface>
<interface name="public">
<any-address />
</interface>
Lembre-se de configurar seu firewall para não permitir conexões na porta 9999 fora da sua rede, ela é a porta da interface de management da configuração acima.