Process Manager - php

I am trying to write a wrapper around a simulator which provides a web API so that users can spawn off simulations on a server and collect the results.
My initial design was to have a php simulation StartSimulation.php running in Apache which will fork & exec the simulation, and pipe the results to a file. However, you cannot fork & exec in php inside an apache module. Furthermore, it gets a bit messy in this implementation because there is no processes managing the orphaned simulations, so it is tricky to kill them and count them. It seems a bit difficult to use a standard web server since there is no global state which stores the list of running processes (simulations).
The simulator is written in c++, but It should be modified as little as possible. I prefer to write a wrapper for it and exec the simulator and capture its stdout and make that accessible to the user.
Basically, the wrapper should have an web-accessible API with 3 commands.
1.) start_simulation - which basically forks & execs an instance of the simulator, recording its pid in a table, and piping its stdout to a known buffer. The wrapper should not allow more than 16 instances of the simulator to run on the server. If successful, it returns a simulation code to the user. Otherwise, it returns an error code.
2.) stop simuation - takes the simulation code returned in (1), and kills the associated process.
3.) get_process - takes the simulation code returned in (1), looks up the known buffer which holds the stdout for that process, and returns that information to the user.
Should I write a custom application server for this? If so, is there a nice package with some starter code?
Thanks
Kyle

