Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Concurrency #22

Open
evert opened this issue Jan 21, 2016 · 8 comments
Open

Concurrency #22

evert opened this issue Jan 21, 2016 · 8 comments

Comments

@evert
Copy link

evert commented Jan 21, 2016

Hi there!

I just did my first FastCGI implementation. It was fairly simple to get it up and running. The big thing I'm thinking about now though is, how is this actually going to improve performance? Given that currently the request-response sequence happens in one synchronous process, this effectively will mean that if a user triggers a request that takes more than a second (due to a slow mysql query), every other user will have to wait till that request has finished.

Is there an internal API that will allow me to get the daemon to parse incoming requests, and send responses back asynchronously? If so, I'll be able to integrate an event loop...

@AndrewCarterUK
Copy link
Member

Hello!

Thanks for taking the time to look into this project and provide valuable feedback :)

The "solution" I've had to that is to use multiple FastCGI instances (workers):

upstream workers {
    server localhost:5000;
    server localhost:5001;
    server localhost:5002;
    server localhost:5003;
}

server {
    # ... 

    location # ... {
        include         /etc/nginx/fastcgi_params;
        fastcgi_pass    workers;
        # ...
    }

    # ...
}

This prevents blocking operations from slowing down the server to a degree, but obviously you recognise it as just pushing the problem down the chain rather than actually solving it entirely!

In a way, this isn't actually too different to how PHP works anyway (there is a maximum number of worker threads and php instances on a server). The difference is that the resource overhead is higher, as without an adaptive process manager (such as apache mod_fastcgi) all the instances you want are running all the time.

To actually answer your question, there has been no accommodation for asynchronous processing at the moment.

I've toyed with the idea of making the daemon accept a response promise, but this wouldn't be much use without a way to expose the daemon's event loop to the application (otherwise the application would never be able to resolve a promise). To this, I see two solutions:

  1. Couple to an async framework, such as ReactPHP or Icicle.
  2. Work towards an abstraction for an event loop and couple to that instead.

I've tried to avoid going down the first route (as all of these projects are quite young), but just typing this out has given me an idea. Maybe there's a way to create a ReactPHP/Icicle driver as a separate package - so that users could use async functionality without the project directly coupling to either implementation? I'll be having a look at this over the next few weeks :)

The second option is the end goal, and I've been speaking to people in the FIG about this for a while. A few months ago I approached Christopher Pitt - and we have now formed a working group for an event loop PSR. The async-interop organization is the new home to our discussions (in the issues) and development (although it's pretty bare at the moment!).

TL/DR

Nothing at the moment, but you can just spawn multiple instances. In the next version I'm going to build support for a separate ReactPHP driver package and hopefully one day there will be an industry abstraction for this.

Thanks again for your thoughts and time, I'll keep this issue updated with any development :)

@evert
Copy link
Author

evert commented Jan 21, 2016

Another thing that you could consider, is exposing some of the inner workings of this application better so I can myself hook it into an event loop.

I have a very minimal stream-select based eventloop (hosted here, what I would need from this project in order make this work, and I'm fairly sure it's the same for react and icicle, is:

  1. I need access to socket stream resources. This way I can hook it into stream_select. This also works for libevent,libav and I pretty much guarantee that this works for ReactPHP/Icicle.
  2. I need to be able to tell the server: Give me the 0 or more pending requests. These requests will need to carry some kind of id.. I don't see that on the Request object right now.
  3. I need a function that allows me to send responses. Each response would have to carry some sort of id that matches the original request. I assume that the underlying protocol actually has some kind of sequence number for this.

Just those three things should effectively be enough to turn your synchronous server into an asynchronous one without having to supply adapters just yet or create a hard dependency on one implementation.

@brstgt
Copy link

brstgt commented Jan 17, 2017

Hi there!

The idea behind this project is really great! Unfortunately it will be absolutely useless for serious and high traffic projects without a proper process manager and either a multi-threaded or multi-process model.
Have there any plans evolved about this?

@AndrewCarterUK
Copy link
Member

Hi @brstgt,

A process manager isn't within scope for this project as I don't see any benefits in one that I would write over a proper process manager such as supervisord (which is what I recommend people use). It's quite easy to have a multi-process model too, just launch more processes and using NGINX to load balance between them.

Where this project could benefit is from an easier to access asynchronous core, potentially using the event loop standard from the async-interop group. I'd probably look to make a change of this scale into a new major version, and I'd probably aim to slim down on the dependencies (as mentioned in a different issue) at the same time.

I could be convinced otherwise on all of the above, but that's my current position. And those are "the plans".

At the moment I'm not working in web development (I'm currently working with embedded software). Thus my free time for open source has been directed away from PHP projects (though that may change). I'll still maintain this project for any bugs or security fixes that are required - but I'll be looking to the community for significant new features (and I'm more than happy to discuss and assist with the development of PRs).

Thanks very much for your interest in this project and I hope this is of some assistance!

Cheers,

Andrew

@brstgt
Copy link

brstgt commented Jan 18, 2017 via email

@brstgt
Copy link

brstgt commented Jan 18, 2017

Btw.: I could imagine that using pthreads could make this relatively easy. Forking is more complicated and requires IPC. pthreads already supports worker threads that can handle a task queue. So balancing by "least busy worker" would be walk in the park. Only dealing with (shared) resources in threaded environments (especially in PHP) needs some special attention.
And the downside is that it requires php-zts. I could imagine that this could also be implemented as a driver for connection handling.
Threads have some advantages over processes:

  • Threads are lighter, no (slow) forking required
  • No IPC required
  • Resources (sockets) stay in process scope
  • No process manager required - it is all one process

Disadvantages

  • Respawn on memleaks is not that graceful and smooth

@codeliner
Copy link

Hi, I'm following this very interesting discussion because we'd like to combine our work @prooph with async ideas and ways to keep the read/write models alive between requests.

@AndrewCarterUK your "plans" sound good.

my free time for open source has been directed away from PHP projects

I hope you find the way back to PHP ;)

@brstgt Do you know appserver.io? I came across the appserver project recently and wondering if it provides the features you listed above.

@brstgt
Copy link

brstgt commented Sep 21, 2017

Take a look at that one: https://github.com/amphp/aerys

We migrated from FPM to Aerys and are super happy! If you have a proper request abstaction, HTTP can definitely be favoured over fastcgi.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

4 participants