PHP fifo queue system over multiple server - php

I'm trying to build a queue system with a FIFO file. This is actually working prety fine on my local development machine. But when I want to deploy to production, I'm running into the issue that my WEB server and CLI server are two seperate servers. And so the fifo file is not working when the listener is started on the CLI server and something is added from the WEB server.
So I need to start the listener from the WEB SAPI, but how can I do that? And also, if it crashes, it needs to be restarted again.
I know how to do this all from just on server, but no idea how to do so on multiple server setup.
The code:
<?php
//queue-listener.php
function process($job) {
sleep(1); //. make it look like we did work.
return;
}
$queue = array();
//////// setup our named pipe ////////
$pipefile = '/storage/queueserver';
umask(0);
if(!file_exists($pipefile)){
if(!posix_mkfifo($pipefile,0666)) {
die('unable to create named pipe');
}
}
$pipe = fopen($pipefile,'r+');
if(!$pipe) die('unable to open the named pipe');
stream_set_blocking($pipe,false);
//////// process the queue ////////
while(1) {
while($input = trim(fgets($pipe))) {
stream_set_blocking($pipe,false);
$queue[] = $input;
}
$job = current($queue);
$jobkey = key($queue);
if($job) {
echo 'processing job ', $job, PHP_EOL;
process($job);
next($queue);
unset($job,$queue[$jobkey]);
} else {
echo 'no jobs to do - waiting...', PHP_EOL;
stream_set_blocking($pipe,true);
}
}
Code for adding something to queue
$pipefile = '/storage/queueserver';
$fhp = fopen($pipefile, 'r+') or die ("can't open file $pipefile");
fwrite($fhp, "GenerateLabel|". date('H:i:s')."\n");

Related

read/write websocket server in php is not wotking

I am running an apache server on a linux machine.
Im trying to create a website which has a page that needs asynchronous updates and two-way communication with the server.
Im trying to make this simple file which fires up as an intermediate server for each session the code looks something like this:
<?php
$server = stream_socket_server("tcp://localhost:8090", $errno, $errstr);
if(!$server){
echo "$errstr ($errno)<br>";
die();
}
$users = [];
//var_dump($server);
while(true){
$client = stream_socket_accept($server); // new connection
echo "new connection<br>";
array_push($users, $client);
$pid = pcntl_fork();
if($pid){
while(true){
$buff='';
while(!str_ends_with($buff, "\n\r")){
$buff = $buff . fread($client, 1024);
}
//$len = strlen($buff);
foreach($users as $u){ // broadcast
fwrite($u, $buff);
}
if($buff == 'END'){
exit(1);
}
echo "routed<br>";
}
}
}
?>
This is supposed to be a basic echo broadcast. it keeps track of new connections and waits for any client to send a message and sends it back to everyone.
the basic idea was that it would listen for a new connection and use fork to handle the read and write processes.
However I've recently learned that fork() does not work on apache and so my question is how can i make this work.

Combining php stream sockets with eventSource

I am trying to learn about sockets in PHP and lots of reading has brought me down the route of stream_socket_server().
I got a basic chat working between two terminals in linux using code similar to what is below and was hoping my next step would be to build a chat or a notification system on the web.
What I expected to happen:
The event listener in eventSource.html would listen for an event in the while loop and output the message received from a linux terminal that was running server.php
What is happening:
Everything from the point of view of eventSource.html is working as it should. So if I was to take away the entire purpose of this and just replace the message with a standard string Hello World then this succesfully outputs <li>message:{Hello World}</li> every second.
However once I try and read data from the terminal nothing is appearing except <li>message: {}</li> every second. It is worth noting that when I run server.php it waits for the client, and when I then run eventSource.html it sucessfully connects.
I am misunderstanding how this works? I assumed that every second in the while loop it would look out for data in that stream.
Or am I going down a completely wrong road in terms of learning sockets.
server.php (I load this from the terminal in linux)
<?php
$PORT = 20226; //chat port
$ADDRESS = "localhost"; //adress
$ssock; //server socket
$csock; //chat socket
$uin; //user input file descriptor
$ssock = stream_socket_server("tcp://$ADDRESS:$PORT"); //creating the server sock
echo "Waiting for client...\n";
$csock = stream_socket_accept($ssock); //waiting for the client to connect
//$csock will be used as the chat socket
echo "Connection established\n";
$uin = fopen("php://stdin", "r"); //opening a standart input file stream
$conOpen = true; //we run the read loop until other side closes connection
while($conOpen) { //the read loop
$r = array($csock, $uin); //file streams to select from
$w = NULL; //no streams to write to
$e = NULL; //no special stuff handling
$t = NULL; //no timeout for waiting
if(0 < stream_select($r, $w, $e, $t)) { //if select didn't throw an error
foreach($r as $i => $fd) { //checking every socket in list to see who's ready
if($fd == $uin) { //the stdin is ready for reading
$text = fgets($uin);
fwrite($csock, $text);
}
else { //the socket is ready for reading
$text = fgets($csock);
if($text == "") { //a 0 length string is read -> connection closed
echo "Connection closed by peer\n";
$conOpen = false;
fclose($csock);
break;
}
echo "[Client says] " .$text;
}
}
}
}
client.php gets loaded from eventSource.html below
<?php
date_default_timezone_set("America/New_York");
header("Content-Type: text/event-stream\n\n");
$PORT = 20226; //chat port
$ADDRESS = "localhost"; //adress
$sock = stream_socket_client("tcp://$ADDRESS:$PORT");
$uin = fopen("php://stdin", "r");
while (1) {
$text = fgets($uin);
echo 'data: {'.$text.'}';
echo "\n\n";
ob_end_flush();
flush();
sleep(1);
}
eventSource.html
<script>
var evtSource = new EventSource("client.php");
evtSource.onmessage = function(e) {
var newElement = document.createElement("li");
newElement.innerHTML = "message: " + e.data;
var div = document.getElementById('events');
div.appendChild(newElement);
}
</script>
<div id="events">
</div>

