Swoft is a high-performance PHP microservices framework based on swoole coroutine 2.x, with a built-in HTTP server. Framework full coroutine implementation, better performance than the traditional PHP-FPM mode.

  • Swoole is easily extensible
  • Built-in HTTP coroutine server
  • MVC layered design
  • High-performance routing
  • Global container injection
  • The high performance of RPC
  • Services govern circuit breakers, downgrades, loads, registrations, and discovery
  • The RPC service
  • Connection pool Mysql, Redis, RPC
  • Way to automatically reload
  • Powerful log system

Future

  • Event mechanism
  • Connection pool wait queue
  • The alias mechanism
  • Internationalization i18n
  • Restful style
  • Database ORM(automatic validation, model, DB)
  • Crontab Indicates a scheduled task
  • Service monitoring
  • Log statistical analysis
  • Unified configuration center

Update record

  • .
  • 2017-08-15 Reconstructing the Console Command Line
  • 2017-08-24 rewrite IOC container, add controller route annotation registration, no longer rely on PHP-di. When used, re-install composer
  • 2017-08-28 Automatically reload inotify

Quick start

The document

Chinese document

QQ communication group :548173319

Environmental requirements

  1. hiredis
  2. composer
  3. PHP7.X
  4. Way (optional)
  5. Swoole2.x and turn on coroutines and asynchronous Redis

The installation

  • Clone project
  • Composer Install Install dependencies
  • Configuration base. PHP
  • Set the boot parameter swoft. Ini

Start the

The startup service supports both HTTP and TCP startup, as configured in swoft.

Common commands

PHP swoft. PHP swoft. PHP swoft. PHP start -d // restart PHP swoft. PHP restart // reload PHP swoft stopCopy the code

Swoft. Ini parameters

[swoft]
;;;;;;;;;;;;;;;;;;;
; About swoft.ini   ;
;;;;;;;;;;;;;;;;;;;

[server]
pfile = '/tmp/swoft.pid';
pname = "php-swf";

[tcp]
enable = 1;
host = "0.0.0.0"
port = 8099
type = SWOOLE_SOCK_TCP

[http]
host = "0.0.0.0"
port = 80
model = SWOOLE_PROCESS
type = SWOOLE_SOCK_TCP

[setting]
worker_num = 4
max_request = 10000
daemonize = 0;
dispatch_mode = 2
log_file = SWOOLE_LOG_PATHCopy the code

The router

There are two ways of route resolution: registered routes and automatic route resolution. All routes are configured in routes.php. It is recommended that all routes be registered in advance or via annotations. Automatic route resolution is not recommended. Route configuration parameter (base.php):

