lunes, 12 de septiembre de 2011

Controla el estado de la memoria en aplicaciones Java

En muchas ocasiones nos encontramos con aplicaciones que tienen leak de memoria, y se va consumiendo este recurso sin que sea liberado adecuadamente. La solución: corregir el código, que suele ser el principal origen del problema. Pero cuando eso no depende de ti y tienes que ofrecer disponibilidad en dicha aplicación, una posible solución es controlar el consumo que hace la aplicación de la memoria, y en función de esta, realizar un reinicio antes de su degradación.

Aquí dejo un script que puede resultar útil. Se basa en controlar como se consume la memoria en las zonas Eden y Old Generation. Al haber una fuga de memoria, la zona Old Generation no se liberará como debe, y la zona Eden se consumirá también, imposibilitando la creación de nuevos objetos.

El script lo que hace es controlar estas zonas y cuando se alcance cierto umbral durante cierto número de ciclos consecutivos, reiniciar la aplicación (evitamos que la aplicación se acabe degradando y que al final la tengamos que reiniciar a mano):



#!/bin/bash

#Se obtiene el PID del proceso java utilizando jps y filtrando por Bootstrap
PID=`jps | grep Bootstrap | cut -d " " -f 1`
DATE=`date +%d-%m-%y----%H:%M:%S`
#Log para registrar las acciones del propio script
LOG="/scripts/reinicio_mem.log"
#Fichero temporal
TMP="/tmp/valores.txt"
#Se lanza el jmap -heap para obtener los valores del Eden y Old Generation
su - tomcat -c "jmap -heap $PID | grep -A4 -e Eden -e Old | grep % | cut -d '%' -f 1" > $TMP
CONT=1
FICH_CONTADOR="/tmp/contador"
FICH_SEGUIDO="/tmp/seguido"
#Script para levantar tomcat
SCRIPT_ARRANQUE="/scripts/arranque_tomcat.sh"
#Script para parar tomcat
SCRIPT_PARADA="/scripts/parada_tomcat.sh"
#Script para el envío de correo
SCRIPT_CORREO="/scripts/envia_correo.py"

#Función para reiniciar tomcat
reinicio () {
    $SCRIPT_PARADA &
    wait
    $SCRIPT_ARRANQUE &
    wait
    $SCRIPT_CORREO &
    wait
    return 0
}


#Función para controlar el conteo de veces seguidas que supera el valor umbral. 
#Tras 5 veces seguidas se procede a reiniciar tomcat
contador () {
    if [ -e $FICH_SEGUIDO ]; then
        VALOR_CONTADOR=`cat $FICH_CONTADOR`
        VALOR_CONTADOR=`expr $VALOR_CONTADOR + 1`
        if [ $VALOR_CONTADOR -eq 5 ]; then
            rm -rf $FICH_SEGUIDO
            echo "Se alcanzan valores por encima del 99% durante 5 veces consecutivas. Se reinicia tomcat." >> $LOG
            echo 0 > $FICH_CONTADOR
            reinicio
        else
            echo $VALOR_CONTADOR > $FICH_CONTADOR
        fi
    else
        touch $FICH_SEGUIDO
        echo 1 > $FICH_CONTADOR
    fi
}

#Se asigna de forma dinámica el nombre de la variable y su valor
for i in $( cat $TMP | tail -2); do
    eval export "VAR_${CONT}=`echo $i`"
    CONT=2
done

echo $DATE >> $LOG
echo "Eden: " $VAR_1"%" >> $LOG
echo "Old: " $VAR_2"%" >> $LOG
let EDEN=`echo $VAR_1 | cut -d "." -f 1`
let OLD=`echo $VAR_2 | cut -d "." -f 1`
if [ $EDEN -ge 99 ] && [ $OLD -ge 99 ]; then
    echo "Eden y Old Generation por encima del 99%" >> $LOG
    contador
else
    echo "Eden y Old Generation por debajo del 99%" >> $LOG
    rm -rf $FICH_SEGUIDO
fi

Para el envío de correo, podemos utilizar un script como este:


 #!/usr/bin/env python
# -*- coding: iso-8859-15 -*-

import os, string,smtplib, socket
from email.MIMEBase import MIMEBase
from email.MIMEText import MIMEText
from email.Utils import COMMASPACE, formatdate
from email import Encoders

maquina = socket.gethostbyaddr(socket.gethostname())[0]

#Funcion para el envio de correo
def sendMail(to, subject, text, server="servidor.domino.es"):
    assert type(to)==list
    fro = maquina + "@dominio.es"

    COMMASPACE = ', '
    msg = MIMEText(text)
    msg['From'] = fro
    msg['To'] = COMMASPACE.join(to)
    msg['Date'] = formatdate(localtime=True)
    msg['Subject'] = subject


    smtp = smtplib.SMTP(server)
    smtp.sendmail(fro, to, msg.as_string() )
    smtp.close()


sendMail(
        ["destinatario@dominio.es"],
        "Reinicio " + maquina,"Se ha reiniciado la " + maquina + " tras 5 ciclos seguidos con valores por encima del umbral de uso de memoria."
        )

No hay comentarios:

Publicar un comentario

Obtener informacion de repositorios a través de los metadatos .git publicados por error

 A raiz de CTF realizado recientemente, me ha parecido interesante publicar este post sobre los errores de seguridad que se encuentran en mu...