I have enabled the logs and here is what I got (several times) : 

Initialization
Apache Worker Process Initialization: PID=11330; Configuration=0x7fe92f816000;

Establish a new connection
Connection-No=156:0; Gateway PID=11330; 
      InterSystems IRIS Server=IRIS (xxx.xxx.xxx.xxx:56773);
      InterSystems IRIS PID=31932
      
(...)

Close Down
Apache Worker Process Close Down: PID=11330; Configuration=0x7fe92f816000;

This is on the non working server. The other one does not have such events, unless Apache is restarted (since there is no need to recreate new connections every time).

mpm module: MaxConnectionsPerChild (also named MaxRequestsPerChild in older versions) is set to 0. According to Apache docs, this means the process should never expire (but it does as seen in the logs). This is also the recommended setting from the InterSystems page you mentioned. MaxSpareThreads is not set on both servers (unlike what is recommended by InterSystems).

Intersystems page also recommend to use a "NSD based Gateway configuration" if needed. That's not what I use. Both servers load the /opt/webgateway/bin/CSPa24.so module, not the mod_csp24.so one.

Also: I have notified a difference in Apache version. The non working server use an older one. I will try upgrading it.

Thanks for you help. I took a look at Apache and I couldn't find anything wrong with it. Here is a summary of it :

LoadModule csp_module_sa /opt/webgateway/bin/CSPa24.so
CSPFileTypes csp cls zen cxw
CSPModulePath /opt/webgateway/bin/
CSPConfigPath "/opt/webgateway/bin/"

<Location "/csp/bin/Systems/">
    SetHandler cspsys-handler-sa
</Location>

<Location "/csp/bin/RunTime/">
    SetHandler csp-handler-sa
</Location>

Alias /iris/csp/ "/opt/webgateway/iris/csp/"
Alias /csp/ "/opt/webgateway/iris/csp/"

<Directory "/opt/webgateway/iris/csp">
    CSPFileTypes csp cls zen cxw
    AllowOverride None
    Options MultiViews FollowSymLinks ExecCGI
    Require all granted
    <FilesMatch ".(log|ini|pid|exe)$">
        Require all denied
    </FilesMatch>
</Directory>

<Directory "/opt/webgateway/bin/">
    AllowOverride None
    Options None
    Require all granted
    <FilesMatch ".(log|ini|pid|exe)$">
        Require all denied
    </FilesMatch>
</Directory>

<Location "/csp/">
    Require all granted
</Location>

<Location "/csp/bin/Systems/">
    SetHandler cspsys-handler-sa
    Require ip xxx.xxx.xxx.xxx
</Location>

<Location "/csp/bin/RunTime/">
    SetHandler csp-handler-sa
    Require ip xxx.xxx.xxx.xxx
</Location>

<LocationMatch "/csp/(sys|user|samples|docbook|documatic)/">
    Require all denied
</LocationMatch>

Here is the solution I end up using. This is based on a solution suggested by Julius Kavay :

set cmd="netstat -anp TCP"
set oldIO = $io
open cmd:"QR":10
use cmd
// in case, $zeof is not set per default
// set old=$system.Process.SetZEOF(1)  
for {
  read line  //timeout alternative, no need of $zeof: read line:1 
  quit:$zeof
  ... //do something with line
}
close cmd
use:oldIO]"" oldIO
// d $system.Process.SetZEOF(old)

You can find more info about it here.

As an alternative, it's also possible to use pipes ($zeof must be used as well): 

set dev="|CPIPE|"_$job   
open dev:cmd:10   
use dev
...

close dev

Here is what I end up using :     

set file = ##class(%File).%New("journal.txt")
set sc = file.Open("NW")

set path = ##class(%SYS.Journal.System).GetLastFileName()
set journal = ##class(%SYS.Journal.File).%OpenId(path)

set rec = journal.FirstRecord    
while $iso(rec)
{
    if (rec.TypeName = "SET") || (rec.TypeName = "KILL")
    {            
        do file.WriteLine(rec.Address_$c(9)_rec.TimeStamp_$c(9)_rec.ProcessID_$c(9)_rec.TypeName_$c(9)_rec.InTransaction_$c(9)_rec.GlobalNode_$c(9)_rec.DatabaseName)
    }
    set rec = rec.Next
}    

set journal = ""
do file.Close()

It's possible to change (and thus disable) concurrency of a persistent object by calling this private method (eg: in constructor) :

Method %OnNew() As %Status
{
    do ..%ConcurrencySet(0) // <--- here
    quit $$$OK
}

Disclaimer : this is an undocumented method. It might be removed from IRIS implementation in the future, use it at your own risk.

The parameter of that method accept same values as what is returned by $system.OBJ.GetConcurrencyMode().
Once is concurrency is disabled, updates from other processes to related global can occurs even within transactions (data consistency is lost). A possible workaround is to create a single lock on the global :

do ##class(Test.Test).%LockExtent()   //same as lock +^Test.TestD
//do something here 
//...
do ##class(Test.Test).%UnlockExtent() //same as lock -^Test.TestD

This prevent having many locks created during long duration transactions that create many persistent objects (but on the other side, it locks the whole global, not individual nodes).