Giter VIP home page Giter VIP logo

Comments (9)

bennnjamin avatar bennnjamin commented on June 8, 2024

Amp is great for this but it is non-trivial to integrate into conventional PHP applications. Since you mentioned AJAX I am assuming you have conventional PHP deployment where each PHP process handles a single web request. In this typical execution context, you have a PHP script that is executed in it's entirety for a single HTTP request, usually returning an HTTP response, and exiting.

Amp is an event loop that will run until there is nothing else to run on the loop. So once you start an Amp event loop, it blocks until completion, and provides an API for you to run tasks on the event loop. If you re-create your example with a single PHP script and run it from the command line, you will see the exact same behavior: your command line script runs synchronously for 30 seconds and exits. The event loop has one task to run after a delay of 30 seconds, and exits after all tasks complete. The asynchronous power of Amp comes when you use it to run your entire application, allowing any task (including handling HTTP requests/responses) to be run on the event loop. This lets you do things like immediately return a response to the client, and simultaneously kick off a long-running job on the event loop.

Now with your existing application, you have a couple options.

  1. Since you're already using a message-broker (ZMQ), that is an ideal solution for handling async/long-running jobs in conventional PHP applications. You can use ZMQ to communicate between your current application, and an Amp event loop to run jobs.
  2. You can also just use shell_exec to kick off background process as well right after the upload completes. Like shell_exec("poll_api.php &")
  3. You can skip Amp for now and create a PHP script that polls for jobs from ZMQ, looking for messages to run that job (see https://github.com/zeromq/php-zmq/blob/master/examples/simple-server.php).

from amp.

fuad-tareq avatar fuad-tareq commented on June 8, 2024

Thanks for your suggestions, they're very helpful. I was actually just thinking of doing your first suggestion but I might be missing something.

For contextual purposes, I have a CLI-ran PHP Ratchet script with websockets. This is where I send my ZMQ requests to as well. It also tracks clients logging on and off the site so it is an infinitely running PHP React Event Loop (server). I thought that if I ran the exact same example (I posted earlier) within the CLI-ran PHP Ratchet's onMessageReceived() function, that I'd be able to execute the task asynchronously. But I must be missing something because the code I had put after the async task only executed 30 seconds later. Am I missing an amp event loop that I need to instantiate myself on another CLI-ran PHP script for the example I copied from the docs to work? If so, is it possible to somehow get this async task to use my PHP Ratchet's (React) event loop somehow haha?

As for your 2nd suggestion, I did actually try that at first and got it to work (although with exec as opposed to shell_exec) exactly how I wanted it to, but I had 2 main concerns with it, specifically:

  1. Passing arrays/objects as parameters to the script.
  2. What happens when a 2nd client uploads a video at the same time (i.e. would 2 background processes run simultaneously and if so how many simultaneous background processes can the system handle and what happens when it can't OR would they be scheduled to run sequentially).

from amp.

kelunik avatar kelunik commented on June 8, 2024

Hey @fuad-tareq, thank you for the detailed summary of your current situation. It really helps giving you the right hints.

As @bennnjamin already mentioned, thing are only async / non-blocking within the event loop. If you're already running Ratchet, that's great! Ratchet makes use of ReactPHP, which is another event loop implementation in PHP. However, we've got you covered with https://github.com/amphp/react-adapter. It let's you run any Amp / ReactPHP application in combination on a single event loop. Without the adapter, each event loop would block the other from executing. Given that, I'd probably move polling into the Ratchet process.

from amp.

bennnjamin avatar bennnjamin commented on June 8, 2024

In that case you definitely have the right setup for what you're trying to achieve. This line $result = Amp\Promise\wait($promise); will cause your application to block until the promise resolves so if your server has this code exactly as written, any code after that line will be executed 30 seconds later. Instead, you want to use

$promise->onResolve(function ($exception, $value) {
  //handle $exception, or $value here
});

which is a callback that will be ran after your promise completes, so 30 seconds later in your case. This will not block the event loop.

exec/shell_exec is not the most scalable solution for the reasons you mentioned. Since you already have a separate PHP process running an event loop, I would not recommend it. Amp or React is a far superior solution. To answer the question though 1) Passing data to the script is done by serializing it, typically with json_encode and json_decode 2) yes you would get two background processes and it may not be scalable depending on your client load. There are more robust solutions to allow X number of jobs to run simultaneously, etc.

from amp.

fuad-tareq avatar fuad-tareq commented on June 8, 2024

Ah, I must be doing something really silly now because I did exactly as you suggested @bennnjamin but it only partially worked. I have this exact snippet in my onMessageReceived() handler in the React event loop:

function asyncMultiply($x, $y) {
  // Create a new promisor
  $deferred = new \Amp\Deferred;

  // Resolve the async result 30s from now.
  Loop::delay($msDelay = 30000, function () use ($deferred, $x, $y) {
    $deferred->resolve($x * $y);
  });

  return $deferred->promise();
}

if ($data['w_async'] === 'true') {
  error_log('async start');
  $promise = asyncMultiply(6, 7);
  $promise->onResolve(function ($exception, $value) {
      error_log('onComp: ' . print_r($exception, true));
      error_log('onComp: ' . print_r($value, true));
   });
  error_log('async end');
}

And I'm getting the following output immediately on execution in my error_log:

async start
async end

What I expected:

async start
async end
... 30 seconds later ...
onComp ...
onComp ...

So while it is no longer blocking my event loop, I am not getting the onComp error_logs (inside the onResolve) I expect 30 seconds later. What am I missing?

PS: @kelunik's brilliant answer went straight over my head, not sure if he means that there's a more efficient way to achieve what I am intending using amphp's React adapter... or perhaps I'm supposed to combine his answer with yours in order to get the promise to work as intended?

from amp.

bennnjamin avatar bennnjamin commented on June 8, 2024

You do need to ensure you are using amphp/react-adapter as @kelunik mentioned. This is because your application can only have one event loop running. Both Amp and React provide their own event loop implementations. The adapter allows you to mix both Amp and React libraries in the same application, which is great!

You could also try just getting the example to work with React's own Promise library (https://github.com/reactphp/promise) since it seems you already have an event loop running under React.

from amp.

fuad-tareq avatar fuad-tareq commented on June 8, 2024

Thanks for the quick response. Upon looking more into this, I am a bit confused because it seems there's React PHP (as referenced by you which is on v1.3 right now) and there's react/zmq which is what I have installed and it's on version 0.4. I suspect those are 2 different libraries... Would your react-adapter still work with this setup?

from amp.

bennnjamin avatar bennnjamin commented on June 8, 2024

I'm not familiar with React's versioning semantics so you may need to open an issue with them. react-adapter allows you to run any React PHP library the expects a LoopInterface on the Amp event loop. If you are using React, it might be simpler to stick with only react libraries for now (dropping any Amp libs), or create a simple project that uses react-adapter to run React libraries on the Amp event loop.

from amp.

fuad-tareq avatar fuad-tareq commented on June 8, 2024

Thanks for all your help with this. It took forever and a half and I had to manually update the ReactPHP part of the react/zmq library. But I finally managed to get it working with ReactPHP's Promises as you suggested. I did like amphp more but since ReactPHP was already part of my Ratchet PHP server, it made sense to use the latter.

from amp.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.