Swoole, PHP
git clone https://github.com/Watish/WatishWEBcomposer create-project watish/watishweb:dev-masterThe entry file of the project is project /bin/CoServer.php
swoole-cli ./bin/CoServer.php php ./bin/CoServer.php
Create a new class in the src/Controller directory, here we define it as HelloController
<?php
namespace Watish WatishWEB Controller ;
use Watish Components Attribute Path ;
use Watish Components Struct Request ;
class HelloController
{
#[Path( ' / ' )]
public function index ( Request $ request ) : array
{
return [
" msg " => " hello world "
];
}
}After saving, start the project and visit http://127.0.0.1:9502/ to see
{ "msg" : " hello world " }Isn't it very simple?
Unlike the traditional php-fpm form, there is memory isolation between multiple processes , which means that the variable process B set by process A cannot be retrieved. In addition, the request is not isolated . That is to say, two requests under the same process, although the processing logic in different coroutines is processed, if both global variable A are modified, then the global variable will be modified twice.
For details, please refer to the programming instructions in the swoole document #Critical Errors
Use WatishComponentsIncludesContext to effectively avoid the above problems
Context is a static class that not only provides simple Get and Set methods, but also provides GlobalSet, GlobalGet and other methods of multi-worker process global variables through process communication.
Note: Multi-worker process global variable setting (Context::Global_Set, etc.) is implemented asynchronously based on UnixSocket. It cannot guarantee the consistency of data at a certain moment. Here, you can use WatishComponentsUtilsTable , an encapsulated KV memory table for SwooleTable , which can make full use of each row of resources and support closure serialization.
When the browser sends a request to the server, the server will call the handle method, and then judge whether the requested route exists through the routing scheduler. The routing parameters exist, encapsulate them to WatishComponentsStructRequest , and pass them into the global middleware-> Local middleware-> Controller
Two ways to register routing
Note: You need to modify the **register_route_auto ** to true in **/config/server.php **
. . .
" register_route_auto " => true
...Prefix is a class annotation that defines the prefix of the route under this class.
#[Prefix(string $ prefix )]Path is a method annotation that defines the routing path
#[Path(string $ path ,array $ methods )]Take a chestnut:
<?php
namespace Watish WatishWEB Controller ;
use Watish Components Attribute Middleware ;
use Watish Components Attribute Path ;
use Watish Components Attribute Prefix ;
use Watish Components Struct Request ;
use Watish WatishWEB Middleware TestMiddleware ;
#[Prefix( ' /hello ' )]
class HelloController
{
#[Path( ' /index ' )]
public function index ( Request $ request ) : array
{
return [
" msg " => " hello world "
];
}
#[Path( ' /user/{name} ' ,[ ' GET ' , ' POST ' ])]
#[Middleware([TestMiddleware::class])]
public function msg ( Request $ request ) : array
{
return [
" msg " => " hello " . $ request -> route ( ' name ' )
];
}
}The above code is routed as follows
| path | Controller | method | middleware |
|---|---|---|---|
| /hello/index | HelloController@index | ANY | none |
| /hello/user/{name} | HelloController@msg | GET, POST | TestMiddleware |
The path to the routing configuration file is: project/config/route.php
Reuse the above chestnuts, then the above routing configuration should be as follows
<?php
use Watish Components Includes Route ;
use Watish WatishWEB Controller HelloController ;
function do_register_global_middleware ( Route $ route ): void
{
/**
$route->register_global_middleware(CorsMiddleware::class);
*/
}
function do_register_routes ( Route $ route ): void
{
$ route -> register ( ' /hello/index ' ,[HelloController::class, ' index ' ],[],[]);
$ route -> register ( ' /hello/user/{name} ' ,[HelloController::class, ' msg ' ],[TestMiddleware: class ],[ ' GET ' , ' POST ' ]);
}Register method transfer parameters as follows
Watish Components Includes Route-> register (string $ path , array $ callback , array $ before_middlewares , array $ methods )Note: Middleware must implement Middleware Interface
Register by annotation
You can implement global middleware registration by using GlobalMiddleware class annotations
For example:
<?php
namespace Watish WatishWEB Middleware ;
use Watish Components Attribute GlobalMiddleware ;
use Watish Components Struct Request ;
use Watish Components Struct Response ;
#[GlobalMiddleware]
class CorsMiddleware implements MiddlewareInterface
{
public function handle ( Request $ request , Response $ response ): void
{
$ response -> header ( " Access-Control-Allow-Origin " , " * " );
$ response -> header ( " Access-Control-Allow-Credentials " , true );
}
}Register via route
The configuration file path is: project/config/route.php
<?php
use Watish Components Includes Route ;
use Watish WatishWEB Controller HelloController ;
use Watish WatishWEB Middleware CorsMiddleware ;
function do_register_global_middleware ( Route $ route ): void
{
$ route -> register_global_middleware (CorsMiddleware::class);
}
function do_register_routes ( Route $ route ): void
{
$ route -> register ( ' /hello/index ' ,[HelloController::class, ' index ' ],[],[]);
$ route -> register ( ' /hello/user/{name} ' ,[HelloController::class, ' msg ' ],[],[ ' GET ' , ' POST ' ]);
}Register by annotation
You can use Middleware to annotate a controller or a method
#[Middleware(array $ middlewares )]Create a TestMiddleware first
<?php
namespace Watish WatishWEB Middleware ;
use Watish Components Struct Request ;
use Watish Components Struct Response ;
class TestMiddleware implements MiddlewareInterface
{
public function handle ( Request $ request , Response $ response )
{
$ response -> header ( " test " , " test " );
}
}Then modify HelloController
<?php
namespace Watish WatishWEB Controller ;
use Watish Components Attribute Middleware ;
use Watish Components Attribute Path ;
use Watish Components Attribute Prefix ;
use Watish Components Struct Request ;
use Watish WatishWEB Middleware TestMiddleware ;
#[Prefix( ' /hello ' )]
class HelloController
{
#[Path( ' /index ' )]
#[Middleware([TestMiddleware::class])]
public function index ( Request $ request ) : array
{
return [
" msg " => " hello world "
];
}
#[Path( ' /user/{name} ' ,[ ' GET ' , ' POST ' ])]
#[Middleware([TestMiddleware::class])]
public function msg ( Request $ request ) : array
{
return [
" msg " => " hello " . $ request -> route ( ' name ' )
];
}
}As mentioned above, the index method and the msg method have local middleware TestMiddleware
Of course, the above code can be written like this, and directly add Middleware annotations to HelloController
<?php
namespace Watish WatishWEB Controller ;
use Watish Components Attribute Middleware ;
use Watish Components Attribute Path ;
use Watish Components Attribute Prefix ;
use Watish Components Struct Request ;
use Watish WatishWEB Middleware TestMiddleware ;
#[Prefix( ' /hello ' )]
#[Middleware([TestMiddleware::class])]
class HelloController
{
#[Path( ' /index ' )]
public function index ( Request $ request ) : array
{
return [
" msg " => " hello world "
];
}
#[Path( ' /user/{name} ' ,[ ' GET ' , ' POST ' ])]
public function msg ( Request $ request ) : array
{
return [
" msg " => " hello " . $ request -> route ( ' name ' )
];
}
}Register via configuration file
Refer to the configuration file routing registration method in the routing section register parameter, not details here
The controller is the core of the entire business project, responsible for processing requests, calling services, and returning data
Simple, no description
Together with dependency injection , give me a chestnut:
<?php
namespace Watish WatishWEB Controller ;
use Watish Components Attribute Inject ;
use Watish Components Attribute Middleware ;
use Watish Components Attribute Path ;
use Watish Components Attribute Prefix ;
use Watish Components Struct Request ;
use Watish WatishWEB Middleware TestMiddleware ;
use Watish WatishWEB Service BaseService ;
#[Prefix( ' /hello ' )]
#[Middleware([TestMiddleware::class])]
class HelloController
{
#[Inject(BaseService::class)]
public BaseService $ baseService ;
#[Path( ' /index ' )]
public function index ( Request $ request ) : array
{
return [
" msg " => $ this -> baseService -> toArray ([ " Hello " , ' World ' ])
];
}
#[Path( ' /user/{name} ' ,[ ' GET ' , ' POST ' ])]
public function msg ( Request $ request ) : array
{
return [
" msg " => " hello " . $ request -> route ( ' name ' )
];
}
}Note: The construction method injection is not supported for the time being, and it will be improved in the future (digging holes)
Post code directly
<?php
namespace Watish WatishWEB Service ;
use Watish Components Attribute Async ;
use Watish Components Attribute Inject ;
use Watish Components Utils Logger ;
class TestService
{
#[Inject(BaseService::class)]
public BaseService $ baseService ;
#[Async]
public function asyncHello (): void
{
Logger:: info ( " Hello " );
}
public function hello ( string $ name ) : string
{
return " hello { $ name }" ;
}
}In Service, dependency injection can still be performed. In addition, the method can be async annotated (note that the method annotated by Async must be of void type) to make it an asynchronous method
Command class files are stored in project /src/Command/
Note: The Command class requires the implement CommandInterface interface
Command classes can only register commands using annotations
The sample code is as follows:
<?php
namespace Watish WatishWEB Command ;
use Watish Components Attribute Command ;
use Watish Components Utils Logger ;
#[Command( " hello " , " command " )]
class HelloCommand implements CommandInterface
{
public function handle (): void
{
Logger:: info ( " Hello " );
}
}The above code can be executed in the following ways
swoole-cli
swoole-cli ./bin/CoServer.php command:helloPHP
php ./bin/CoServer.php command:hello
Annotation of the usage of Command
Command (string $ command , string $ prefix = " command " )Task class is stored in project /src/Task/
Note: All Task classes need to implement TaskInterface
The Task class only supports the registration of timed tasks using Crontab annotation .
The sample code is as follows:
<?php
namespace Watish WatishWEB Task ;
use Watish Components Attribute Crontab ;
use Watish Components Utils Logger ;
#[Crontab( " * * * * * " )]
class HelloTask implements TaskInterface
{
public function execute (): void
{
Logger:: info ( " Hello " , " HelloTask " );
}
}This is a timed task that outputs Hello every second
How to use Crontab annotation
Crontab (string $ rule )where rule is a standard crontab expression
Note: There is only mysql for the time being, redis (can add it yourself)
This framework uses connection pools to maintain mysql and redis connections, and completes the creation of the connection pool at the beginning of the startup. Now it only needs to be used in business logic.
**WatishComponentsIncludesDatabase::mysql() ** Returns an encapsulation of the Laravel query constructor (mainly changing the underlying Pdo logic, no difference in normal use)
WatishComponentsIncludesDatabase::redis() returns a Predis Client
Please configure the database first! Configuration file: project/config/database.php
The framework uses the following components and encapsulates some components
Under the WatishComponentsConstructor namespace, a quick construction of some components is provided
AsyncTaskConstructor::make() asynchronous task delivery
LocalFilesystemConstructor::getFilesystem() Local File System Constructor
ValidatorConstructor::make(array $data, array $rules) Validator constructor
Thanks to the excellent component developers
Test environment: Ubuntu22.0.4 LTS
Test hardware: Virtual Machine (VirtualBox) 6c6t, 8192M, enable virtualization support
Test tool: ApacheBench
This is ApacheBench, Version 2.3 <$Revision: 1879490 $>
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/
Benchmarking 127.0.0.1 (be patient)
Server Software: swoole-http-server
Server Hostname: 127.0.0.1
Server Port: 9502
Document Path: /hello/user/test
Document Length: 20 bytes
Concurrency Level: 3000
Time taken for tests: 2.040 seconds
Complete requests: 30000
Failed requests: 0
Total transferred: 7680000 bytes
HTML transferred: 600000 bytes
Requests per second: 14708.19 [#/sec] (mean)
Time per request: 203.968 [ms] (mean)
Time per request: 0.068 [ms] (mean, across all concurrent requests)
Transfer rate: 3677.05 [Kbytes/sec] received
Connection Times (ms)
min mean[+/-sd] median max
Connect: 0 83 17.3 85 125
Processing: 32 109 41.6 102 380
Waiting: 0 79 40.0 71 362
Total: 107 193 37.8 189 457
Percentage of the requests served within a certain time (ms)
50% 189
66% 200
75% 205
80% 208
90% 224
95% 236
98% 344
99% 389
100% 457 (longest request)
Note: Don't pay too much attention to performance. The real business logic is often complicated. Pressure testing on the demo cannot indicate anything (pictures)
If it is easy to use, you can click a star. If you have any questions, please mention the issue. The author will actively maintain it.
Updated on 2022-12-28 16:01
Please run composer install first