I know this is an old question but hopefully this will still prove helpful to somebody...
Having the process management (fork/exec/wait/kill/signal etc.) in a PHP script called directly from Apache via an http request is definitely not the way to go. As you say, it gets messy very quickly :)
I would suggest that the PHP script called via http is simply a command proxy to a simple server process. If PHP is your preferred language, you can implement both this way.
For example, you can do this with message queues as follows...
You can create a fairly simple PHP server that creates a message queue and wait for
messages to come in. It can then do the work of starting or stopping simulator processes
The remote user selects an operation (start, stop, get output) via a web page form.
This results in an HTTP/POST request sending the form data to your PHP script (I'd do this as an AJAX call so I can send the data and interpret the result without reloading the page)
Your server-side PHP script can interpret the form data and send a command via a message to the PHP server process
Let's illustrate this with some PHP code. I'm keeping this trivial and unsophisticated in the interests of brevity.
PHP Script (web form target)
This is where we interpret our incoming form request and turn it into a message for the server process
<?php
/*
* Responses are sent as simple text strings to be interpreted by
* the client-side JavaScript handling this AJAX call. Responses
* starting with 'ERR:' are errors, responses starting with 'ACK:'
* are acknowledgements. Simply check the first few characters of
* the response in the client-side JavaScript.
*/
header('Content-Type: text/plain; charset=UTF-8);
/*
* Here we define some message types, one per command. These should correspond
* to the command string sent from the web form
*/
$command = array(
'START_SIM' => 1,
'STOP_SIM' => 2,
'GET_STATUS' => 3,
'GET_OUTOUT' => 4
);
$queue_id = 0xbeef; /* An arbitrary message queue id */
$cmd = $_REQUEST['command']; /* The command from the web form */
/* get simulator instance id to act upon, if specified */
if (isset($_REQUEST['id']))
$sim_id = $_REQUEST['id'];
else
$sim_id = ''; /* no sim id? probably a create command */
/* check the message queue exists... */
if (msg_queue_exists($queue_id) === false) {
echo 'ERR:Message queue not found!';
return;
}
/* open the message queue and pass the command on to the server... */
if (($qhandle = msg_get_queue($queue_id)) === false) {
echo 'ERR:Failed to open message queue to server';
return;
}
if (msg_send($qhandle, $command[$cmd], $sim_id) === false)
echo 'ERR:Failed to send command';
else
echo 'ACK:Command sent ok';
?>
PHP Server (run separately on your web server)
And here's an equally simple server...
<?php
/*
* assume the same queue id's and defines as in the
* client code above, etc..
*/
if (($qhandle = msg_get_queue($queue_id)) === false) {
/* emit failure message to log file etc.. */
...
return;
}
while (1) {
if (msg_receive($qhandle, 0, $msgtype, $message,
true, 0, $rc) === false) {
/* log error message ... */
} else {
/*
* Get the client id (in case you want to send
* a reply back to the client) and the
* message data, which is the simulation id.
*
* Remember that we use the message type to
* indicate the command being requested
*/
$client = $message['client'];
$sim_id = $message['msg'];
evaluate_command($client, $msgtype, $sim_id);
}
}
?>
Obviously this is horribly simple, has no error checking and you'll need to write the "evaluate_command()" function yourself. I've just scribbled this down to illustrate the idea (and I've written this off the cuff, so it may be replete with other errors too!)

Related

PHP fputs "waits" until the end of the script

Currently I am trying to develop a PHP script used as a publicly available part of a client/server application. The php script should be used to authenticate users with a one-time token.
The other part of the application is a java program, which offers a telnet socket for other applications to connect to. Authentication is done through this telnet connection.
The java part is already working - but I still have a huge problem with the PHP part.
In php, I have opened a connection to the telnet port of the java program, which works so far. After the connection is initialized, the java program waits for input from the PHP script in order to authenticate the user.
After the authentication process has been finished, it returns a String to the PHP script (or any other program connected to its telnet server) which the PHP script should output.
Before I explain my problem, this is the part of the PHP script where the actual communication happens:
$tnconn = fsockopen("localhost", 53135, $errno, $errstr, 2);
if(!$tnconn) {
echo "SERVER_UNAVAILABLE";
die();
} else {
$data = $p_ip." ".$p_name." ".$p_token;
fputs($tnconn, $data);
while (true) {
if(($telnet_response = fgets($tnconn)) == false) {
break;
}
}
}
echo $telnet_response;
It seems like the fputs() statement is executed after the loop even tho it should happen before it starts - else the java application couldn't get the data that is passed to the php script, but it is needed to authenticate users.
Right after the data was received, the telnet server would output the String to indicate whether authentication was successful or not.
I tried temporarily removing the loop and the data was successfully passed with fputs() so I assume php waits until the whole script is finished and then executes the function.
How can I make it send the data before the loop?
Thank you in advance.
The issue is probably that you need to send a \n at the end of your data string so the telnet server knows you have sent a full sequence of data. Otherwise it is most likely sitting there waiting for more input.
Try:
$data = $p_ip." ".$p_name." ".$p_token . "\n";

PHP - kill exec on client disconnect

I have very primitive web front-end for my C++ application. Client (web browser) is enters php site and fills form with parameters. Than (after post submit) php calls exec and application does its work.
Application can work longer than minute and requires pretty large amount of RAM.
Is there any possibility to detect disconnecting from client (for example closure of tab in web browser). I want to do this, because after disconnecting client will not be able to see result of computations, so I can kill application and free some RAM on server.
Thanks for any help or suggestions.
As long as the C++ program produces output while running, rather than generating all the output just prior to termination, use passthru() instead of exec().
This causes PHP to flush the output to the client as the content is produced, which allows PHP to detect when clients disconnect. PHP will terminate when the client disconnects and kill the child process immediately (as long as ignore_user_abort() is not set).
Example:
<?php
function exec_unix_bg ($cmd) {
// Executes $cmd in the background and returns the PID as an integer
return (int) exec("$cmd > /dev/null 2>&1 & echo $!");
}
function pid_exists ($pid) {
// Checks whether a process with ID $pid is running
// There is probably a better way to do this
return (bool) trim(exec("ps | grep \"^$pid \""));
}
$cmd = "/path/to/your/cpp arg_1 arg_2 arg_n";
// Start the C++ program
$pid = exec_unix_bg($cmd);
// Ignore user aborts to allow us to dispatch a signal to the child
ignore_user_abort(1);
// Loop until the program completes
while (pid_exists($pid)) {
// Push some harmless data to the client
echo " ";
flush();
// Check whether the client has disconnected
if (connection_aborted()) {
posix_kill($pid, SIGTERM); // Or SIGKILL, or whatever
exit;
}
// Could be done better? Only here to prevent runaway CPU
sleep(1);
}
// The process has finished. Do your thang here.
To collect the program's output, redirect the output to a file instead of /dev/null. I suspect you will need pcntl installed as well as posix for this, since the PHP manual indicates the SIGxxx constants are defined by the pcntl extension - although I have never had one installed without the other so I'm not sure either way.

How to fire EventSource SSE events?

I've recently discovered EventSource, YUI3 has a Gallery module to normalise and fallback behaviour, that's what I've chosen to go with in my example as I use that framework already.
So I've searched about quite a bit, read many blogs, posts and examples, all of which show pretty much the same thing: How to set up basic SSE events. I now have 6 examples of open/message/error/close events firing.
What I don't have (what I'd hoped this link was going to give me) is an example of how to fire SSE events which are more useful to my application, I'm trying one called 'update'.
Here's is my basic test page: http://codefinger.co.nz/public/yui/eventsource/test.php (it might as well be an html file, there's no php code in here yet)
And here's the 'message.php' in the EventSource constructor:
<?php
header('Content-Type: text/event-stream');
header('Cache-Control: no-cache'); // recommended to prevent caching of event data.
/**
* Constructs the SSE data format and flushes that data to the client.
*
* #param string $id Timestamp/id of this connection.
* #param string $msg Line of text that should be transmitted.
*/
function sendMsg($id, $msg) {
echo "id: $id" . PHP_EOL;
echo "data: $msg" . PHP_EOL;
echo PHP_EOL;
ob_flush();
flush();
}
while(true) {
$serverTime = time();
sendMsg($serverTime, 'server time: ' . date("h:i:s", time()));
sleep(10);
}
// I was hoping calling this file with a param might allow me to fire an event,
// which it does dutifully, but no browsers register the 'data : update' - though
// I do see the response in Firebug.
if( $_REQUEST['cmd'] ){
sendMsg($serverTime, $_REQUEST['cmd'] );
}
?>
From the live example above, you can see that I've tried to use YUI's io module to send a request, with param, to fire my 'update' event when I click the 'update' button. It seems to work, as you can see in Firebug's Net panel, but my event isn't handled (I realise the script above will run that loop again, I just want to get my event handled in connected browsers, then I'll remove/cleanup).
Am I doing this part wrong? Or is there something more fundamental I'm doing wrong? I'm trying to push events in response to my UI's state changing.
This SO question seemed to come close, #tomfumb commented that his next question was going to be "how to send new events to the client after the initial connection is made - now I see that the PHP just has to never stop executing." But surely I'd only send events as they happen... and not continuously...
there are several issues in your approach:
The server-side code that reads the cmd parameter is unreachable because of the infinite loop that sends event data to the client.
You are trying to send an event from the client to the server. It is in the specification name - Server-Sent Events - the server is the sender and the client is the receiver of events. You have options here:
Use the appropriate specification for the job called Web Sockets which is a two-way communication API
Write the logic that makes the desired type of communication possible
If you choose to stay with the SSE API I see two possible scenarios
Reuse the same Event Source connection and store a pool of connections on the server. When the user sends subsequent XMLHttpRequest with the update command, get the EventSource connection from the pool, that was made by this visitor, and send response with it that specifies your custom event type, the default type is message. It is important to avoid entering in the infinite loop that would make another EventSource connection to the client, but the client does not handle it because he made the request with XMLHttpRequest and not with EventSource.
Make all requests with EventSource. Before making a new EventSource request, close the previous one - you can do this from the client or from the server. On the server check the parameters and then send data to client.
Also you can use XMLHttpRequest with (long) polling and thus avoiding the need of using EventSource. Because of the simplicity of your example I can't see a reason to mix the two type of requests.

ZMQ PUSH/PULL model: Lost messages when a node PUSHes messages and exits

I have a requirement that a PHP web application write messages to a non-blocking queue and other process(es) dequeue them. My current design is PHP app create a ZMQ.PUSH socket, do a connect to the destination address and send the message. While at the destination, a process (Java) create a ZMQ.PULL socket, do a bind on the same address and receive the message. However, when the dequeuer process is down (or not started), the messages that the PHP app sent during that time are lost (not delivered to the dequeuer when the process starts again on the same address). Is this an issue with the PHP application creating a new ZMQ.Context everytime?
As an example, I created another Java process to write messages to the queue.
public static void main(String[] args) {
ZMQ.Context context = ZMQ.context(1);
ZMQ.Socket socket = context.socket(ZMQ.PUSH);
socket.connect("tcp://localhost:5557");
for(int i = 0; i < 10; i++) {
socket.send(("Sending : " + message).getBytes(), 0);
}
}
This has the same problem of lost messages if the dequeuing process does not start by the time the above processes finishes.
But adding a while(true) {} to the end of the above method body does not result in any lost messages - all the messages are delivered when the dequeuer starts. So am I correct in the assumption that the ZMQ.Context object being garbage collected causes the problem here? If yes, then how to solve this problem in a PHP web application? Since I cannot persist the ZMQ.Context object.
You are looking for so-called durable messages.
Look at the zmq_setsockopt in particular the ZMQ_IDENTITY option as a first step in your solution.
Make sure you read the sections in the manual about High-Water Marks in order to not get too far behind if the reader is offline for a while.
Good Luck

Can a PHP script be scheduled to run at a specific time or after a specific amount of time has expired?

I am writing a social cloud game for Android and using PHP on the server. Almost all aspects of the game will be user or user-device driven, so most of the time the device will send a request to the server and the server will, in turn, send a response to the device. Sometimes the server will also send out push messages to the devices, but generally in response to one user's device contacting the server.
There is one special case, however, where a user can set a "timer" and, after the given time has elapsed, the server needs to send the push messages to all of the devices. One way to do this would be to keep the timer local to the user's device and, once it goes off, send the signal to the server to send the push messages. However, there were several reasons why I did not want to do it this way. For instance, if the user decides not to play anymore or loses the game, the timer should technically remain in play.
I looked around for a method in PHP that would allow me to do something like this, but all I came up with were alarms, which are not what I need. I also thought of cron jobs and, indeed, they have been recommended for similar situations on this and other forums, but since this is not a recurring event but, rather, a one time event to take place at an arbitrary point in time, I did not know that a cron job is what I want either.
My current best solution involves a cron job that runs once a second and checks to see if one of these events is to occur in the next second and, if so, sends out the push messages. Is this the proper way to handle this situation, or is there a better tool out there that I just haven't found yet?
cron is great for scripts run on a regular basis, but if you want a one-off (or two-off) script to run at a particular time you would use the unix 'at' command, and you can do it directly from php using code like this:
/****
* Schedule a command using the AT command
*
* To do this you need to ensure that the www-data user is allowed to
* use the 'at' command - check this in /etc/at.deny
*
*
* EXAMPLE USAGE ::
*
* scriptat( '/usr/bin/command-to-execute', 'time-to-run');
* The time-to-run shoud be in this format: strftime("%Y%m%d%H%M", $unixtime)
*
**/
function scriptat( $cmd = null, $time = null ) {
// Both parameters are required
if (!$cmd) {
error_log("******* ScriptAt: cmd not specified");
return false;
}
if (!$time) {
error_log("******* ScriptAt: time not specified");
return false;
}
// We need to locate php (executable)
if (!file_exists("/usr/bin/php")) {
error_log("~ ScriptAt: Could not locate /usr/bin/php");
return false;
}
$fullcmd = "/usr/bin/php -f $cmd";
$r = popen("/usr/bin/at $time", "w");
if (!$r) {
error_log("~ ScriptAt: unable to open pipe for AT command");
return false;
}
fwrite($r, $fullcmd);
pclose($r);
error_log("~ ScriptAt: cmd=${cmd} time=${time}");
return true;
}
soloution 1 :
your php file can include a ultimate loop
$con = true;
while($con)
{
//do sample operation
if($end)
$con = false;
else
sleep(5); // 5 seconds for example
}
soloution 2 :
use cron jobs -- Depend on yout CP you can follow the instruction and call your php program at the specific times
limit : in cron job the minimum time between two calling is 1 minute
soloution 3 :
use a shell script and call your php program when ever you want
You can make PHP sleep for a certain amount of time - it will then resume the code afterwards but this is seriously not recommended because when a script sleeps it still uses up processor resources, and if you had multiple scripts sleeping for long periods of time it would put impossible load on your server.
The only other option that I know of is Cron. As #Pete says, you can manage Cron jobs from within PHP, e.g.:
http://net.tutsplus.com/tutorials/php/managing-cron-jobs-with-php-2/
This is going to involve a fair bit of coding, but I think it is your best options.
Another option is to have your user's browser call a PHP function using an Ajax request and JavaScript's setTimeout as suggested by the accepted answer in this question:
how to call a function in PHP after 10 seconds of the page load (Not using HTML)

Categories