INTRODUÇÃO
Loadbalance
é uma aplicação que recebe uma requisição HTTP, e decide qual
instância (servidor) chamar de um cluster de servidores. Nesse caso,
estamos lidando com cluster de servidores JBoss AS 7, que pode
possuir N servidores, que também são chamados de instâncias do
cluster. O loadbalance, através de critérios configurados, toma a
decisão de chamar uma determinada instância do cluster para
ATENDER a requisição HTTP. Um dos critérios usados, é verificar
qual instância do cluster de JBoss AS 7 está ativa no momento e
com menos carga. Localizado a instância, a requisição é
direcionada para ela. E também, se algumas instâncias deixaram de
funcionar, o loadbalance direciona as requisições para as
instâncias ativas. Para isso, o loadbalance também conta com
mecanismo para verificar se as instâncias estão vivas no momento,
pois não faz sentido redirecionar requisições para instâncias
mortas.
Veja no
desenho abaixo como que funciona um cluster de instâncias do JBoss
AS com o loadbalance (Apache WebService + mod_cluster).
CONFIGURAÇÃO DE
CADA NODO DO JBOSS AS 7.1.3
1 – Copie as pastas
do servidor JBOSS AS 7.1.3 e renomeie cada cópia para:
jboss-as-7.1.3.Final-node1
jboss-as-7.1.3.Final-node2
jboss-as-7.1.3.Final-node3
Cada pasta será uma
instância do servidor JBoss AS 7 e funcionará no modo STANDALONE.
Para cada cópia,
alterar o arquivo standalone.conf que está na pasta “bin”
dentro de cada instância colocando os seguintes parâmetros:
JAVA_OPTS="$JAVA_OPTS
-Djboss.server.default.config=standalone-full-ha.xml"
JAVA_OPTS="$JAVA_OPTS
-Dorg.jboss.as.logging.per-deployment=false"
# Esse offset deve ser
diferente para cada nodo.
JAVA_OPTS="$JAVA_OPTS
-Djboss.socket.binding.port-offset=1000"
# O nome do nodo deve
ser diferente para cada nodo.
JAVA_OPTS="$JAVA_OPTS
-Djboss.node.name=nodo1"
Lembrando que para cada
instância, teremos o parâmetros “-Djboss.node.name” e
“-Djboss.socket.binding.port-offset” diferentes.
Podemos usar os
seguintes valores para cada instância:
Instância
|
-Djboss.socket.binding.port-offset
|
-Djboss.node.name
|
jboss-as-7.1.3.Final-node1
|
1000
|
nodo1
|
jboss-as-7.1.3.Final-node2
|
2000
|
nodo2
|
jboss-as-7.1.3.Final-node3
|
3000
|
nodo3
|
Arquivo
standalone-full-ha.xml
Também, precisamos
alterar o arquivo “standalone-full-ha.xml” para cada
instância do cluster de acordo com as configurações a seguir:
Na tag “server”,
colocar o atributo name com o nome da instância. Lembrando que esse
nome deve ser único entre as instâncias do cluster.
Instância
|
Tag “server”, atributo “name”
|
jboss-as-7.1.3.Final-node1
|
<server name="standalone-nodo-1"
xmlns="urn:jboss:domain:1.3">
|
jboss-as-7.1.3.Final-node2
|
<server name="standalone-nodo-2"
xmlns="urn:jboss:domain:1.3">
|
jboss-as-7.1.3.Final-node3
|
<server name="standalone-nodo-3"
xmlns="urn:jboss:domain:1.3">
|
Dentro da tag “server”
colocar a seguinte tag para todas as instâncias do cluster.
<system-properties>
<property
name="org.apache.tomcat.util.http.Parameters.MAX_COUNT"
value="5000"/>
</system-properties>
Essa propriedade define
o tamanho máximo de uma requisição HTTP. Se não definir esse
tamanho, ao fazer uma requisição HTTP com muitos parâmetros na
url, a instância que tratará a requisição, poderá lançar
exceção com erro 500.
Dentro da tag
“cache-container” colocar o seguinte atributo para iniciar
o cache no inicio do servidor.
start="EAGER"
Existe um bug no JBoss
AS 7.1.x que não inicia o cluster corretamente no infinispan
(gerenciador de cache distribuído do JBoss AS 7.x). Para contornar
esse problema, temos que iniciar todos os caches de forma “EAGER”
(carrega tudo no inicio) e não “LAZY” (padrão, carrega só
quando o cache for de fato chamado por alguma aplicação).
No subsistema web,
mudar a tag para:
<subsystem
xmlns="urn:jboss:domain:web:1.2"
default-virtual-server="default-host"
instance-id="${jboss.node.name}" native="false">
Note que essa tag vai
informar o nome do servidor configurado acima.
Configurar as
interfaces com o código abaixo:
<interfaces>
<interface
name="management">
<inet-address value="${jboss.bind.address.management:0.0.0.0}"/>
</interface>
<interface
name="public">
<inet-address value="${jboss.bind.address:0.0.0.0}"/>
</interface>
<!-- TODO -
only show this if the jacorb subsystem is added -->
<interface
name="unsecure">
<inet-address value="${jboss.bind.address.unsecure:0.0.0.0}"/>
</interface>
</interfaces>
Na tag
“socket-binding-group”, alterar para as seguintes configurações
de acordo com a instância usada.
Instância
|
Tag “socket-binding-group”
|
jboss-as-7.1.3.Final-node1
|
<socket-binding-group
name="standard-sockets" default-interface="public"
port-offset="${jboss.socket.binding.port-offset:1000}">
|
jboss-as-7.1.3.Final-node2
|
<socket-binding-group
name="standard-sockets" default-interface="public"
port-offset="${jboss.socket.binding.port-offset:2000}">
|
jboss-as-7.1.3.Final-node3
|
<socket-binding-group
name="standard-sockets" default-interface="public"
port-offset="${jboss.socket.binding.port-offset:3000}">
|
No
subsistema modcluster, mudar a tag para:
<subsystem
xmlns="urn:jboss:domain:modcluster:1.1">
<mod-cluster-config advertise-socket="modcluster"
connector="ajp"
proxy-list="127.0.0.1:6666">
<dynamic-load-provider history="10"
decay="2">
<load-metric type="cpu" weight="2"
capacity="1"/>
<load-metric type="sessions"
weight="1" capacity="512"/>
</dynamic-load-provider>
</mod-cluster-config>
</subsystem>
Muito
importante configurar o atributo “proxy-list” para o
servidor de IP 127.0.0.1 (local) na porta 6666. Esse valor é
configurado também no arquivo httpd.conf do Apache Web Server
(httpd). Se não configurar isso, a instância do JBoss pode não
localizar o httpd e este último, não conseguir fazer os
redirecionamentos e começar a lançar os erros 404, “not found”,
não localizado.
No
subsistema de logging, acrescente o seguinte bloco para fazer log no
modcluster para debug.
<logger
category="org.jboss.modcluster">
<level
name="INFO"/>
</logger>
Arquivo nodo.sh
Esse arquivo será criado para iniciar cada instância do cluster.
Veja abaixo:
./standalone.sh -c standalone-full-ha.xml -b 127.0.0.1 -u 230.0.0.4 &
Para funcionar o cluster com JGroups, o parâmetro b não pode ser
configurado com “0.0.0.0”, deve ser atribuído endereço IP da
máquina que está rodando o JBoss AS 7. Caso esteja rodando tudo
local, use “127.0.0.1”.
O “&” (E comercial) serve para desvincular a execução do
comando do prompt, assim, mesmo que o console seja fechado, o
servidor continua ativo. Para desligar o servidor, precisa-se
executar esses comandos:
ps aux | grep “jboss”
Para
localizar o processo com o nome “jboss”.
kill
-TERM pid
Para
enviar uma mensagem para finalizar o processo, nesse caso, o servidor
JBoss AS 7. A variável “pid” é o número do processo obtido na
execução do comando anterior.
Ou para
desligar de vez (definitivamente) o servidor JBoss AS 7, digite
kill -9
pid
Depois
disso, copie esse arquivo para cada pasta “bin” de cada instância
do JBoss AS 7.
Dê
permissão de execução para esse arquivo “.sh”, com o comando
abaixo:
chmod a+x
nodo.sh
CONFIGURAÇÃO DO
JBOSS MOD_CLUSTER (LOADBALANCE).
na versão
1.1.x. A última versão até a escrita desse é artigo é a versão
1.1.3. Na documentação do JBoss AS 7, o site
www.jboss.org
deixa bem claro que deve ser usado o mod_cluster na versão
1.1.x. Se
utilizar outra versão, pode ser que não funcione.
Lembrando
também de baixar a versão para a plataforma alvo correta. Para
escrever esse artigo, usamos o sistema operacional Linux 32 bit. E
também, baixar o mod_cluster “completo”, no sentido de estar
junto com Apache Web Server, chamado também de “
httpd”. Pois o
mod_cluster do JBoss é uma extensão (módulo) do
httpd para
trabalhar com cluster de JBoss AS's. Se baixar somente os módulos,
deverá copiar os arquivos binários “
.so” (linux) ou “
.dll”
(windows) para os diretórios de módulos do httpd e ainda,
configurar o arquivo “httpd.conf”(que será usado a seguir). Mais
detalhes da configuração do
httpd, entre no site
http://httpd.apache.org/docs/2.2/
Mas nesse
artigo, usaremos a versão já empacotada do mod_cluster com o httpd.
2 –
Descompactar arquivo “mod_cluster-1.1.3.Final-linux2-x86-ssl.tar.gz”
com o comando:
tar xvfz
mod_cluster-1.1.3.Final-linux2-x86-ssl.tar.gz
3 –
Copiar o diretório "opt/jboss" para o diretório "/opt". Para isso,
precisa-se usar o superusuário.
Diretório
aonde descompactou o arquivo “tar.gz”.
Entre no
diretório “opt” criado, comando:
cd opt
Comando para cópia:
sudo cp
-R jboss /opt
4 –
Edite o arquivo httpd.conf localizado no diretório
“/opt/jboss/httpd/httpd/conf”.
Procure a
linha “ServerName” e descomente essa linha.
Mude
“www.example.com:80” para
“127.0.0.1:80”. Lembrando que a porta “80” é porta que será
usada para receber requisições HTTP e apartir daí, o httpd com o
mod_cluster (JBoss) delegará a requisição para a instância
do JBoss AS 7 que cumprir os critérios do loadbalance (dentro do
mod_cluster). Lembrando também que o endereço IP “127.0.0.1”
pode ser alterado para o endereço IP da máquina atual.
5 – Criar os scripts
para iniciar e parar o Apache Web Server com o mod_cluster.
O nosso loadbalance
conforme explicado anteriormente, funciona como um módulo (extensão)
do Apache Web Server, então criaremos scripts para iniciar e parar
esse servidor (httpd).
// Script para inicio
do Apache Web Server.
// Coloque o nome de
“httpd_start.sh”
// Dê permissão de
execução com o comando:
chmod a+x
httpd_start.sh
// Cole o conteúdo
abaixo no arquivo “httpd_start.sh”.
/opt/jboss/httpd/sbin/apachectl
start &
// De forma análoga,
vamos criar o arquivo “httpd_stop.sh”
// para parar o
servidor httpd.
// Dê permissão de
execução com o comando:
chmod a+x httpd_stop.sh
// Cole o conteúdo
abaixo no arquivo “httpd_stop.sh”.
/opt/jboss/httpd/sbin/apachectl
stop &
6 – Inicie o servidor
Apache Web Server (httpd) com o comando
sudo ./httpd_start.sh
// Lembrando que para
usar a porta 80 precisamos de acesso root ao sistema operacional.
7 – Entre em qualquer
navedor web (firefox, chrome, etc) e acesse o endereço:
Deverá aparecer a
seguinte mensagem:
“It works!”
Está trabalho, ativo.
RODANDO UMA
APLICAÇÃO WEB SIMPLES
Com o loadbalance
(mod_cluster) e as instâncias configuradas, precisamos criar uma
aplicação web simples e fazer a sua implantação (deploy) em cada
instância do JBoss AS 7. Para isso, vamos criar o servlet
“LoadBalanceServlet” cujo código será mostrado a seguir:
Arquivo
“LoadBalanceServlet.java”
package test;
import
java.io.IOException;
import
java.util.Calendar;
import
java.util.Enumeration;
import
java.util.Properties;
import
javax.servlet.ServletContext;
import
javax.servlet.ServletException;
import
javax.servlet.ServletOutputStream;
import
javax.servlet.annotation.WebServlet;
import
javax.servlet.http.HttpServlet;
import
javax.servlet.http.HttpServletRequest;
import
javax.servlet.http.HttpServletResponse;
/**
* Servlet
implementation class LoadBalanceServlet
*/
@WebServlet("/LoadBalanceServlet")
public class
LoadBalanceServlet extends HttpServlet {
private static final
long serialVersionUID = 1L;
/**
* @see
HttpServlet#HttpServlet()
*/
public
LoadBalanceServlet() {
super();
// TODO
Auto-generated constructor stub
}
protected void
doGet(HttpServletRequest request,
HttpServletResponse
response) throws ServletException, IOException {
ServletOutputStream
out = response.getOutputStream();
response.setContentType("text/html");
out.println("<html>");
out.println("<head>");
out.println("<title>"
+ "Teste loadbalance - Host "
+
request.getRemoteAddr());
out.println("</title>");
out.println("</head>");
out.println("<body>");
out.println("Nodo
do JBOSS chamado: ");
out.println("jboss.node.name:
"
+
System.getProperty("jboss.node.name"));
out.println("Tempo:
" + Calendar.getInstance().getTimeInMillis());
out.println("</body>");
out.println("</html>");
out.close();
out.flush();
}
/**
* @see
HttpServlet#doPost(HttpServletRequest request, HttpServletResponse
* response)
*/
protected void
doPost(HttpServletRequest request,
HttpServletResponse
response) throws ServletException, IOException {
doGet(request,
response);
}
}
A seguir será mostrado
o código do arquivo web.xml contendo a configuração do Servlet.
xml
version="1.0"
encoding="UTF-8"?>
<web-app
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
id="WebApp_ID"
version="3.0">
<display-name>LoadBalanceTestWAR</display-name>
<distributable/>
<servlet>
<servlet-name>LoadBalance</servlet-name>
<servlet-class>test.LoadBalanceServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>LoadBalance</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
</web-app>
Com essa configuração,
basta acessar o servlet pelo seguinte endereço:
<
Também precisamos do
arquivo “jboss-deployment-structure.xml” para fazer o
deploy da aplicação web.
Arquivo
“jboss-deployment-structure.xml”
xml
version="1.0"
encoding="UTF-8"?>
<jboss-deployment-structure
xmlns="urn:jboss:deployment-structure:1.1">
<deployment>
<dependencies>
<module
name="javax.api"
/>
</dependencies>
</deployment>
</jboss-deployment-structure>
Todos esses arquivos
“.xml” devem estar na pasta WEB-INF da aplicação web.
Como
estamos usando a porta 80 que é padrão para o protocolo HTTP,
podemos omitir a porta. Ao se fazer uma requisição para esse
endereço, o mod_cluster de acordo com as políticas definidas,
delegará a requisição para uma determinada instância do cluster
de JBoss AS 7. Esse servlet deve criado e empacotado como um arquivo
“LoadBalanceTestWAR.war”.
Para
testar o loadbalance, faremos diversas requisições para o mesmo
endereço (
http://127.0.0.1/LoadBalanceTestWAR)
diversas vezes, partindo de apenas um browser web ou diversos
browsers web.
Nas
figuras a seguir, serão mostradas duas chamadas para o mesmo
endereço que foi processado por dois nodos diferentes. Para simular
isso, basta clicar em atualizar diversas vezes, até mudar o texto da
página para outro nodo do cluster.
CONCLUSÃO
Esse
artigo mostrou todos os passos necessários para configurar um
loadbalance sobre um cluster de JBoss AS 7. E também, podemos mudar
as políticas envolvidas na seleção do nodo, como por exemplo:
O
nodo que estiver mais inativo, isto é, quantidade de cpu usada
menor, terá prioridade para receber requisições
O
nodo que estiver usando menos memória heap da JVM, terá
prioridade para receber requisições
O
primeiro nodo localizado que estiver ativo terá prioridade de
receber requisições.
O
nodo será escolhido de forma aleatória e o escolhido, receberá a
requisição.
O
nodo que processou menos requisições no dia terá prioridade para
receber requisições.
O
nodo que processou uma requisição anterior do mesmo host de
origem, terá prioridade para processar requisições.
Além
dessas regras, podemos criar outras regras e combiná-las entre si,
de acordo com as regras do negócio envolvidas. Basicamente,
recebemos como ENTRADA a requisição e no fim, o loadbalance deverá
retornar uma instância do cluster de JBoss AS 7 que deverá receber
a requisição. Depois disso, o mod_cluster delega a requisição
para o nodo escolhido. Esse processo se repete a cada requisição.
Portanto,
para maior aprofundamento, precisa-se saber como configurar o
mod_cluster para conter regras personalizadas e de acordo com o nosso
negócio em questão.
REFERÊNCIAS