Giter Club home page Giter Club logo

server's Introduction

DriftPHP Server

CircleCI

This package provides a ReactPHP based async, reactive and non-blocking server for PHP applications working on top of ReactPHP Promises and PSR standards. The server has a small kernel/application abstraction for an optimized integration for several domain implementations. Here some features.

  • Handle request based on Promises
  • Serve static content in a non-blocking way
  • Compress both Responses and Stream data
  • Work with different workers, using multiple PHP threads (and CPUs)
  • Visualize the usage and check how fast and light your request handles are
  • Use the PHP-Watcher (only available if the PHP-Watcher is included in your composer) to automatically update the server if you change some of your code

By default, the server will use the DriftPHP Kernel adapter, but you can change the adapter easily when starting the server (Check the adapters chapter)

Table of content

Installation

You can install the server by adding the dependency in your composer.json file

"require": {
    "drift/server": "^0.1"
}

Start the server

This package provides an async server for DriftPHP framework based on ReactPHP packages and Promise implementation. The server is distributed with all the Symfony based kernel adapters, and can be easily extended for new Kernel modifications.

To start the server, just type this line. Your project might have a custom bin folder, so check it.

php vendor/bin/server run 0.0.0.0:8000

You can use as well the short mode, defining only the port and assuming this host 0.0.0.0.

php vendor/bin/server run 8000

And that's it. You will have a fully working server for your application.

Build your adapter

In order to build your adapter, the only thing you need is to create an implementation of the interface Drift\Server\Adapter\KernelAdapter. This layer will allow the server to start your application, handle each request, locate your static resources and shutdown the application. The ObservableKernel will provide as well some information about where your code is located, specifically designed for the watcher feature.

/**
 * Class KernelAdapter.
 */
interface KernelAdapter extends ObservableKernel
{
    /**
     * @param LoopInterface            $loop
     * @param string                   $rootPath
     * @param ServerContext            $serverContext
     * @param OutputPrinter            $outputPrinter
     * @param MimeTypeChecker          $mimeTypeChecker
     * @param FilesystemInterface|null $filesystem
     *
     * @return PromiseInterface<self>
     *
     * @throws KernelException
     */
    public static function create(
        LoopInterface $loop,
        string $rootPath,
        ServerContext $serverContext,
        OutputPrinter $outputPrinter,
        MimeTypeChecker $mimeTypeChecker,
        ?FilesystemInterface $filesystem
    ): PromiseInterface;

    /**
     * @param ServerRequestInterface $request
     *
     * @return PromiseInterface<ResponseInterface>
     */
    public function handle(ServerRequestInterface $request): PromiseInterface;

    /**
     * Get static folder.
     *
     * @return string|null
     */
    public static function getStaticFolder(): ? string;

    /**
     * @return PromiseInterface
     */
    public function shutDown(): PromiseInterface;
}

When you have your adapter created, the is as easy is this to start serving from your application

php vendor/bin/server run 0.0.0.0:8000 --adapter='My\Namespace\Adapter"

Custom response output

You can internally use the x-server-message header for custom server messages. The server will remove this server value before returning the response content.

Workers

This server creates a single worker by default. A simple PHP thread that will use one single CPUs. Luckily this server provides you a simple way of creating multiple instances listening the same port, emulating a simple balancer between N threads.

php vendor/bin/server run 0.0.0.0:8000 --workers=8

You can guess the number of physical threads your host has by using the value -1. By default, a single worker will be used.

This feature is not designed and intended for production environments. We encourage to use a reversed proxy or a small balancer if you need to balance between several processes. Furthermore, this feature uses pcntl_fork, so as the documentation explains it is not available for Windows users.

Watcher

You can use the watcher by installing the seregazhuk/php-watcher dependency in your composer.

"require-dev": {
    "seregazhuk/php-watcher": "*"
}

After installing the dependency, you will be able to start your server by checking code changes.

php vendor/bin/server watch 0.0.0.0:8000

This feature is for development only.

Static server

This server can serve static files as well located in your project. By default, an adapter will provide a path where static files should be found (like DriftPHP statics are located under public/ folder), but you can overwrite this value, or even override it.

php vendor/bin/server watch 0.0.0.0:8000 --static-folder=/my/own/folder/
php vendor/bin/server watch 0.0.0.0:8000 --no-static-folder

You can create an alias as well if you need it. That can be useful if you want to mask the internal path with an external one, only exposing this second one. Both values must be separated by the symbol :, being the first part the alias, and the second one the internal path.

php vendor/bin/server watch 0.0.0.0:8000 --static-folder=/public/:/internal/public/path

In this example, a file named app.js located under /internal/public/path/ folder will be accessible at http://localhost:8000/public/app.js. By default, this feature is disabled.

Static server cache

You can define your static folder cache by adding a YAML file wherever you want and referencing it when starting the server.

php vendor/bin/server watch 0.0.0.0:8000 --static-cache=/my/path/static.cache.yml

In this file you can define specific headers for all your static resources (in fact, not only cache ones). You can define as well regular expressions for matching them.

/public/js/.*:
  Cache-Control: max-age=31536000
/public/css/app.js:
  Content-Type: application/javascript
/public/images/logo.png:
  Other-Header: Other-Value

By default, this feature is disabled and no extra headers will be added into your static resources.

Important

