How to pass some data to a running thread in PHP? - php

So let's say I create a thread and detach it from the main process, and start it.
So, after the thread is detached, how is it possible to pass some chunks of data like strings, or ints to the already running thread?
Edit
What I am basically doing is trying to implement the WS protocol:
<?php
// Pseudo-Code
class LongRunningThread extends \Thread {
private $handshakeReq;
public function __construct(Request $handshakeRequest) {
$this->handshakeReq = $handshakeRequest;
}
public function run() {
// Do handshake
// But do not exit, because after the handshake is done the socket connection needs to be maintained.
// Probably some trigger which notifies that a new message is here and the message arrives <automagically>
if(trigger) {
$message = $message;
$this->onNewWsMessage($message);
}
}
public function onNewWsMessage(string $rawMessage) {
// Process the message...
}
}
$stream = stream_socket_server(sprintf("tcp://%s:%d",
"localhost",
1337
), $errno, $errmsg);
// Boiler plate, and connection acceptance (blah blah blah)
// $client is the the accepted connection
$message = fread($client, 4096);
// Cannot pass the $client in here because the instability of resources with threads
// as passing them here, apparently converts them to <bool> false
$longRunningThread = new \LongRunningThread($message);
$longRunningThread->start() && $longRunningThread->join();
I found various answers related to passing data to a running thread, but I couldn't find any specifically for PHP.
I am using pthreads

