Giter Club home page Giter Club logo

Comments (13)

bartvanhoutte avatar bartvanhoutte commented on June 9, 2024 1

Also see php-http/react-adapter#49

from httplug.

WyriHaximus avatar WyriHaximus commented on June 9, 2024 1

@veewee Thanks for the details explanation on PSR-18, we're looking into it at the moment, no promises, but with fibers PSR-18 and such are very much likely to be in reach.

from httplug.

dbu avatar dbu commented on June 9, 2024

my understanding is that the PSR for promises would need to be restarted. promises might need to consider event loop, though "we" (as in, the async http client) is not really concerned with event loop, async http requests are useful in classic PHP applications as well to just parallelize several http requests.

when promises get accepted, we can pick up the async http client topic again.

for the state of a promises PSR, i guess it would be best to inquire with the fig, maybe on their mailing list. afaik its a tricky topic to get right, but maybe ideas/opinions/technology has evolved since the last attempt?

from httplug.

joelwurtz avatar joelwurtz commented on June 9, 2024

I think we can close this, with fiber in php 8.1 async or sync can have the same API so this is no longer necessary,

I believe next goal would be to deprecate async interface, create an async implementation of PSR 18 (with amp http client v3 by example) and add an example on how to do parallel request with fiber + psr 18

from httplug.

dbu avatar dbu commented on June 9, 2024

that sounds very interesting! i did not look into fiber yet. is it some sort of "threads"? so we could have the requests block but people use multiple fiber threads to do parallel requests?

why would we need a specific client for it to work? afaik PSR-18 can not return a promise but only the real result. what does a client such as amp do different than e.g. guzzle that we can leverage here?

from httplug.

veewee avatar veewee commented on June 9, 2024

@dbu

Fibers can deal with async IO internally without having to deal with promises. This means that the existing PSR-18 interface can be used for both async and sync requests.
An event-loop (like revolt) must be used in order to keep track of the suspended fibers.

Amp is working on v3 of amp and v5 of their http client which is based on fibers. This means that it will probably also be included in Symfony/http-client at some point, since they use amp internally. Resulting in an async fiber based psr-18 client.

See examples of new http client here:
https://github.com/amphp/http-client/blob/8603d69bffa33f805b7009b58637c692340c65aa/examples/concurrency/1-concurrent-fetch.php#L1

I'dd say that the async client interface might still make sense for non-fiber based clients like e.g. guzzle at this point in time.

Fiber based clients could even make the promise->wait() method non-blocking.

So even if there would be fiber based psr-18 clients, it might make sense to keep the interface nevertheless.
My guess is that there most likely won't come a PSR for async clients, since we have fibers.

from httplug.

dbu avatar dbu commented on June 9, 2024

thanks for that update @veewee

it seems to me like we should eventually rework the doc section on promises now that fibers exist.

just to make sure i understand correctly: when using fibers and PSR-18, from the PHP point of view, you'd start a fiber for each request and have that fiber blocked until the request is finished?

from httplug.

veewee avatar veewee commented on June 9, 2024

It depends on the implementation.
Fibers are meant to be non-blocking: you suspend code execution to a future time, if a specific condition is met.

You indeed start a fiber for every request. After sending the request, there is a moment that the server must wait for a response. At that moment, the fiber is suspended until there is something to read on the socket. The implementation can be suspended again for example for chunked responses etc.

The idea is that the event-loop keeps track of all these suspended states. After sending multiple requests, you can wait for all requests to be finished. None of these waits is blocking the other requests. Meaning that you can actually parse some responses before an other one has finished responding. This parsing can be done whilst the event-loop is waiting for a socket to become readable again.

from httplug.

dbu avatar dbu commented on June 9, 2024

i stumbled over https://packagist.org/packages/symplely/hyper which implements psr-18. i did not have time to investigate much, but if i get the idea correctly, fibers might allow async programs to interact with a psr-18 client.

from httplug.

dbu avatar dbu commented on June 9, 2024

oh, if i get it correctly, symplely/hyper depends on a php extension libuv and is not using fibers, so not really a general solution.

