Monitores
En la lección anterior estudiamos el funcionamiento de los enlaces entre procesos, y cómo se pueden usar para que un error en un proceso afecte a toda la malla de procesos que estén enlazados al proceso inicial.
Sin embargo, por lo general esta no será la solución que querremos tomar cuando un proceso tenga errores. Lo que querremos es detectar ese error desde fuera para tomar una acción correctiva, y esto no lo podemos hacer con enlaces normales, sino con monitores.
Cuando creamos un monitor, lo que estamos haciendo es crear una conexión entre dos procesos. En este caso, la relación no es bidireccional, sino que hay un proceso monitorizado y un proceso que monitoriza.
Para usar monitor con un proceso ya existente, puedes usar la primitiva Process.monitor/1, que recibe como parámtro el PID del proceso a monitorizar. El proceso que llame a Process.monitor se convertirá en el proceso que monitoriza el proceso monitorizado en busca de caídas. Eso significa que si el proceso A hace Process.monitor(b), una caída del proceso B notificará al proceso A, pero una caída de A no monitorizará al proceso B.
La función Process.monitor devuelve una reference(), que es una referencia de monitor que podemos usar luego para descartar el monitor usando Process.demonitor/1, como cuento un poco más abajo.
Una diferencia significativa entre link y monitor es que mientras que un par de procesos A y B sólo pueden tener un único enlace, pueden existir múltiples monitores vigilando el mismo grafo de procesos, así que puedes llamar a Process.monitor(pid) varias veces y recibirás una referencia nueva en cada caso. Cada referencia volcará un mensaje en el proceso que monitoriza, así que recibirás varias veces un mensaje si el proceso monitorizado cae.
Tratamiento de errores cuando hay monitores
Cuando haya establecido un monitor por el que A supervisa el proceso B, si el proceso B cae, el proceso A recibirá en su cola de mensajes una tupla con la siguiente forma:
{:DOWN, reference, :process, pid, reason}
El primer y el tercer elemento de la tupla tienen siempre el mismo aspecto. El primer elemento de la tupla es siempre el átomo :DOWN, mientras que el tercer átomo de la tupla, en el caso de Elixir, siempre va a ser :process. En cuanto a los elementos 2, 4 y 5:
- El segundo elemento de la tupla,
reference, es la referencia de monitor que previamente fue devuelta porProcess.monitor. - El cuarto elemento de la tupla,
pid, es el PID del proceso monitorizado, que es al que le pasamos como parámetro aProcess.monitor. - El quinto elemento representa la razón por la que se ha interrumpido la ejecución del proceso. Puede ser la razón del error o el código de estado general entregado a la primitiva
exit/1al detener el proceso.
Podríamos tratar el error con pattern matching. Por ejemplo, como parte de la recepción de un mensaje:
receive do
{:ok, res} ->
IO.puts("Este es un mensaje completamente normal")
{:DOWN, _, :process, _, reason} ->
IO.puts(:stderr, "Algo ha salido mal")
IO.inspect(reason)
end
Cómo eliminar un monitor
Si necesitas retirar un monitor de funcionamiento, puedes llamar a Process.demonitor/1 pasándole como parámetro la referencia que previamente te devolvió Process.monitor/1. El tipo de datos es la referencia, no el PID.