29 de mar de 2009

Monitoração de domínio e gravação de thread dump automático com WLST

Outro dia me deparei com um questionamento no fórum de discussão sobre como conseguir retirar os thread dumps do Servidor de aplicações, Oracle Weblogic, em momento oportuno, ou seja, quando o ambiente estiver passando por um momento de contingência. A discussão do problema pode ser vista detalhadamente no fórum do javaranch no link: http://www.coderanch.com/t/427054/BEA-Weblogic/Issue-with-WeblogicServer

Devido a este problema decidi escrever um artigo mostrando como podemos utilizar a linguagem de scripts do Weblogic Server, Weblogic Scripting Tool(WLST), para fazer monitoramento de servidores. Um ponto importante nestes script é que ele permite inclusive que este monitoramente seja feito de forma independente do Admin Server, pois neste caso, o script mostra conexão direta com servidores gerenciados de um domínio. Para escrevê-lo estou utilizando WLST que por sua vez utiliza linguagem Jython e é padrão para configuração do Oracle Weblogic.

O script:

O script demonstrado aqui é bem simples e o que ele faz basicamente é:

1) Carregar informações de um arquivo de propriedades.

2) Tenta conectar com cada um dos servidores da lista de servidores configurada no arquivo de propriedades.

3) Verifica status do servidor e caso o servidor estiver com um número maior que o definido de threads com delay na resposta gera um thread dump do servidor para posterior análise.

Abaixo segue o código do script comentado e logo em seguida um exemplo de arquivo de propriedades que pode ser utilizado como template.

from java.util  import Properties
from java.io  import FileInputStream
from java.io  import File

#============DEFINICAO DE FUNCOES============
#funcao que carrega dados de arquivo de propriedades
def carregaArquivoPropriedades(nomeArquivo) :
    print 'carregando propriedades do arquivo: ' + nomeArquivo
    #arquivo de propriedades a ser carregado
    localizacaoArquivoPropriedadesConfiguracao = nomeArquivo    
    myProps = Properties()
    #carrega arquivo de propriedades
    myProps.load(FileInputStream(File(localizacaoArquivoPropriedadesConfiguracao)))
    return myProps

#funcao que retorna lista de todos os servidores configurados para este domínio.
def getServidoresDominio():
    print 'executando funcao getServidoresDominio']
    domainConfig()
    servidores = cmo.getServers()
    return servidores

#funcao que pega lista de servidores como parametro e verifica o status de cada um deles.
def mostraStatusServidores(servidores): 
    print '=======================STATUS dos servidores do domínio==============='
    domainRuntime()
    for servidor in servidores:
        try:
            cd('/ServerRuntimes/' + servidor.getName())
            #ls()
            print servidor.getName() + ': ' + get('State')
        except WLSTException,e:
            servidoresNaoAcessiveis.append(servidor)
    servidoresAlerta = []
    #testa novamente servidores que não estava acessíveis para ter certeza
    for servidor in servidoresNaoAcessiveis : 
        try:
            cd('/ServerRuntimes/' + servidor.getName())
            #ls()
            print servidor.getName() + ': ' + get('State')
        except WLSTException,e:
            servidoresAlerta.append(servidor)
    if len(servidoresAlerta) > 0 :
        print 'ALERTA == ALERTA == ALERTA == ALERTA == ALERTA == ALERTA =='
        print 'Os seguintes servidores estão inacessíveis para monitoração, favor checar!'
        for servidor in servidoresAlerta :
            print servidor.getName()
            
#faz checagem de memória nos servidores do domínio especificado.
def listaConfiguracoesMemoriaServidoresDomino():
    servidoresComBaixoHeapMemoria = []
    for servidor in servidores:
        try:
            cd("domainRuntime:/ServerRuntimes/" + servidor.getName() + "/JVMRuntime/" +servidor.getName())
            heapSizeMax = cmo.getHeapSizeMax()/1048576
            heapMemoriaLivre = cmo.getHeapFreeCurrent()/1048576
            heapMemoriaLivrePercentagem = cmo.getHeapFreePercent()
            #adiciona na lista de servidores com pouca memoria se servidor tiver menos de 2% de memória livre
            if heapMemoriaLivrePercentagem < 2 :
                servidoresComBaixoHeapMemoria.append(servidor)
            #memoriaMB = memoria/1048576 #transforma os bytes em MB para mostrar
            print '\n================================================================='
            print "O servidor " + servidor.getName() + " esta com :"
            print 'Memoria total alocada(MB): ' + str(heapSizeMax)
            print 'Memoria total disponivel(MB): ' + str(heapMemoriaLivre)
            print 'Memoria livre(%): ' + str(heapMemoriaLivrePercentagem)
        except WLSTException,e:
            print 'AVISO: O servidor ' + servidor.getName() + ' nao esta acessivel atraves do admin server'
    #faz novo teste de heap de memória e caso continuem com pouca memória disponível, mostra ALERTA para observação do servidor.
    for servidor in servidoresComBaixoHeapMemoria :
        try:
            cd("domainRuntime:/ServerRuntimes/" + servidor.getName() + "/JVMRuntime/" +servidor.getName())
        except WLSTException,e:
            print 'AVISO: O servidor ' + servidor.getName() + ' nao esta acessivel atraves do admin server'
            heapMemoriaLivrePercentagem = cmo.getHeapFreePercent()
            #adiciona na lista de servidores com pouca memoria se servidor tiver menos de 2% de memória livre
            if heapMemoriaLivrePercentagem < 2 :
                print 'ALERTA == ALERTA == ALERTA == ALERTA == ALE RTA == ALERTA =='
                print 'O servidor ' + servidor.getName() + ' está com heap de memória < que 2% e deve ser observado!'