By default, this package will not install the react/filesystem package. This means that, if you don't install it by hand in your project, all the disk operations will be blocking. These operations done synchronously will be much faster and efficient, but by using large size files could slow down the entire process.

Symfony bridge

In order to help you from migrating an application from Symfony to DriftPHP, assuming that this means that your whole domain should turn on top of Promises, including your infrastructure layer, this server is distributed with a small Symfony adapter. Use it as a tool, and never use it at production (using a ReactPHP based server in a blocking application is something not recommendable at all in terms of performance and service availability). That adapter will help your migrating from one platform to the other, as will allow this server to work with your Symfony kernel.

php vendor/bin/server watch 0.0.0.0:8000 --adapter=symfony

DriftPHP resources

Some first steps for you!

or

server's People

Contributors

basster avatar mmoreram avatar nivpenso avatar petronetto avatar seregazhuk avatar tomjvdberg avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar

server's Issues

Set basic request server headers

There are no $_SERVER parameters available in the Symfony request. For the isFromTrustedProxy there is at least the REMOTE_ADDR header required.

RFC - Fallback for synchronous applications

To continue discussion of #58

I am happy that feature #85 was merged. I am asking myself if we could make the integration of Drift into existing Symfony applications even more simple.

I am asking myself if the changes to the Kernel could be put into a Trait and we could create an AsyncKernelInterface that drift can check against do see if the Kernel supports Async operations.

We would need to make it possible to allow the application to implement async operations on the one hand, which are only enabled if they are running in a Drift context, and fallback to synchronous if they are executed using PHP-FPM.

Using this approach we may could also achieve that existing applications like Sulu, eZ Publish or OroCRM adopt "Drift" as an additional experimental application mode which would extend the popularity and adoption of the project.

Tip for excessive memory usage

Driftphp app run by supervisord (restarts on exit),

this patch:

--- a/vendor/drift/server/src/Application.php
+++ b/vendor/drift/server/src/Application.php
@@ -49,6 +49,9 @@
  */
 class Application
 {
+    private const MAX_MEMORY_MB = 160;
+    private const GLOBAL_CONNECTION_COUNT = 'drift_connection_count';
+
     private LoopInterface $loop;
     private ServerContext $serverContext;
     private string $rootPath;
@@ -111,6 +114,26 @@
             $this->loop
         );

+        $GLOBALS[self::GLOBAL_CONNECTION_COUNT] = 0;
+
+        $socket->on('connection', function($conn) use ($kernelAdapter) {
+            $GLOBALS[self::GLOBAL_CONNECTION_COUNT]++;
+
+            $conn->on('close', function() use ($kernelAdapter) {
+                $GLOBALS[self::GLOBAL_CONNECTION_COUNT]--;
+
+                $memoryUsage = (int) (memory_get_usage() / 1000000);
+                if (
+                    0 === $GLOBALS[self::GLOBAL_CONNECTION_COUNT]
+                    && $memoryUsage > self::MAX_MEMORY_MB
+                ) {
+                    echo "Stopping due to excessive memory usage: $memoryUsage MB" . PHP_EOL;
+                    await($kernelAdapter->shutdown(), $this->loop);
+                    $this->loop->stop();
+                }
+            });
+        });
+
         $http = new HttpServer(
             $this->loop,
             new StreamingRequestMiddleware(),

plus socat relay on the front:

socat TCP4-LISTEN:"$WEB_PORT",reuseaddr,fork,bind="$POD_IP" TCP4:127.0.0.1:"$WEB_PORT",forever,interval=3

($POD_IP because I'm using Kubernetes)

This seamlesly restarts main service, while it is processing requests :)

Add SSL support

We should add SSL Support in the server (with SSL feature in socket reactphp package). We could work with these flags

  • --ssl-enabled
  • --ssl-local-cert
  • --ssl-passphrase
  • --ssl-crypto-method

We should add as well a force-ssl flag with a redirection when is needed

  • --force-ssl

The watcher doesn't rebuild the cache when necessary

Currently, the watcher rebuilds the whole container (with cache) only in --debug mode. Which is not used by default (for example, in skeleton).

And it leads to weird errors. For example, I have a controller. And I add a dependency to it. The watcher says that it detects the change and reloads the app. But, the container stays the same. The way the controller is built in the container is outdated. And I receive an error like this:

Too few arguments to function App\Controller\DefaultController::__construct()

I'm not sure that we need to use debug mode always for development. Because recreating a cache for any change looks like a bit overhead ๐Ÿ™ˆ
@mmoreram do you have any ideas how to solve it? Or maybe I'm misusing the server and doing something wrong?

streamed request / response not handled correctly

In src/RequestHandler.php#L195 streams are not handled correctly for the request body. Also in the response conversion, the streams get lost.

In symfony/psr-http-message-bridge, there is correct handling for streams.

Check possible multi-CPU usage

This issue is to discuss how we could work with all CPUs at the same time by using a single DriftPHP server. Something like --workers=4.

When running the server with watch argument breakpoints doesn't work using xdebug

This is an issue I found while setting up a development environment.

I haven't explored the issue furthermore, and for that reason, I find it important to add this issue here for further investigation.

When setting debug configuration settings (I use PHPStorm) to run a server with a watch argument, the server doesn't pause on breakpoints except at the bootstrap.php file.

Important note: the same configuration with a run argument works and the program pause at breakpoints.

image

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.