I'm writing a websocket server in PHP, that needs to be able to handle a large number of concurrent connections. I'm currently using the socket_select function to allow it to handle them, but this still blocks all other connections when sending a large block of data to a client. Is there a way for the master script to accept the incoming socket, and then start up a second PHP script (in a non-blocking fashion, obviously) and pass the client socket to that script for processing? I know this is possible in C, but the codebase is such that a migration is impossible, sadly.
*The server is running exclusively on a Unix stack, no need for a MS compatible solution.
I'm currently using the socket_select function to allow it to handle them, but this still blocks all other connections when sending a large block of data to a client.
Then don't send all the data at once. If you are going to do
socket_write ($mysocket, $mybuffer, 10000000);
then yeah, you'll have to wait until all 10 million bytes have been sent out. However, you can use the $write array of socket_select to check if you can write to the socket, in combination with non-blocking sockets. Each time socket_select says you have a 'go!' on the socket, write data until socket_write starts to complain (i.e. returns FALSE or less than the specified length). This will keep the socket's send buffer optimally filled.
The downside is that you must keep track of exactly where in your output buffer you are; also, turn off non-blocking on the socket after you've written al your data or socket_select will keep on firing (this assumes you want to send multiple large blobs of data).
The answer turns out to be the same answer you'd use in C - fork(). When you fork, the state of all open files and ports is preserved, so a child process can read a port that was opened by its parent (this is the same way that modern webservers spin off worker threads for each client connection that comes in) It does require using the pcntl (process control) module which is disabled by default and should be used sparingly, but it works:
if($verbose)
echo "Connected Client from $remoteaddy to $localaddy\n";
echo "Forking...";
$pid = pcntl_fork(); // you're bringing children into this world, just to kill them in a few seconds. You monster.
if($pid==0){
$p = posix.getpid();
echo "PID OF CHILD: $p\n";
//in child process. Send a handshake and wait for the callback from the WebSockets library
$this->send($client, "Thank you for contacting myAwesomeServer.com! I'm slave #{$p}, and I'll be your host today");
}else if($pid>0){
$childWorkers[]=$pid;
echo "[ OK ]\n";
$this->disconnect($client->socket, false); //disconnect the clients socket from the master thread, so only the child thread is talking to the client
}else if($pid==-1){
echo "[FAIL] unable to create child worker\n";
}
NOTE!! This approach is PURELY ACADEMIC, and should only be used on small, 'pet' projects when you don't have enough time to learn a more appropriate language (personally, I know C well enough to fork(), but my lack of knowledge of its string manipulation functions would no doubt leave a gaping security hole in the server). I'm not sure how the Zend engine is doing this pcntl_fork(), but I'd imagine that the memory image of this monstrosity is going to be many times the size of equivalent C code..
Related
I currently have a PHP function:
function sendLocalSocket($message, $port){
$context = new ZMQContext();
$socket = $context->getSocket(ZMQ::SOCKET_PUSH);
$socket->connect("tcp://127.0.0.1:$port");
$socket->send($message);
}
and a python function:
def create_local_socket(port, pull=False):
context = zmq.Context()
if pull:
# for receiving socket messages
socket = context.socket(zmq.PULL)
socket.bind("tcp://127.0.0.1:%d" % port)
else:
# for sending socket messages
socket = context.socket(zmq.PUSH)
socket.connect("tcp://127.0.0.1:%d" % port)
return socket
which then sends a message with create_local_socket(port).send_json()
Unfortunately either of these two functions when called in quick succession start to hang dramatically and slow down the rest of my system.
I have been thinking of ways to keep the sockets open for the never terminating python script but unfortunately for PHP this is called after a file upload.
The problem is that these functions are called arbitrarily - the php script uses the same port each upload but the python script uses different ports (although there is repeated use).
I know that the php is hanging because the python ZMQ listener logs the current time - the time the file was uploaded which takes longer and longer until the backlog of uploads dies down. I also know that the listener script is not hanging and takes 0.2 seconds after logging receiving the file. (but that is effectively a back log!)
I feel like the answer is to persist these connections.
Edit 1:
Trying: $context = new ZMQContext(1, true); did not help.
Given each call to either of the code-snippets generates another Context()-instance, this constitutes rather an anti-pattern for a reasonable use of resources.
A mild read about Zen-of-Zero will soon show the main reasons, why building a (semi-)persistent signalling / messaging layer is the way to go, as the resources allocations / deallocations are always expensive plus leaving so many un-terminated instances will soon deplete any amount of resources available.
ZeroMQ tools are far from being used as a one-shot disposable. Efficiency goes hand in hand with minimised resources-related overheads, so indeed best to re-design the ad-hoc creation of never deallocated resources pools and rather prepare all the needed tools before these need to be already active and ready to serve an ad-hoc request.
A due re-factoring of the Product has high time to take place.
While documentation reports some persistence aided modus-operandi, one ought carefully review its costs/benefits before going into this direction, as Zen-of-Zero is actually considering any sort of sharing a rather anti-pattern for distributed-system design practices, plus warns on:
Note
It’s important to remember that careless use of persistent sockets can exhaust available file-handles on the machine.
function sendLocalSocket( $message, // IS to be get delivered
$port // WAS reported to be
){ // all the time the same
$context = new ZMQContext( 2, true ); // MAY try persistent CTX
$socket = $context->getSocket( ZMQ::SOCKET_PUSH,
"persistLoggerID" // MAY use persistent SOCK
);
$socket->connect( "tcp://127.0.0.1:$port" ); // MAY use less expensive TC
$socket->send( $message,
ZMQ::MODE_NOBLOCK // USE non-blocking mode
);
$socket->disconnect( "tcp://127.0.0.1:$port" ); // USE .disconnect()
}
Final note
Quality, robustness and performance of any application depends on how well the language and the language-specific ZeroMQ-binding can respect the native API and reflect the best-practices evolved for using the native DLL-services. Any "abstraction" and/or "automated" steps, that reduce the designer's control over the scope and ordering of otherwise due steps on the native-API level may look comfortable, yet these also reduce the options for designing robust and high-performance deployments, as some native-API tweaking options need not be principally available at the user-application level, once having been shaded-out by language-specific ZeroMQ-binding abstractions.
I have a web server that sends push notifications to devices when certain actions are performed with a POST or GET. This works, but because of the nature of having to open an SSL connection to APNS, write all the tokens and exit, the actions that involve this operation are latent compared to those who do not. The latency is only a second or so, but a second is still significantly longer than say 100ms, which is how long it takes otherwise (if I comment out the APNS part).
It seems that it's also bad practice to open and close the connection to APNS every time you want to send notifications, not only because of the extra time it takes. I believe a better approach would be to have a different server (not running PHP) handle the stream writing to APNS and receive tokens and messages by let's say some kind of python service, perhaps.
Basically:
Web Server sends fwrite by php (unencrypted, no SSL) to a socket on a local server that has a persistent connection to APNS open, which also is asynchronous in its handling of the response from to the APNS operation: The problem is that PHP will wait until it has written all bytes to the socket before it echoes the desired response to the client. I would imagine it takes much less time to fwrite to a local server unencrypted than it does to SSL to APNS. Correct me if I'm wrong.
I don't have a lot of experience with Python, but as far as I know it's not that hard.
I have the tokens stored in MySQL and retrieving and generating the payload with PHP in place and working - it's just slowing everything down the way it's set up now.
I cannot find any guides on this subject - only how to do what I am already doing with fwrite and OpenSSL in PHP.
I seek suggestions as how to best handle this situation.
You don't need to switch to Python to solve this issue. What you could do is tweak your design a bit to resemble more of "dump and purge" concept.
Basically, the scripts that receive the GET and POST calls dump the data related to the push notification payload locally.
In the background, you have a PHP script running all the time that has already established a connection with APNS and just checks constantly if anything has been dumped locally and needs to be sent.
Script A (The dumper):
// ... something triggers a push ...
if ($_GET['something'] == 'foo') {
$data = // Get all data needed to build push
createPayload($data); // Dump data somewhere; file or database
}
// ... something else maybe ...
return; // Return asap to not keep client waiting
Script B (The purger):
$apns = // Open connection to Apple Push server
while (TRUE) {
// Read file or database where payloads get dumped
$success = (fwrite(payload to $apns));
sleep(5); // Sleep a bit to avoid CPU going crazy
if (!$success) {
// Reopen connection with Apple
}
}
// Close apns here in case you have a break condition in your loop
Using two scripts instead of one, you will return to your client as quickly as possible and still be able to send push notification quickly to Apple's servers.
I have PHP server, in which I need to update client A about some client B action, at the moment it's done. How it is normally being accomplished ?
My current solution would be:
A leaves an open $.ajax(...).done(function myCallBack(){...}); call for this.
when A is in myCallBack(){...} it issues another $.ajax(...).done(function myCallBack(){...});. This way the communication with server stays open for receiving new info anytime
the PHP would have to continually check for inter-session communication file to transfer data to/from two concurrent sessions
Leaving $.ajax(...).done(function myCallBack(){...}); open (and spawning new ones all the time) is the way to do it ?
For inter-session communication - is there a way to signal events or something like that (instead of continuously monitoring some file [a waste of resources!]) ?
This is how I solved it:
Client A leaves an open ajax call: $.ajax(...).done(function myCallBack(){...});: on the PHP server side (session), A locks on a semaphore using sem_acquire($semaphore_A), and waits.
Client B removes semaphore $semaphore_A using sem_remove($semaphore_A), thus releasing A that returns to client JS callback myCallBack().
Of-course, it's very simplistic, and I use shared-memory (e.g. shm_attach()) to exchange status and data between processes.
For other developers stuck with this technique:
what took me so long to debug it, is the fact the I opened a session (session_start()) and didn't close it (session_ write_ close()) - which locked other processes and prevented any concurrency !
Client A would send data to the server, Client B needs to poll the server to ask for changes. When there are changes Client B would get them on it's next poll.
I need to write some service for my application. I want each client to have limited persistent connections (like only allow first 10 clients to connect).
I know I can listen on the port with PHP by socket_listen(). The parent process accept the connection, then pcntl_fork() the process to have children to handle connection.
But as far as I know, PHP resources doesn't persist when fork()ed. I wonder if it is possible to do this with PHP, or I have to do this in C?
1)
Why bother forking? Run your daemon as a single process and use socket_select() ( or stream_select) to listen for requests.
See Aleksey Zapparov's code here for a ready-written solution.
2)
Why bother with the pain of writing your own socket code - use [x]inetd to manage the servers and do al the communication on stdio (note that unlike solution 1, there will be a seperate process for each client - therefore the handling code will be non-blocking)
--
You are correct in saying that PHP resources should not be available in a forked process - but give no indication of how this relates to your current problem. Is it just so that you can count the number of connections? Or something else? In the case of the former, there are much easier ways of doing this. Using solution 1, just increment and decrement a counter variable when clients connect/disconnect. In the case of 2, the same approach but keep the variable in a datafile/database (you might also want to store info about the connections and run occasional audits). Alternatively limit the connections on the firewall.
C.
Maybe you could try share it using memcache (http://www.php.net/manual/en/book.memcache.php). (i never tried this, may it may works)
Typical PHP socket functionality is synchronous, and halts the thread when waiting for incoming connections and data. (eg. socket_read and socket_listen)
How do I do the same asynchronously? so I can respond to data in a data received event, instead of polling for data, etc.
Yup, that's what socket_set_nonblock() is for. Your socket interaction code will need to be written differently, taking into account the special meanings that error codes 11, EWOULDBLOCK, and 115, EINPROGRESS, assume.
Here's some somewhat-fictionalized sample code from a PHP sync socket polling loop, as requested:
$buf = '';
$done = false;
do {
$chunk = socket_read($sock, 4096);
if($chunk === false) {
$error = socket_last_error($sock);
if($error != 11 && $error != 115) {
my_error_handler(socket_strerror($error), $error);
$done = true;
}
break;
} elseif($chunk == '') {
$done = true;
break;
} else {
$buf .= $chunk;
}
} while(true);
How do I do the same asynchronously?
so I can respond to data in a data
received event, instead of polling for
data, etc.
You will need to execute your script and issue stream_select to check weither there is any data to receive. Process and send data back.
The term "asynchronous" is often misused in network programming. For I/O, asynchronous is often just used as another word for non-blocking. This means that the process is able to continue before a call on the network api has completed transmission.
For process execution in general, asynchronous means that multiple instructions are able to be computed at once (concurrently.)
In other words, asynchronous I/O is not truly asynchronous unless multiple threads are used to allow multiple reads/write/accepts to occur concurrently - all sockets will sill have to wait on a synchronous non-blocking call if it has data to be read/written or will otherwise not block, and reading/writing a large file can still take seconds or even minutes if not interrupted. Note that this would require a perfect flow between the client and server or TCP itself will interrupt the transmission. For example, a server sending faster than a client can download would cause a block on a write.
So from a strict point of view PHP is not able to perform asynchronous networking, only non-blocking. In short, the progression of the process will stop while the network call is able to usefully read/write etc. However, the process will then continue when the call is not able to usefully read/write or would otherwise block. In a truly asynchronous system the process will continue regardless, and the read/write will be done in a different thread. Note that blocking I/O can still be done asynchronously if done in a different thread.
Moreover, PHP is not able to do event driven I/O without installing an extension that supports it. You will otherwise need to do some form of polling in order to do non-blocking I/O in PHP. The code from Chaos would be a functional non-blocking read example if it used socket_select.
With that said, the select function will still allow true non-blocking behavior in PHP. In C, polling services have a performance loss over event driven, so I'm sure that it would be the same for PHP. But this loss is in the nanoseconds-microseconds depending on the amount of sockets, where the time saved from a non-blocking call is typically milliseconds, or even seconds if the call is made to wait.
AFAIK PHP is strictly singlethreaded, which means you can't do this asynchronously, because Script execution is always linear.
It's been a while since i have done this, but as far as i recall, you can only open the socket, and have the script continue execution upon receiving data.