The actual question is quite vague.
What you want to do falls to my understanding under the IPC (interprocess communication) and can be implemented with a couple of ways (to my knowledge the most common one is :http://php.net/manual/en/function.stream-socket-pair.php).
I would suggest though that you could use some kind of queueing and polling system like rabbitmq to pass around messages.It will provide some overhead but its a well known and highly used solution

Related

How to run bin/console messenger:consume command out of Symfony project?

I use Messenger Component in a non-Symfony project and Doctrine as a DSN transport. Now I want to test my code and consume the messages on my local machine, but I don't know how to run the messenger command in the console.
I tried to use Symfony\Component\Console\Application and register the \Symfony\Component\Messenger\Command\ConsumeMessagesCommand command in the console but there are many nested dependencies.
Do you have any idea?
We actually do this in many projects, even WordPress CLI tools, and we use this library to do it along with this for the transport. It doesn't require Symfony and can work with most queue systems that follow the general standard.
The general idea is that you want something (probably a singleton) to return an instance of Interop\Queue\Context, and here's what we use:
function createContext(): \Interop\Queue\Context
{
$factory = new \Enqueue\Dbal\DbalConnectionFactory(
sprintf(
'mysql://%1$s:%2$s#%3$s/%4$s',
DB_USER,
DB_PASSWORD,
DB_HOST,
DB_NAME
)
);
$context = $factory->createContext();
$context->createDataBaseTable();
return $context;
}
You'll also want something to handle each message, and you'll want to pass the message and consumer to it:
function handleMessage($message, $consumer)
{
// Business logic here
if($business_logic_failed) {
$context = createContext();
$failed_queue = $context->createQueue('FAILED_QUEUE_HERE');
$context->createProducer()->send($failed_queue, $message);
} else {
$consumer->acknowledge($message);
}
}
Then to use it:
$context = createContext();
$queue = $context->createQueue('QUEUE_NAME_HERE');
$consumer = $context->createConsumer($queue);
// This can be an infinite loop, or a loop for 10 messages and exit, whatever your logic
while(true) {
// This command will block unless you pass a timeout, so no sleep is needed
$message = $consumer->receive(/* optional timeout here */);
handleMessage($message, $consumer);
// Do whatever you want with message
}
Sprinkle a lot of try/catch around that, too, and make sure that no matter what you acknowledge or fail the message in some way.

Amphp : Run many async loops with same connection (eventstore client)

I'm using eventstore client which uses amphp. I need inside my application to reuse the connection in many parts.
So I created a connection provider:
public function getConnection(): EventStoreConnection
{
if ($this->connection) {
return $this->connection;
}
$this->connection = $this->createConnection();
wait($this->connection->connectAsync());
return $this->connection;
}
And then I use this connection at many places:
\Amp\Loop::run(function () use ($eventStoreEvents, $streamName) {
$connection = $this->connectionProvider->getConnection();
// Creation of an event stream
yield $connection->appendToStreamAsync($streamName, ExpectedVersion::ANY, $eventStoreEvents);
// sleep(10); // This sleep does not work, code continue like nothing happend
});
\Amp\Loop::run(function () use ($streamName, $aggregateFqcn, &$aggregateRoot) {
$start = 0;
$count = \Prooph\EventStore\Internal\Consts::MAX_READ_SIZE;
$connection = $this->connectionProvider->getConnection();
do {
$events = [];
/** #var StreamEventsSlice $streamEventsSlice */
$streamEventsSlice = yield $connection
->readStreamEventsForwardAsync(
$streamName,
$start,
$count,
true
);
if (!$streamEventsSlice->status()->equals(SliceReadStatus::success())) {
dump($streamEventsSlice); // Event stream does not exist
// Error here: the event stream doesn't exist at this point.
throw new RuntimeGangxception('Impossible to generate the aggregate');
}
} while (! $streamEventsSlice->isEndOfStream());
});
The problem: it seems that the first request is not over but the second loop starts already. The sleep uncommented doesn't have any effect!
But the event stream is finally created with the related events inside, so the first request worked.
If I start a connection then close then start a new one, it works. But it's slow, due to handshake overhead on each new connection.
I tried a similar example with the WebSocket library of Amphp and it worked. Do you see anything wrong?
Here is my test with websocket that worked:
$connection = \Amp\Promise\wait(connect('ws://localhost:8080'));
Amp\Loop::run(function () use ($connection) {
/** #var Connection $connection */
yield $connection->send("Hello...");
sleep(10); // This sleep works!
});
Amp\Loop::run(function () use ($connection) {
/** #var Connection $connection */
yield $connection->send("... World !");
});
$connection->close();
What you are trying to do makes no sense. You should read amphp's documenation.
Amp uses a global accessor for the event loop as there’s only one event loop for each application. It doesn’t make sense to have two loops running at the same time, as they would just have to schedule each other in a busy waiting manner to operate correctly.
That said, there is literally NO SECOND LOOP.
Prooph eventstore library is based on amphp but doesn't follow all principles: you can't wait for the connection to be ready. It will be even worse if you try to use it at scale, so don't try to wait for the promise is complete.
As an alternative, you can set a promise for later and check if the connection is null. That's what actually does the library internally to process further steps.
On my side, I decided to stop using this library. But as an alternative you can use the library that uses the HTTP client, it's also from the prooph team.

Using reactive PHP in a blocking application

I'm currently working on a PHP application that will be using some websocket connections to talk to another service.
To talk to this websocket service, we are using Ratchet - which is a PHP library based on react PHP.
This piece of code needs to send and respond to a couple of requests, and after that, should return the information to the "main thread".
Example flow:
HTTP request -> controller -> Starts a service which opens a websocket client -> websocket client is talking to server -> once its done it should return the outcome to the controller code -> controller outputs to user
The issue I'm having is that I'm not familiar with Reactive PHP and am not sure how to handle this.
I've tried;
$service = new WebsocketService();
$startTimer = time();
$service->getList(44);
while($service->getResponse() == null) {
usleep(500);
if (time() > $startTimer + 10) {
continue; //Timeout on 10 seconds
}
}
var_dump($service->getResponse());
The service code would set its "response" variable to something other than null once its done. This obviously fails, because the sleep method is blocking the thread. Also without, it seems like the while loop is blocking I/O and the reactive code fails.
A solution would be to open up a new thread and run the websocket code there, but I wouldn't be happy with that.
I feel like I need to implement some sort of "watcher" around the websocket process, but I'm not sure how to do that.
Our Websocket service client code looks like this;
private $response = null;
/**
* #return null|object
*/
public function getResponse() {
return $this->response;
}
public function getList($accountId) {
$this->response = null;
\Ratchet\Client\connect('ws://192.168.56.1:8080')->then(function(\Ratchet\Client\WebSocket $conn) use ($accountId) {
$login = new \stdClass();
$login->action = 'login';
$conn->on('message', function($msg) use ($conn, $login, $accountId) {
try {
$response = json_decode($msg);
if ($response->result_id == 100) {
//Succesfully logged in to websocket server
//Do our request now.
$message = new \stdClass();
$message->target = 'test';
$conn->send(json_encode($message));
}
if (isset($response->reply) && $response->reply == 'list') {
$this->response = $response; //This is the content I need returned in the controller
$conn->close(); //Dont need it anymore
}
} catch (\Exception $e) {
echo 'response exception!';
//Do nothing for now
}
});
$conn->send(json_encode($login));
}, function ($e) {
echo "Could not connect: {$e->getMessage()}\n";
});
}
Running the code like this also does not work;
$service = new WebsocketService();
$service->getList(44);
echo 'Test';
var_dump($service->getResponse());
because the "test" echo comes before I even get a response from the websocket server.
Please, enlighten me! I'm not sure what to search for.
PHP and websockets still seem to be a bit experimental. Nevertheless I have found a great tutorial on medium.com, written by Adam Winnipass which should be really helpful for solving your problem: https://medium.com/#winni4eva/php-websockets-with-ratchet-5e76bacd7548
The only difference is that they are implementing their websocket client with JavaScript instead of PHP. But in the end there should not be much of a difference, because as soon as we have opened the Websocket connection of each end both applications have to send and also wait to receive notifications - this is how they illustrate it:
Seems like one possibility to create a successful Websocket connection is to extend the MessageComponentInterface
use Ratchet\MessageComponentInterface;
which also requires
use Ratchet\ConnectionInterface;
The message component interface defines the following methods:
onOpen
onMessage
onClose
onError
And I think this is how the Ratchet library is implementing it. This is how they are finally starting their server:
use Ratchet\Server\IoServer;
use MyApp\MyCustomMessageComponentInterface;
use Ratchet\Http\HttpServer;
use Ratchet\WebSocket\WsServer;
require dirname(__DIR__) . '/vendor/autoload.php';
$server = IoServer::factory(
new HttpServer(
new WsServer(
new MyCustomMessageComponentInterface()
)
),
8080
);
$server->run();
With this architecture you already can receive (onMessage) and sending is also possible with the send() method.
I can not solve the exact problem with your existing code. But I guess if you are using the pre-built classes and interfaces of the library as intended (and demonstrated here) you should be able to achieve what you want by adding your code to the corresponding methods.
More information and examples can be found in the docs:
http://socketo.me/docs/server
http://socketo.me/api/namespace-Ratchet.html
Are you extending class with WsServer, This might be issue, if you are getting fatal errors. I am not sure whether you are getting fatal errors or warnings. Also i notice the public function onOpen() opens a connection. Please try referring this document http://socketo.me/api/class-Ratchet.WebSocket.WsServer.html might be useful.

Async PHP requests

So, I want to create an asynchronous web service in PHP. Why? Because I've a nice async front-end, but Chrome will block my requests if I have more than 6 active TCP connections. Of course I have read some similar questions like:
Async requests in PHP
Multiple PHP Requests Crashing Page
but these don't cover my question.
I installed pthreads with the intention that I would be able to make multiple requests in different threads so that my PHP wasn't blocking other requests(in my situation I start eg. a long process and I want to be able to poll if the process is still busy or not).
PHPReact seems to be a nice library(non-blocking I/O, async) but this won't work either(still sync).
Am I missing something or is this nowadays still not possible in PHP?
class Example{
private $url;
function __construct($url){
$this->url = $url;
echo 'pooooof request to ' . $this->url . ' sent <br />';
$request = new Request($this->url);
$request->start();
}
}
class Request extends Thread{
private $url;
function __construct($url){
$this->url = $url;
}
function run(){
// execute curl, multi_curl, file_get_contents but every request is sync
}
}
new Example('https://gtmetrix.com/why-is-my-page-slow.html');
new Example('http://php.net/manual/en/function.file-get-contents.php');
new Example('https://www.google.nl/webhp?sourceid=chrome-instant&ion=1&espv=2&ie=UTF-8#q=php%20file%20get%20contents');
The ideal situation would be to make use of callbacks.
ps. I have seen some servers(like Node.js) that are providing this functionality, but I prefer a native approach. When this is not possible I'm really thinking of switching to Python, Java, Scala or some other language that supports async.
I can't really make sense of what you are doing ...
Asynchronous and Parallel are not interchangeable words.
Threads at the frontend of a web application don't make sense.
You don't need threads to make many I/O bound tasks concurrent; That is what non-blocking I/O is for (asynchronous concurrency).
Parallel concurrency seems like overkill here.
Regardless, the reason your requests appear synchronous is the way this constructor is written:
function __construct($url){
$this->url = $url;
echo 'pooooof request to ' . $this->url . ' sent <br />';
$request = new Request($this->url);
$request->start();
}
The Request thread will be joined before control is returned to the caller of __construct (new) because the variable goes out of scope, and so is destroyed (joining is part of destruction).

PHP parallel/asynchronous SSH connections

I'm trying to open multiple connections (various devices) to run a command and get the output.
The problem is that i have to run them "all at once"/parallel.
If i wait for one result and then to run the other one it takes way too long
and with a large number of devices that can go very bad.
I'm also using curl which I know that there is curl_multi and I was wondering if there was something similar with SSH for php.
I'm using Net_SSH2 for now.
You'll need to use two PHP libraries: https://robo.li/tasks/Remote/#ssh and https://github.com/cheprasov/php-parallel. Your class method might be something similar to the example below:
function runParallelSSH() {
$parallel = new Parallel(new ApcuStorage());
foreach ($credentials as $user => $host) {
$connection = sprintf('%s#%s', $user, $host);
$connections[] = $connection;
$Parallel->run($connection, function() {
$gitTask = $this->taskGitStack()
->checkout('master')
->pull();
});
}
$results = $parallel->wait($connections);
}
Without using thirdparties like curl_multi you have to use PHP multithreading, for this you need an extension pthreads.
Look in the docs for PHP threading
The most interesting feature is using it like this (code modified from PHP.net)
class My extends Thread {
public function run() {
//curl_exec whatever
}
}
$my = new My();
//start as many as you need
$my->start();
//wait for the threads to finnish and join one thread at a time with main-process-thread:
var_dump($my->join());:
Good Luck!

Categories