knowing whether a php script is still running

I've got a bunch of php scripts scheduled to run every couple of minutes in cron on a CentOS machine. I would like for every script to self check if the previous instance of it is still running when it starts and stop if it is.
I do this to manage tasks and making sure they only run one at a time
public function startTask($taskname)
{
$running = "running";
$lockfile = $taskname . "RUNNING";
file_put_contents($lockfile, $running);
}
public function endTask($taskname)
{
$lockfile = $taskname . "RUNNING";
unlink($lockfile);
}
public function isTaskRunning($taskname)
{
$lockfile = $taskname . "RUNNING";
if (file_exists($lockfile))
{
return true;
}
}
You call startTask('name') when you start the task and then endTask('name') when you finish. And on the first line of the task you use
if (isTaskRunning('name')) {
die('already running');
}
Put these in a config class or something thats included in all task files and your away
Use a lock file:
<?php
$lockfile = "/tmp/lock.txt";
$fp = fopen($lockfile, "r+");
if (flock($fp, LOCK_EX)) { // acquire an exclusive lock
ftruncate($fp, 0); // truncate file
fwrite($fp, sprintf("Started: %s\nPID: %s", date(), getmypid()));
// perform your tasks here.
fflush($fp); // flush output before releasing the lock
flock($fp, LOCK_UN); // release the lock
} else {
echo "Couldn't get the lock!\nCheck $lockfile for more info.";
}
fclose($fp);
Alternatively, if you are using a database you can do a database Named Lock like this:
<?php
$process = "myProcess";
$sql = mysql_query("select get_lock('$process', 0)");
$got_lock = (bool)mysql_fetch_array($sql)[0];
// If process is already running exit
if(!$got_lock){
echo "Process running";
exit;
}
// Run my process
for($i=0;$i<100000000;$i++){
echo $i;
}
// Release the lock
mysql_query("select release_lock('$process')");
This form of locking is called a named lock, it doesn't "Lock" the database, it just creates a "Named Lock" and when you call it it checks to see if the name exists. It is nothing like a table lock or row lock It was built into mysql for other applications, such as this.
You can have as many locks as you need, and they are automatically released once the application ends, such as your client disconnecting from mysql: process finishes, php breaks/crashes, mysql crashes (not 100% sure on this one), etc.
You can add to your PHP script a simple echo at the end (maybe with date()) which will be visible in your cron logs and shows you if the script reaches the end (and then finish its task)

php socket occasionally unresponsive and no errors thrown

