Laravel octane first experience

Time:2021-9-21

Laravel octane first experience

Laravel octane has been released for several weeks. Although it is still in beta status, it can’t stop developers from loving it. In less than a month, its number of stars in GitHub has exceeded 2K; partdeveloperTheir project has been run on laravel octane.

If you are still waiting, you can also wait for the stable version in a week or two.

We will likely go ahead and tag Octane 1.0 as stable next week Taylor Otwell on Twitter.

In order to experience the magic of acceleration, the author has taken a simple H5 project to test the water in the production environment. In addition to some messy problems, the author is excited about others. The customer also said that our platform is so fast. I’ll see you next time.

Composition of laravel octane

Laravel octane has built-in two high-performance application services:SwooleandRoadRunner, as described in the official documents:

Octane boots your application once, keeps it in memory, and then feeds it requests at supersonic speeds.

We know that the laravel framework has always been excellent, but it has always been criticized for its performance. The boot time of the framework may be longer than the business processing time, and with the increase of the third-party service providers of the project, its startup speed becomes more and more uncontrolled. Laravel octane accelerates our application by starting the application once and resident in memory.

Laravel octane needs php8.0 support. If you work under MacOS, you can refer to this article to update your PHP versionUpgrade to PHP 8 with Homebrew on Mac

Octane simple list

Although the official document has described it in detail, the author demonstrates it here through a simple listing item.

Create Laravel Application

➜ laravel new laravel-octane-test

 _                               _