Return [//... 'router' => ['class' => swoft\web\Router::class, 'ignoreLastSep' => false, /user/index and /user/index/ are two different routes 'tmpCacheNumber' => 1000,// cache number of routes, last 1000 'matchAll' => '', // matchAll, All requests will match this URI or closure], //...] ;Copy the code

Route Registration instance

// Matches the GET request. Closure $router->get('/', function () {$resposne = App::getResponse(); $resposne = App::getResponse(); $resposne->setResponseContent("hello"); $resposne->send(); }); $router->get('/test/{name}', function ($arg) {echo $arg; / / 'John} [' tokens' = > [' name' = > '\ w +', / / add parameters matching limit. If he does not add the corresponding limits, will automatically be set to match any character except '/']]). // This parameter is optional. $router->get('/hello[/{name}]', function ($name = 'No') {echo $name; / / 'John} [' tokens' = > [' name' = > '\ w +', / / add parameters matching limit]]). $router-> POST ('/user/login', function () {$request = App::getRequest(); var_dump($request->getGetParameters(), $request->getPostParameters()); }); $router->map([' GET ', 'POST '], '/user/login', function () {$request = App::getRequest(); var_dump($request->getGetParameters(), $request->getPostParameters()); }); / / allow any request method $router - > any ('/home ', function () {$resposne = RequestContext: : the method getResponse (); $resposne->setResponseContent("hello, you request page is /home"); $resposne->send(); }); $router->any('/404', function () { $resposne = App::getResponse(); $resposne->setResponseContent("Sorry,This page not found."); $resposne->send(); }); $router->group('/user', function ($router) {$router->get('/', function () {$resposne = App::getResponse(); $resposne->setResponseContent("hello. you access: /user/"); $resposne->send(); }); $router->get('/index', function () { $resposne = App::getResponse(); $resposne->setResponseContent("hello. you access: /user/index"); $resposne->send(); }); }); $router->get('/', app\controllers\Home::class); $router->get('/index', 'app\controllers\Home@index'); $router->get('/about', 'app\controllers\Home@about'); // Calling '/home/test' will execute 'app\controllers\ home ::test()' $router->any('/home/{any}', app\controllers\ home ::class); / / can match '/ home', '/ home/test such as $router - > any ('/home / / {name}', app/controllers/home: : class); MatchAll can be configured to intercept all requests in one of two ways. $resposne = App::getResponse(); $resposne = App::getResponse(); $resposne->setResponseContent("System Maintaining ... ..." ); $resposne->send(); },Copy the code

The controller

A class that inherits \swoft\ Web \Controller is both a Controller and a Controller has two ways of annotation automatic registration and manual registration. It is recommended to use annotations for automatic registration, which is convenient and simple to maintain. If the same route is registered several times, the former route will be overwritten by the latter.

Automatic annotation registration

Annotation auto-registration is commonly used with three annotations @autoController, @Inject, and @requestMapping.

@AutoController already uses @AutoController and can no longer use @bean annotations. The @autoController annotation does not need to specify the bean name. The unified class for the bean name @autoController () automatically resolves the Controller prefix by default and uses a hump format. @autoController (prefix=”/demo2″) or @autoController (“/demo2”) have the same function.

Inject the same as before

@requestMapping @requestMapping (route=”/index2″) or @requestMapping (“/index2″) @requestMapping (route=”/index2”, Method =RequestMethod::GET) The registered method does not use @requestMapping or RequestMapping(). By default, the action method is resolved and the route is registered in hump format.

/** * controller demo ** @autoController (prefix="/demo2") ** @uses DemoController * @version 2017 08月22日 * @author stelin <[email protected]> * @copyright Copyright 2010-2016 swoft software * @license PHP Version 7.x {@link http://www.php.net/license/3_0.txt} * / class DemoController extends Controller {/ * * * * * @ Inject injection logic layer () * @ var IndexLogic */ private $logic; /** * define a route that supports get and POST. Uri =/demo2/index * * @requestMapping (route="index", method={RequestMethod::GET, Public function actionIndex() {$this = $this-> GET (); $this->get('name', 'defaultName'); $this->get('name', 'name'); $POST = $this-> POST (); $this->post('name', 'defaultName'); $request = $this->request(); $this->request('name', 'defaultName'); $this->request('name', 'name'); $this->outputJson($this->outputJson); } /** * define a route, support get, start with "/" definition, direct root path, Uri =/index2 * * @requestMapping (route="/index2" Public function actionIndex2() {$this->redirect("/login/index"); $this->redirect("/login/index"); Public function actionIndex3() {$this->outputJson("suc3"); $this->outputJson("suc3"); }}Copy the code

Manually register

Manual registration of @bean, @inject annotations, manual registration of the additional step is to register their own routing rules in routes.php.

Manual registration of @bean () can only be done by default. And you can’t use the @AutoController annotation

/** * controller demo ** @bean () ** @uses DemoController * @version August 22, 2017 * @author stelin <[email protected]> * @copyright Copyright 2010-2016 swoft software * @license PHP Version 7.x {@link http://www.php.net/license/3_0.txt} */ Class DemoController extends Controller {/** * Inject logical layer ** @inject () * @var IndexLogic */ private $logic; Public function actionIndex() {$GET = $this-> GET (); $this->get('name', 'defaultName'); $this->get('name', 'name'); $POST = $this-> POST (); $this->post('name', 'defaultName'); $request = $this->request(); $this->request('name', 'defaultName'); $this->request('name', 'name'); $this->outputJson($this->outputJson); } /** * uri=/index2 */ public function actionIndex2() {$this->redirect("/login/index"); $this->outputJson("suc2"); } /** */ public function actionIndex3() { $this->outputJson("suc3"); }}Copy the code

Routes.php Register routes manually:

// ...

$router->map(['get', 'post'], '/demo2/index', 'app\controllers\DemoController@index');
$router->get('/index2', 'app\controllers\DemoController@index2');
$router->get('/demo2/index3', 'app\controllers\DemoController@index3');Copy the code

The connection pool

The connection pool is easy to use. You only need to configure the corresponding service connection pool in base.php.

Return "packer" => ['class' => JsonPacker::class], // Service discovery bean, 'consulProvider' => ['class' => swoft service\ consulProvider ::class] / / user service connection pool "userPool" = > [" class = "> \ swoft \ pool \ ServicePool: : class," uri "= > '127.0.0.1:8099127.00 0.1:8099', // useProvider is false. "MaxIdel" => 6, "maxActive" => 10, "maxWait" => 20, "timeout" => '${config. Service. The user. The timeout}', / / reference properties. The PHP configuration values "balancer" = > '${randomBalancer}', / / connections to create load "serviceName = >" 'user',// service name, The name of the connection pool must be in the format of xxxPool/xxxBreaker "useProvider" => false and 'serviceProvider' => '${consulProvider}'. For discovering service], // User service fuse "userBreaker" => ['class' => \swoft\ Circuit \CircuitBreaker::class, 'delaySwithTimer' => 8000], / /... ;Copy the code

The cache

Cache is currently only supported by Redis, which uses two methods: direct call and deferred call.

// call RedisClient::set('name', 'redis client stelin', 180); $name = RedisClient::get('name'); RedisClient::get($name); $ret = RedisClient::deferCall('get', ['name']); $ret2 = RedisClient::deferCall('get', ['name']); $result = $ret->getResult(); $result2 = $ret2->getResult(); $data = [ 'redis' => $name, 'defer' => $result, 'defer2' => $result2, ];Copy the code

RPC calls

RPC and internal services are implemented by listening on TCP ports. The TCP listening port information is configured in swoft. RPC call internal implementation of connection pool, fuse, service registration and discovery.

$result = Service::call("user", 'user ::getUserInfo', [2,6,8]); $result = Service::call("user",' user ::getUserInfo', [2,6,8]); $res = Service::deferCall("user", 'user ::getUserInfo', [3,6,9]); $res2 = Service::deferCall("user", 'user ::getUserInfo', [3,6,9]); $users = $res->getResult(); $users2 = $res2->getResult(); $deferRet = $users; $deferRet2 = $users2;Copy the code

HttpClient

The system provides HttpClient to implement HTTP calls, currently there are two ways, direct call and delayed packet call, delayed packet call, generally used for concurrent calls.

/ / direct call $requestData = [' name '= >' boy ', 'desc' = > 'PHP']. $result = HttpClient: : call (" http://127.0.0.1/index/post? a=b", HttpClient::GET, $requestData); $result = $result; / / delay call way two requests concurrent invocations $ret = HttpClient: : deferCall (" http://127.0.0.1/index/post ", HttpClient: : POST, $requestData); $ret2 = HttpClient: : deferCall (" http://127.0.0.1/index/post ", HttpClient: : POST, $requestData); $defRet1 = $ret->getResult(); $defRet2 = $ret->getResult();Copy the code

The log

Logs record the analysis of common user questions and system location. Currently, logs are planned at the debug Trace Error Info warning Notice level. Each user level records information of varying importance. A notice is generated for each request, and all logs generated by a request have the same loGID. The notice records the details of the request, such as the total URI time, cache time, db operation time, etc.

// Tag start App::profileStart("tag"); App::error(new \Exception("error Exception ")); App::error("this errro log"); App::info("this errro log"); // array App::error(['name' => 'boy']); App::debug("this errro log"); // Tag end App::profileEnd("tag"); App::counting("cache", 1, 10);Copy the code