I've written a database application using MySQL and PHP on the server side, and Flex on the client side. I use a php socket to have it automatically update all clients whenever changes are made to the database.
The whole system works swimmingly, but every now and then the socket seems to stop responding. The strange thing is that the connection is still good – any changes a client performs are implemented, but the socket doesn't broadcast the message. The socket file isn't throwing any errors (though when I run error_log from the socket those messages appear). Memory use of the socket doesn't change on the server, and no disconnect signal is sent. Stranger still, eventually the socket starts working again, after about half an hour or so. If I restart the socket that also solves the problem.
I'm working on a hacky solution allowing the client to restart the socket if it becomes unresponsive, but that's unsatisfying and open to mistakes. What I'd really like is to learn why this might be happening. Does the socket somehow get "saturated" after a certain number of connections? Should I be doing something to clean up the socket server? I've tried three different physical servers (one local and two online) and the same thing happens, so it's definitely me.
I feel like there's something basic that I'm doing wrong. Here's the code I'm using for the socket server (it's a slightly modified version of socket written by Raymond Fain on kirupa.com, so I've left his original comment at the top):
#!/usr/bin/php -q
<?php
/*
Raymond Fain
Used for PHP5 Sockets with Flash 8 Tutorial for Kirupa.com
For any questions or concerns, email me at ray#obi-graphics.com
or simply visit the site, www.php.net, to see if you can find an answer.
*/
//ini_set('display_errors',1);
//ini_set('display_startup_errors',1);
error_reporting(E_ALL);
ini_set('error_log', 'socket_errors.log');
ini_set('log_errors', 'On');
ini_set('display_errors', 'Off');
set_time_limit(0);
ob_implicit_flush();
error_log('testing');
$address = 'xxx.xxx.xx.xx';
$port = xxxxx;
function send_Message($allclient, $socket, $buf)
{
$buf = str_replace("\0","",$buf);
//echo "<mbFeed>$buf</mbFeed>\n\0";
foreach($allclient as $client)
{
socket_write($client, "<mbFeed>$buf</mbFeed>\n\0");
}
}
echo "connecting...
";
//---- Start Socket creation for PHP 5 Socket Server -------------------------------------
if (($master = socket_create(AF_INET, SOCK_STREAM, SOL_TCP)) < 0)
{
echo "socket_create() failed, reason: " . socket_strerror($master) . "\n";
}
socket_set_option($master, SOL_SOCKET,SO_REUSEADDR, 1);
if (($ret = socket_bind($master, $address, $port)) < 0)
{
echo "socket_bind() failed, reason: " . socket_strerror($ret) . "\n";
}
echo 'socket bind successfull.
';
if (($ret = socket_listen($master, 5)) < 0)
{
echo "socket_listen() failed, reason: " . socket_strerror($ret) . "\n";
}
$read_sockets = array($master);
echo "connected.";
//---- Create Persistent Loop to continuously handle incoming socket messages ---------------------
while (true)
{
$changed_sockets = $read_sockets;
$num_changed_sockets = socket_select($changed_sockets, $write = NULL, $except = NULL, NULL);
foreach($changed_sockets as $key => $socket)
{
if ($socket == $master)
{
if (($client = socket_accept($master)) < 0)
{
echo "socket_accept() failed: reason: " . socket_strerror($msgsock) . "\n";
continue;
}
else
{
array_push($read_sockets, $client);
}
}
else
{
$bytes = socket_recv($socket, $buffer, 8192, 0);
if ($bytes == 0)
{
unset($read_sockets[$key]);
unset($changed_sockets[$key]);
socket_close($socket);
}
else
{
$allclients = $read_sockets;
array_shift($allclients);
//any messages starting with ::: are not to be broadcast, and may be used for other things. This message
//usually comes from the client.
if (substr($buffer, 0, 3) == ":::") handleSpecial(substr($buffer, 3));
else
{
//otherwise the message comes from a php file that will be closed, so the socket needs to be closed.
unset($read_sockets[$key]);
unset($changed_sockets[$key]);
socket_close($socket);
send_Message($allclients, $socket, $buffer);
}
}
}
}
}
function handleSpecial($message)
{
error_log($message);
}
?>
As the sockets in use seem to be blocking the call to socket_recv() might not return until the amount of data requested was read. And with this does not handle any other reading sockets, including the accecpting socket.
To get around this use socket_set_nonblock() to make the sockets unblocking. Please note that a call to socket_recv() on a non-blocking socket might return having read less bytes than requested, and therefore the amount of data read shall be tracked for each socket.

PHP Socket and proc_open

I am trying to create a inetd-like service for Windows in PHP for future use with my other application.
So all I can think of is to use Steam Server and proc_open to pipe the stream directly to the process (like inetd). Because on Windows there is no pcntl_fork(), and PHP doesn't support threading.
So far, here is my code. The inetdtest program is a simple program with single printf (written in C). But the problem is that when I connected to my server (via netcat), I got no response message.
<?php
define ('SERVICE_COMMAND', 'inetdtest');
define ('SERVICE_PORT', 35123);
function main() {
echo "Simple inetd starting...\n";
$socket = stream_socket_server('tcp://0.0.0.0:' . SERVICE_PORT, $errno, $errstr, STREAM_SERVER_BIND|STREAM_SERVER_LISTEN);
if ($socket === false) {
echo "Can't bind to service port.\n";
echo "[$errno] $errstr";
die(1);
}
$processes = array();
while (true) {
$current = #stream_socket_accept($socket, 5, $host);
if ($current !== false) {
echo 'Incomming connection from client ' . $host . "\n";
echo "Lunching child process... ";
$io = array(
0 => $current,
1 => $current,
2 => array('file', 'stderr.log', 'a')
);
$proc = proc_open(SERVICE_COMMAND, $io, $pipes, NULL, NULL, array('bypass_shell'));
$status = proc_get_status($proc);
echo " DONE! PID : {$status['pid']}\n";
$processes[] = array($current, $proc);
}
foreach ($processes as $k=>$v) {
$status = proc_get_status($v[1]);
if (false === $status['running']) {
echo "Finalizing process {$status['pid']}... ";
fflush($v[0]);
fclose($v[0]);
proc_close($v[1]);
unset($processes[$k]);
echo "DONE!\n";
}
}
}
}
main();
The code justs works as it stands here (using cat as program and on linux), so the problem lies somewhere in the windows side of things.
For one thing, the option you are passing, to bypass the shell, should be given as
array('bypass_shell'=>true)
This may fix things already. The tricky part with these things, is that you're passing a socket fd to a process, which may or may not be expected to handle that properly. I don't know how these things are done in windows, but cutting cmd out of the equation can only help.
If it still doesn't work, you should create a loop which waits for data (either from network or child processes) and sends data from the network socket to the process pipe, and vice versa.

Categories