Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Concurrent use of a persistent PHP socket

I am wanting to use PHP to create a persistent socket connection to a notification service server and I am wondering how many Apache/PHP threads would be able to concurrently use the socket before I have problems. I have done some testing with this but I cannot seem to produce any problems.


Edit

I am using the socket like this:

$fh = pfsockopen('127.0.0.1', '1338');
fwrite($fh,$data);

Every PHP thread would share the same persistent socket

like image 289
nathanjosiah Avatar asked Jan 10 '13 21:01

nathanjosiah


1 Answers

The limitation with fsockopen is the maximum amount of open file descriptors defined in the systems kernel settings. If pfsockopen is implemented well, it should only use one single socket connection, means only one file descriptor per php process.

You'll have to test this.

e.g.

$fd = pfsockopen('173.194.44.24', 80);
echo $fd;

This will output the id of the file descriptor: Resource id #1

Open this in a web browser and reload the page several times - you should see the same id each time you are using the same socket connection.

In a default Apache prefork MPM - mod_php setup you probably are randomly sent to different forked processes which will most likely result in n different ids cycling through, while n depends on your Apache configuration of

  • MinSpareServers (<= n pConnections)
  • MaxSpareServers (>= n pConnections)
  • MaxRequestsPerChild (tMax)

When you reach MaxRequestsPerChild the process is terminated and the persistent connection on this child as well.

In a Apache Worker MPM or any other fastcgi-capable webserver like Lighttpd or Nginx combined with PHP-FPM or PHP-cgi + fastcgi I am expecting the same behavior, now not caused by the webserver but by the php processes.

In parallel to the apache settings described above, the relevant settings are

PHP-FPM

  • pm.min_spare_servers (<= n pConnections)
  • pm.max_spare_servers (>= n pConnections)
  • pm.max_requests (tMax)

FastCGI

  • PHP_FCGI_CHILDREN (= n pConnections)
  • PHP_FCGI_MAX_REQUESTS (tMax)

In all configurations the maximum lifetime of a persistent connection is (in amount of requests handled by that process) tMax, the max amount of parallel persistent connections n pConnections

Simulating this on the command-line (php-cli)

# php -a
Interactive shell                            # in a webserver environment this is the equivalent of one child

php > $fd1 = fsockopen( 'google.de', 80 );   # open non-persistent connection
php > echo $fd1 . "\n";
Resource id #1
php > $fd2 = fsockopen( 'google.de', 80 );   # open another one
php > echo $fd2 . "\n";
Resource id #2                               # new fd, new connection

php > $pd1 = pfsockopen( 'google.de', 80 );  # persistent connection
php > echo $pd1 . "\n";
Resource id #3                               # first persistent fd
php > $pd2 = pfsockopen( 'google.de', 80 );
php > echo $pd2 . "\n";                        
Resource id #3                               # uses the same connection

php > exit                                   # simulating MaxRequestsPerChild threshold
# php -a
Interactive shell

php > $pd3 = pfsockopen( 'google.de', 80 );  # persistent connection, same host
php > echo $pd3 . "\n";
Resource id #1                               # resource id reused because all old connections are gone

EDIT

Acually I forgot to mention a second limitation. Connections of course can be closed anytime by the server itself. This heavily depends on the server settings and protocol you are using.

Most servers close a connection after n seconds of silence and after x seconds of total connection time.

pfsockopen handles this silently, it simply opens a new connection when the old one is gone.

Simulating this on cli again:

# php -a
Interactive shell

php > $pd1 = pfsockopen( '127.0.0.1', 80 );
php > echo $pd1 . "\n";
Resource id #1
php > $pd1 = pfsockopen( '127.0.0.1', 80 );
php > echo $pd1 . "\n";
Resource id #1

(restarting my webserver on the another console /etc/init.d/nginx restart)

php > $pd1 = pfsockopen( '127.0.0.1', 80 );
php > echo $pd1 . "\n";
Resource id #2
like image 74
Michel Feldheim Avatar answered Oct 23 '22 08:10

Michel Feldheim