#faz checagem de threads dos servidores do domínio
def checaThreadsServidoresDominio() :
    servidoresComThreadsProblematicas = []
    for servidor in servidores :
        try:
            cd("domainRuntime:/ServerRuntimes/" + servidor.getName() + "/ServerChannelRuntimes/DefaultSecure[https]")
            numeroConexoes = cmo.getConnectionsCount()
            print '==============================================='
            print 'Dados do servidor ' + servidor.getName() + ': '
            print 'Conexoes https: ' + str(numeroConexoes)
            cd('domainRuntime:/ServerRuntimes/' + servidor.getName() + '/ExecuteQueueRuntimes/weblogic.socket.Muxer')
            fila = cmo.getPendingRequestCurrentCount()
            print 'Requisicoes de usuarios pendentes: ' + str(fila)
            cd('domainRuntime:/ServerRuntimes/' + servidor.getName() + '/ThreadPoolRuntime/ThreadPoolRuntime')
            hoggingThread = cmo.getHoggingThreadCount()
            print 'Quantidade de threads com status hogging: ' + str(hoggingThread)
            print '===============================================\n'
        except WLSTException,e:
           print 'Erro servidor não acessivel para ServerRuntimes ==>> ' + servidor.getName()

#faz health check nos pools de conexao do ambiente.
def checaPoolsConexoes() :
    domainConfig()
    #recupera lista de pools de conexões configuradas no domínio.
    connectionPools = cmo.getJDBCSystemResources()
    for pool in connectionPools : 
        print pool.getName()
        cd('domainConfig:/JDBCSystemResources/' +pool.getName()+ '/JDBCResource/' +pool.getName()+ '/JDBCConnectionPoolParams/' + pool.getName())
        print '==============================================='
        print 'Dados do pool: ' + pool.getName()
        capacidadeInicialPool = cmo.getInitialCapacity()
        capacidadeMaximaPool = cmo.getMaxCapacity()
        print 'Capacidade maxima do pool: ' + str(capacidadeMaximaPool)
        cd('domainConfig:/JDBCSystemResources/' + pool.getName())
        targetServers = cmo.getTargets()
        print 'targets do pool'
        for targetServer in targetServers :
            print targetServer.getName()
#=================FIM DEFINICAO DE FUNCOES======================
propriedades = carregaArquivoPropriedades('config.properties')
username = propriedades.get('username')
password = propriedades.get('password')
url = propriedades.get('url')
connect(username, password, url)
#lista para armazenar servidores não acessíveis através do admin server.
servidoresNaoAcessiveis = []
servidores = getServidoresDominio()
mostraStatusServidores(servidores)
listaConfiguracoesMemoriaServidoresDomino()
checaThreadsServidoresDominio()
checaPoolsConexoes()
disconnect()
exit()


E o arquivo de propriedades de exemplo:
#este arquivo de propriedades é utilizado para configurar os   
#servidores em um ambiente. Todas as chaves devem ter o formato  
#do nome utilizando sempre _ (underscore) pois é o que define a   
#busca dos nomes dos elementos e separa durante a configuarção   
#no script criaInstancias.py  
USUARIO_ADMIN=user  
SENHA_ADMIN=pwd  
   
PORTA_HTTPS_SERVIDORES=443  
   
#parametro que configura com que quantidade de threads com delay o thread dump deve ser tirado para o servidor.  
PARAMETRO_THREAD_DUMP=10  
   
#Dados dos servidores a serem monitorados pelo script  
SERVIDOR_1=server1  
IP_SERVIDOR_1=xxxxxx  
  
SERVIDOR_2=server2  
IP_SERVIDOR_2=xxxxxxx  
   
SERVIDOR_3=server3  
IP_SERVIDOR_3=xxxxxx  
   
SERVIDOR_4=server4  
IP_SERVIDOR_4=xxxxxx  
   
# .... you may add as many servers as you want here...  


Para este script ficar realmente funcional eu programaria para que ele ficasse conectado com os servidores por um período definido em ciclos e que verificasse o status de cada um de tempos em tempos(3 em 3min por exemplo). E caso o servidor apresentasse algum problema considerável dispararia uma rotina que seria responsável por tirar thread dumps com intervalo de cerca de 20s entre um e outro por um período de 2 min. Os thread dumps retirados seriam de extrema utilidade para definir qual o real problema com o ambiente.

[]s

Nenhum comentário: