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