Cancelar sockets postgresql
Cancelar sockets postgresql
Nota : los comandos enunciados a continuación están funcionado en Linux Centos 7.0 java 1.8 y postgres 9.0
En el desarrollo de aplicaciones y el uso de Postgres me he encontrado con un inconveniente por demás muy molesto de los sockets IDLE, (ociosos), que aunque no consumen mucho recuros de CPU y muy poca memoria , tiene el inconveniente de ir sumando en la cuenta de el máximo numero de sockets abiertos, que en general es un numero limitado casi nunca superior a 3 0 4 centenares, podría decirse que son bastantes en general pero si tiene una pagina de mucho trafico estos valores limite pueden ser rebasados rápidamente, y dejar el postgres inoperante por un largo periodo de tiempo, el caso es que aunque uno se esfuerce mucho en abrir y cerrarlos con comando como rt.close() o st.close() por ejemplo, no faltan los usuarios indisciplinados que no terminan los procesos pues cancelan la session de forma imprevista al salirse sin dejar terminar los procesos de forma natural en el navegador, por esta razón me ha tocado implementar algún método que me permita eliminar estos sockets ociosos problemáticos.
La solucion la he implementado en dos formatos básicos pero siempre partiendo del hecho de que tengo un (job) trabajo ejecutándose de forma periódica cada cierto tiempo , yo suelo usarlo cada 5 minutos mediante la utilidad cron (ver comando cron en linux para programar tareas repetidas sin intervención).
Se creó un sencillo scrip que solo difere en un comando dependindo si deseo que se ejecute como un comando del sistema operativo un poco mas burdo en el control de tiempo o uno mas afinado con verificacion de tiempo de socket inactivo, ambos funcionan pero dependen de la necesidad especifica.
1. Modo usando solo comandos del sistema operativo.
El comando central es el siguiente:
ps -ef | grep
postgres | grep idle |sort -n -k5.1 -k5.2 -k5.4 -k5.5|awk 'NR < 355 {print $2}'| xargs kill -15
a. ps -ef : lista los sockets disponibles en el sistema
b. grep postgres : selecciona solo los sockets postgres
c. grep idle : solo lo que están inactivos
c.
grep idle |sort -n -k5.1 -k5.2 -k5.4 -k5.5 : organiza la lista de sockets IDLE por el 5 campo que es donde está la hora/fecha de lanzamiento del socket (5.1=primr digito de 5.2=segundo digito de hora 5.4= primer digito de minutos 5.5= segundo digito de minutos,
-r organización de mayor menor y -k=Posix1
d. awk '{print $2}' : awk es un shell de linux para la busqueda y tratamiento de archivos de texto, con el comando print $2 le indico a awk que solo estoy interesado en la segunda columna listada por ps -ef , donde esta los numeros de los sockets
e. awk 'NR < 355{print}' : Muestra las primeras 355 primeras lineas, como solo deseo borrar las primeras 355 primeras ocurrencias de sockets inactivos , y en mi script MAXIMO está en 450 dejo una salvaguarda los los últimos 45 (10%), sockets que pueden estar siendo procesados en el momento de la ejecución del script para evitar cancelar alguna tarea que se esta pueda estar ejecutando, en mi caso he visto que un margen 10% aproximadamente me ha funcionado bien. en este caso especifico 450*(10/100)=355. sockets a borrar.
f. xarg : recorre de forma secuencial el resultado entregado por awk y ejecuta un kill sobre cada una de las entradas, eliminado cada socket que se ajuste al requerimiento de ser de postgres y estar en estado IDLE. el kill -15 evita que cancele también el proceso padre de postgres que sería un efecto indeseado ya que me apaga la base de datos.
#*************************************************
# el script /usr/bin/sos_postgresql
# Define el MAXIMO numero de sockets abiertos
# USUARIOS son los sockets IDLE actuales
#*************************************************
MAXIMO=450
USUARIOS=`ps -e | fgrep "postmaster" | wc -l`
if [ ${USUARIOS} -gt ${MAXIMO} ]
then
ps -ef | grep
postgres | grep idle |
sort -n -k5.1 -k5.2 -k5.4 -k5.5|awk 'NR < 355 {print $2}'|
xargs kill -15
fi
Un pequeño script que implementa no un conteo de lineas sino un retardo de
10 segundos antes del borrado para dar tiempo a que los sockets listados
se cierren
el comando ping con:
con -i 1 : espere un segundo
-c 10 : haga 10 veces el ping
Se podría haber colocado -i 10, pero lo que se dice es que genere un ping con un intervalo de 10 segundos, no hay garantía de que sean 10, depende del linux, no trate de usar el ping en una sola línea como en el script anterior porque el awk lo que recibe es la salida del ping y no el conenido de los sockets a eliminar, no funciona, por eso se necesita el archivo intermedio sos.txt para almacenar los sockets que están IDLE, esperamos que terminen normalmente los que están en proceso, durante unos 10 segundos en este caso y procedemos a eliminarlos los que quedaron registrados en sos.txt
ping -i 1 -c 10 127.0.0.1 > null
Horrible, horrible pero efectivo como feo.
#*************************************************
# el script /usr/bin/sos_postgresql
# Define el MAXIMO numero de sockets abiertos
# USUARIOS son los sockets IDLE actuales
#*************************************************
MAXIMO=450
USUARIOS=`ps -e | fgrep "postmaster" | wc -l`
if [ ${USUARIOS} -gt ${MAXIMO} ]
then
ps -ef | grep postgres | fgrep 'idle' > sos.txt
ping -i 1 -c 10 127.0.0.1 > null
awk '{print $2}' sos.txt
|
xargs kill -15
fi
2. Modo usando java
#*************************************************
# el script /usr/bin/sos_postgresql
# Define el MAXIMO numero de sockets abiertos
# USUARIOS son los sockets IDLE actuales
#*************************************************
MAXIMO=450
USUARIOS=`ps -e | fgrep "postmaster" | wc -l`
if [ ${USUARIOS} -gt ${MAXIMO} ]
then
java SosSocket
fi
-------------------------------------------------------------------------------------------------
Script java para cancelar sockets
El principio básico es consultar una vista definida de postgres, sobre una base cualquiera que exista. en este ejemplo "mibase", con el usurio de la base "miusuario", llamada pg_stat_activity que informa el estado de los sockets postgres consulta el campo procpid donde esta el ID del socket y verifica
medinte current_query='<IDLE>' el estado del socket , nota la consulta '<IDLE>' depende de la versión de postgres puede tener o no corchetes, paréntesis, comillas dobles o sencillas, verifique
su sistema especifico como presenta el campo current_query para sockets inactivos, recuerde solo debe cancelar los sockets IDLE, pues hay diferentes versiones de IDLE, dentro de lo cuales están
lo que se encuentran en waiting y transation, esos no deberán ser eliminados porque abortan un proceso que está en ejecución
now()-query_start > '00:05:00', garantiza que solo elimina los que tiene mas de 5 minutos inactivos, es decir que es casi seguro que no van a entrar en waiting. La pareja de comando listados a continuación efectúan un llamado al comando del sistema operativo que es el encargado de cancelar sockets.
socketIDLE = "kill -15 " ;
Sos.Run(socketIDLE) ;
Sos.Run efectúa el procedimiento
Runtime command = Runtime.getRuntime() ;
que permite llevar a cabo la ejcucion de comandos del sistema
operativo desde java.
/*
-----------------------------------------------------------------------------------------------------------------
javac SosSocket.java
-----------------------------------------------------------------------------------------------------------------
*/
import java.util.Calendar ;
import java.io.* ;
import java.sql.* ;
import java.net.* ;
import java.util.* ;
import java.text.* ;
public class SosSocket
{
public static void main(String [ ] args)
{
SocketCancelar() ;
}
static public void SocketCancelar()
{
Connection conexion = null ;
Statement st ;
Vector sockets = new Vector() ;
String socketIDLE = new String() ;
int i = 0 ;
int j = 0 ;
try
{
st = Conexion() ;
sockets = ConseguirSocketsIDLE(st) ;
if (sockets.size() > 0)
{
socketIDLE = "kill -15 " ;
for (i=0 ; i < sockets.size(); i++)
{
socketIDLE += sockets.elementAt(i).toString() ;
Sos.Run(socketIDLE) ;
}
}
st.close() ;
}
catch (Exception e)
{
System.out.println (e.toString()) ;
}
}
static public Vector ConseguirSocketsIDLE(Statement st)
{current_query='<IDLE>'
ResultSet rt ;
StringBuffer SqlSb = new StringBuffer() ;
Vector sockets = new Vector() ;
SqlSb.append(" SELECT ") ;
SqlSb.append(" procpid ") ;
SqlSb.append(" FROM ") ;
SqlSb.append(" pg_stat_activity ") ;
SqlSb.append(" WHERE ") ;
SqlSb.append(" current_query='<IDLE>' ") ;
SqlSb.append(" AND ") ;
SqlSb.append(" now()-query_start > '00:01:00' ") ;
try
{
rt = st.executeQuery(SqlSb.toString()) ;
while (rt.next())
{
sockets.addElement(rt.getString(1)) ;
System.out.println (rt.getString(1)) ;
}
rt.close() ;
return (sockets) ;
}
catch(Exception e)
{
System.out.println (e.toString()) ;
return (sockets) ;
}
}
static public Statement Conexion()
{
Connection conexion = null ;
Statement st ;
StringBuffer SqlSbC = new StringBuffer() ;
try
{
SqlSbC = new StringBuffer() ;
SqlSbC.append("jdbc:postgresql:") ;
SqlSbC.append("movimiento") ;
String dataBase = SqlSbC.toString() ;
Close() ;
Class.forName("org.postgresql.Driver") ;
conexion = DriverManager.getConnection(
"miBase","postgres","miusuario") ;
st = conexion.createStatement() ;
return(st) ;
}
catch(Exception e)
{
return (null) ;
}
static public void Run(String comando)
{Runtime command = Runtime.getRuntime() ;
Runtime command = Runtime.getRuntime() ;
Process proceso = null ;
try
{
proceso = command.exec(comando) ;
proceso.waitFor() ;
}
catch (Exception e)
{
System.out.println (e.toString()) ;
}
}
hola
ResponderEliminarEl kill -9 no ocasiona que toda la instancia PostgreSQL se caiga?? pensaría que debería emplear un kill -15.
Gracias por el comentario efectivamente eso ocurre , ya implemente esta observacion
ResponderEliminar