from httplug.

veewee avatar veewee commented on June 9, 2024

@dbu

https://github.com/veewee/reactphp-soap/blob/php-soap-psr18/src/Protocol/Psr18Browser.php

This is an example implementation of a fully async fiber based react psr-18 client.
Externally, it behaved like a regular psr-18 client.
Yet you can await the request call to make it work async behind the curtains.

It will probably be sufficient to provide a similar looking adapter somewherein a httplug package in order to make it composer requireable.

from httplug.

dbu avatar dbu commented on June 9, 2024

one of these days i really need to catch up on fibers. but actually, do we even need to provide a special client? or would it be enough for the caller to call await($client->sendRequest(...)) to only block that fiber instead of the whole process? if i understand fibers correctly, they do not use a promise pattern, but are used like threads? so you would start a thread to do the web request and possibly wait somewhere for the thread to finish.

or what would the job of a psr-18 fiber adapter be?

from httplug.

veewee avatar veewee commented on June 9, 2024

Both react and amp have their own fiber based http-client implementation, with their own response / request objects.
They do not provide a PSR-18 version of that client. So the adapter package would provide a PSR-18 client interface for those http clients.
For example a React PSR18 client might look like this:

use Http\Client\HttpClient;
use Psr\Http\Message\RequestInterface;
use Psr\Http\Message\ResponseInterface;
use React\Http\Browser;
use function React\Async\await;

final class Psr18Browser implements HttpClient
{
    public function __construct(
        private Browser $browser
    ){
    }

    public function sendRequest(RequestInterface $request): ResponseInterface
    {
        return await(
            $this->browser->request(
                $request->getMethod(),
                (string) $request->getUri(),
                $request->getHeaders(),
                (string) $request->getBody()
            )
        );
    }
}

As you can see, the interface looks as-is you were calling the API in a sync way.
So in this case, you could just call:

$response = $client->sendRequest(xxx);

It will return a response, just like in a sync API - but react will make sure that it happens in a concurrent non-blocking way underneath. This is done by fibers. React provides an event-loop that waits for I/O and resumes the fiber when the data is available. This makes it possible to do multiple requests at once (since most of the time is just waiting for the response).

The main take-away from above, is that it does I/O operations in a non-blocking way in a single thread. Since most of the time you are waithing for the request stream to be sent or the response stream being received, fibers make sure that other code can be executed whilst you are waiting for that I/O. Yet all happens in a single thread, so no code gets executed concurrently. It's all about waiting for the I/O.

You can transform the API to an async (promise based) API like this:

$promise = async(fn () => $client->sendRequest(xxx));

Yet, since it is all fiber based, I/O in both examples will be non-blocking. You will just have another way of dealing with the response from a PHP code point of view.

This indeed means that there is no need for a new client interface in httplug and you could even remove the async and promise implementations in favour for the fiber based approach.

To give an example, plugins/middleware might look like this:

interface Plugin
{
    public function handleRequest(RequestInterface $request, callable $next, callable $first): Response;
}


class SomePlugin implements Plugin
{
    public function handleRequest(RequestInterface $request, callable $next, callable $first): Response
    {
        try {
            $response = $next($request->withX());
        } catch (NetworkException $e) {
            log('xxx');
            throw $e;
        }

        return $response->withX();
    }
}

This makes it possible to use the same plugins in both sync and async psr-18 implementations.
Since there is no need for a promise anymore.
As mentioned above - if you do want to have promises, you can still do

$promise = async(fn() => $next($request->withX()));

From this package's view, you dont really have to care if the implementation is fiber based or just regular PSR-18. It depends on the implementation if it behaves async or not.

For fiber based implementations, this would run in "parallel". For regular PSR-18 implementations, this will run in "series" (from a I/O point of view of course - we dont have threads)

$responses = parallel([
    async(fn() => $client->request($a)),
    async(fn() => $client->request($b)),
    async(fn() => $client->request($c)),
]);

More info about functions and components used above:

I hope this comment clearifies things. Because it's quite confusing, yet very easy :)

from httplug.

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.