| |                             | |
| |     __ _ _ __ __ ___   _____| |
| |    / _` | '__/ _` \ \ / / _ \ |
| |___| (_| | | | (_| |\ V /  __/ |
|______\__,_|_|  \__,_| \_/ \___|_|

Creating a "laravel/laravel" project at "./laravel-octane-test"
Installing laravel/laravel (v8.5.16)
...
Application ready! Build something amazing.

Install Laravel Octane

$ composer require laravel/octane

After the installation is successful, the reader can execute it directlyartisan octane:installTo install dependencies; Octane will prompt you for the type of server you want to use.

➜ php artisan octane:install

 Which application server you would like to use?:
  [0] roadrunner
  [1] swoole
 >

If you choose Roadrunner, the program will automatically help you install the dependencies required by Roadrunner; If you choose swoole, you just need to make sure you have manually installed the PHP swoole extension.

Using Roadrunner server

The use of Roadrunner is unsatisfactory. The author always makes some errors ignored by the official documents during the installation process.

Failed to download RR executable

In executionoctane:installWhen installing the Roadrunner dependency, the author cannot download the RR executable through GitHub. The error prompted is as follows:

In CommonResponseTrait.php line 178:

HTTP/2 403  returned for "https://api.github.com/repos/spiral/roadrunner-binary/releases?page=1".

If you also encounter such a mistake, it is recommended to go directlyRoadrunner official websiteDownload the RR executable file and. Rr.yaml configuration file of the corresponding platform and put them into the project root directory. For example, address of executable file and configuration file of MacOS platform:

Finally, remember to modify the executable permission of RR and the worker starting command of Roadrunner.

chmod +x ./rr
server:
  # Worker starting command, with any required arguments.
  #
  # This option is required.
  command: "php artisan octane:start --server=roadrunner --host=127.0.0.1 --port=8000"

ssl_valid: key file ‘/ssl/server.key’ does not exists

In the configuration file of Roadrunner, SSL configuration is enabled by default. If you do not need to enable HTTPS access, you can comment the http.ssl configuration.

Error while dialing dial tcp 127.0.0.1:7233

Roadrunner enables the temporary feature by default, and its listen port is 7233. If you don’t want to enable this feature, you can comment on the temporary configuration.

# Drop this section for temporal feature disabling.
temporal:

Information about temporary can be found on the official websitetemporalio/sdk-php: Temporal PHP SDK

Executable file not found in $PATH

Generally, the program execution path is not specified in the configuration file. Please check the following configuration.

  1. Server.command

Modify the startup command to Roadrunner worker, such as:

php artisan octane:start —server=roadrunner —host=127.0.0.1 —port=8000
  1. Service.some_service_*.comment

If you do not want to use this feature, comment on the configuration. So far, the author’s RoadrunnerfinallyIt’s starting.

Laravel octane first experience

AB Test For RoadRunner

The author made a simple AB test with his notebook (2018-13inch / 2.3ghz / 16GB). The framework code has not been changed. It is the default welcome page of laravel.

After changing different concurrency parameters and requests, the results obtained fluctuate slightly up and down as shown in the figure below, and its QPS is basically maintained at about 230 / s.

➜  ~ ab -n 2000 -c 8 http://127.0.0.1:8000/
Server Software:
Server Hostname:        127.0.0.1
Server Port:            8000

Document Path:          /
Document Length:        17490 bytes

Concurrency Level:      8
Time taken for tests:   8.418 seconds
Complete requests:      2000
Failed requests:        0
Total transferred:      37042000 bytes
HTML transferred:       34980000 bytes
Requests per second:    237.59 [#/sec] (mean)
Time per request:       33.671 [ms] (mean)
Time per request:       4.209 [ms] (mean, across all concurrent requests)
Transfer rate:          4297.28 [Kbytes/sec] received

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        3   11   4.6     11      29
Processing:     3   20  34.8     15     270
Waiting:        3   18  34.8     12     270
Total:          7   31  35.2     25     284

By default, the welcome page of laravel will go through the web middleware first, and finally render the blade page; The web middleware contains a large number of cookies and session operations:

protected $middlewareGroups = [
    'web' => [
        \App\Http\Middleware\EncryptCookies::class,
        \Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
        \Illuminate\Session\Middleware\StartSession::class,
        \Illuminate\View\Middleware\ShareErrorsFromSession::class,
        \App\Http\Middleware\VerifyCsrfToken::class,
        \Illuminate\Routing\Middleware\SubstituteBindings::class,
    ],
];

Therefore, the author redefines a test route, which does not contain any middleware (except the global), and only outputs a hello world.

// RouteServiceProvider.php
public function boot()
{
    require base_path('routes/test.php');
}

// test.php
Route::get('/_test', function () {
    return 'Hello World';
});

After retesting, it can be seen that its QPS has reached the official publicity standard 2300 / S (is it the same with the official test remove all middleware?).

Server Software:
Server Hostname:        127.0.0.1
Server Port:            8000

Document Path:          /_test
Document Length:        11 bytes

Concurrency Level:      8
Time taken for tests:   0.867 seconds
Complete requests:      2000
Failed requests:        0
Total transferred:      374000 bytes
HTML transferred:       22000 bytes
Requests per second:    2307.81 [#/sec] (mean)
Time per request:       3.466 [ms] (mean)
Time per request:       0.433 [ms] (mean, across all concurrent requests)
Transfer rate:          421.45 [Kbytes/sec] received

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        0    0   0.1      0       3
Processing:     1    3   8.8      2     143
Waiting:        1    3   8.8      2     142
Total:          1    3   8.8      2     143

In the above test process, the author’s native resources are limited as follows.

~ ulimit -n
256

Using the spool server

The use of swoole server is much smoother; After the PHP swoole extension is installed through PECL, it can be started without any configuration.

Laravel octane first experience

AB Test For Swoole Server

The author uses the same configuration to perform AB test on the spool server. The results are as follows, and its QPS is basically maintained at about 230 / s.

Server Software:        swoole-http-server
Server Hostname:        127.0.0.1
Server Port:            8000

Document Path:          /
Document Length:        17503 bytes

Concurrency Level:      8
Time taken for tests:   8.398 seconds
Complete requests:      2000
Failed requests:        0
Total transferred:      37130000 bytes
HTML transferred:       35006000 bytes
Requests per second:    238.15 [#/sec] (mean)
Time per request:       33.592 [ms] (mean)
Time per request:       4.199 [ms] (mean, across all concurrent requests)
Transfer rate:          4317.61 [Kbytes/sec] received

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        3   11   6.6     10     102
Processing:     4   20  50.3     12     442
Waiting:        2   18  50.3     11     441
Total:          7   30  50.9     23     450

The test results of middleware free routing are as follows. It can be seen that its QPS has reached 1650 / s.

Server Software:        swoole-http-server
Server Hostname:        127.0.0.1
Server Port:            8000

Document Path:          /_test
Document Length:        21 bytes

Concurrency Level:      8
Time taken for tests:   1.212 seconds
Complete requests:      2000
Failed requests:        0
Total transferred:      528000 bytes
HTML transferred:       42000 bytes
Requests per second:    1650.63 [#/sec] (mean)
Time per request:       4.847 [ms] (mean)
Time per request:       0.606 [ms] (mean, across all concurrent requests)
Transfer rate:          425.55 [Kbytes/sec] received

From the AB test results, the performance of the two servers is basically the same; However, because it is tested in the local development environment, many factors are not considered, and the test results are only for reference.

Deploy Online

Although laravel octane provides the start command to start the server, the command can only be run in the foreground (does not support – D); When deploying to a production environment, the common approach is to useSupervisorFor process management. Readers can refer toLaravel SailSupervisor configuration for.

[program:php]
command=/usr/bin/php -d variables_order=EGPCS /var/www/html/artisan serve --host=127.0.0.1 --port=80
user=sail
environment=LARAVEL_SAIL="1"
stdout_logfile=/dev/stdout
stdout_logfile_maxbytes=0
stderr_logfile=/dev/stderr
stderr_logfile_maxbytes=0

During subsequent continuous delivery, you can connect to the service node through Jenkins and useoctane:reloadCommand to reload the service.

Stage ("deploy ${IP}"){
    withCredentials([sshUserPrivateKey(credentialsId: env.HOST_CRED, keyFileVariable: 'identity')]) {
        remote.user = "${env.HOST_USER}"
        remote.identityFile = identity
        sshCommand remote: remote, command: "php artisan config:cache && php artisan route:cache && php artisan octane:reload"
    }
}

However, it should be noted here that when you update the composer dependency, such as adding a third-party package, you’d better restart laravel octane in the production environment.

sudo supervisorctl -c /etx/supervisorctl.conf restart program:php

Otherwise, errors such as class “godruoyi \ snowflake \ snowflake” not found may occur.

Is laravel octane thread safe?

Before answering this question, let’s take a look at the request processing flow of laravel octane.

Laravel octane first experience

As the server starts, the program creates a specified number of worker processes. When the request arrives, it will select one from the list of available workers and hand it over to him for processing. Each worker can only process one request at a time. In the process of request processing, there will be no competition for the modification of resources (variables / static variables / file handles / links). Therefore, when laravel octane is used, threads (processes) are safe.

This is actually consistent with the FPM model. The difference is that after processing a request, the FPM model will destroy all the memory applied by the request; When subsequent requests arrive, you still need to perform a complete PHP initialization operation (seePhp-fpm startup analysis)。 The initialization operation of laravel octane follows the worker boot. During the entire worker life cycle, only one initial operation will be performed (when the program is started). Subsequent requests will directly reuse the original resources. As shown in the figure above, after the worker boot is completed, the larevel application container will be initialized, and all subsequent requests will reuse the app instance.

How laravel octane works

Octane is just a shell. The real requests are processed by an external server. However, octane’s design is worth mentioning.

It can also be seen from the source code that with the completion of the worker’s boot, the laravel application has been successfully initialized.

// vendor/laravel/octane/src/Worker.php
public function boot(array $initialInstances = []): void
{
    $this->app = $app = $this->appFactory->createApplication(
        array_merge(
            $initialInstances,
            [Client::class => $this->client],
        )
    );

    $this->dispatchEvent($app, new WorkerStarting($app));
}

When processing subsequent incoming requests, octane passesclone $this->appGet a sandbox container. All subsequent operations are based on this sandbox container and will not affect the original container. After the request ends, octane empties the sandbox container and unsets objects that are no longer used.

public function handle(Request $request, RequestContext $context): void
{
    CurrentApplication::set($sandbox = clone $this->app);

    try {
        $response = $sandbox->make(Kernel::class)->handle($request); 

    } catch (Throwable $e) {
        $this->handleWorkerError($e, $sandbox, $request, $context, $responded);
    } finally {
        $sandbox->flush();

        unset($gateway, $sandbox, $request, $response, $octaneResponse, $output);

        CurrentApplication::set($this->app);
    }
}

Note again that the same worker process can only process one request at a time, so there is no competition here. Even the modification of static variables is safe.

Precautions & third party package adaptation

Since multiple requests from the same worker share the same container instance, you should be careful when registering singleton objects in the container. For example:

public function register()
{
    $this->app->singleton(Service::class, function ($app) {
        return new Service($app['request']);
    });
}

In the example, a singleton is used to register a singleton service object. When the object is initialized in the boot method of a provider, the application container will always maintain a unique service object; When subsequent workers process other requests, the request object obtained from the service will be the same.

The solution is that you can change the binding method or use closures. The most recommended method is to pass in only the requested information you need.

use App\Service;

$this->app->bind(Service::class, function ($app) {
    return new Service($app['request']);
});

$this->app->singleton(Service::class, function ($app) {
    return new Service(fn () => $app['request']);
});

// Or...

$service->method($request->input('name'));

Readers are strongly recommended to read the official proposalmatters needing attention。 If you think the article is helpful to you, you can also subscribe to the author’s blogRSSOr visit the author’s blog directlyTwo Leng gossip miscellaneous fish

reference resources

Recommended Today

Seven Python code review tools recommended

althoughPythonLanguage is one of the most flexible development languages at present, but developers often abuse its flexibility and even violate relevant standards. So PythoncodeThe following common quality problems often occur: Some unused modules have been imported Function is missing arguments in various calls The appropriate format indentation is missing Missing appropriate spaces before and after […]