Swoft 2.0.3 major update, launching elegant microservices governance
Tags: php, java, go
Published: 2019-07-09 03:56:28

![](https://thepracticaldev.s3.amazonaws.com/i/bw76hz9cgelzhsk96hh5.png)

## What is Swoft?

[Swoft](https://github.com/swoft-cloud/swoft) is a PHP microservices coroutine framework based on the [Swoole](https://github.com/swoole/swoole-src) extension. Like Go, Swoft has a built-in coroutine web server and a common coroutine client and is resident in memory, independent of traditional PHP-FPM. There are similar Go language operations, similar to the Spring Cloud framework flexible annotations, powerful global dependency injection container, comprehensive service governance, flexible and powerful AOP, standard PSR specification implementation and so on.

Through three years of accumulation and direction exploration, Swoft has made Swoft the Spring Cloud in the PHP world, which is the best choice for PHP's high-performance framework and microservices management.

## Update

Remove
- Remove `request->json()` method([c9e8f04](https://github.com/swoft-cloud/swoft-component/pull/455/commits/c9e8f048217670fd975e70d4eee1d8d3b2fccd7e))

Enhancement
- Add interface dependency injection([6169f84](https://github.com/swoft-cloud/swoft-component/pull/442/commits/6169f84ffd32622c4d3bb21f6ab3c69ef04746e2))
- Add `getFile` to obtain information after file upload and save([fe7e3a6](https://github.com/swoft-cloud/swoft-component/commit/fe7e3a69f215a92af83c5837d32b72a5c181383a))
- Add `restart()` to restart service([2ffec37](https://github.com/swoft-cloud/swoft-component/pull/453/commits/2ffec37568618cc49c038f96a901cbb339769623))
- Add call 1.x RPC service support([30d73c3](https://github.com/swoft-cloud/swoft-component/pull/455/commits/30d73c3bcef6294d5985ce5af316e7aa6e86c0e5))
- Add AOP class name matching supports regular expressions([bc5e479](https://github.com/swoft-cloud/swoft-component/pull/455/commits/bc5e47951d1426e6944b934e6c8eb5db5657f8a4))
- Add RPC Server /Http server Middleware namespace `use` error prompt([b1cec04](https://github.com/swoft-cloud/swoft-component/pull/455/commits/b1cec041ea402fe1c837d6755eff6efbfcef3681))
- Add validator exclusion attribute field with`unfields`([b1bf44f](https://github.com/swoft-cloud/swoft-component/pull/459/commits/b1bf44f62ae096674f2c413e9630964782b14d3f))
- Add auto write timestamp([dc58011](https://github.com/swoft-cloud/swoft-component/pull/457/commits/dc58011cfa90996e6e319365fc738c1cd386f08d))
- Add model action event([dc58011](https://github.com/swoft-cloud/swoft-component/pull/457/commits/dc58011cfa90996e6e319365fc738c1cd386f08d))
- Add database migration script([26bb464](https://github.com/swoft-cloud/swoft-ext/commit/26bb464ba61e43b6bbae9ba191a8e69525ad7dc5))
- Add entity automatically interact with json and arrays([dc58011](https://github.com/swoft-cloud/swoft-component/pull/457/commits/dc58011cfa90996e6e319365fc738c1cd386f08d))
- Add model batch update method with`batchUpdateByIds`([dc58011](https://github.com/swoft-cloud/swoft-component/pull/457/commits/dc58011cfa90996e6e319365fc738c1cd386f08d))


Fixed：
- Fix cookies some problems when setting up, add some related methods with withCookie([b05afbb01](https://github.com/swoft-cloud/swoft-component/pull/443/commits/b05afbb011457d217bb3a40c6a3639c28b0eef18))
- Fix no capture processing error when the console uses the coroutine mode to run the command.([8a5418bf](https://github.com/swoft-cloud/swoft-component/pull/443/commits/8a5418bf25a4d4797d99281bf7db8881b7ca43ed))
- Fix restart websocket server command does not stop the old server problem first([db2d935](https://github.com/swoft-cloud/swoft-component/pull/443/commits/db2d935542c715bbe1ed4b3ed822b2d946317b6f))
- Fix task return value is `null`([a69347c](https://github.com/swoft-cloud/swoft-component/pull/455/commits/a69347c8ce486102e23bb82d70cfef63573a94eb)) 
- Fix RPC Server only class middleware can't be used([204bc7f](https://github.com/swoft-cloud/swoft-component/pull/455/commits/204bc7f4f23a26f75534ac808e2f9eb05a0118a2))
- Fix RPC Server return value is `null`([4d091be](https://github.com/swoft-cloud/swoft-component/pull/455/commits/4d091bef0ece773bcd382a750de01eef830f2949))
- Fix Logger and CLog log level cannot be overwritten and invalid([8eb8aba](https://github.com/swoft-cloud/swoft-component/pull/459/commits/8eb8aba2313a45191318b52a3698135d92cb3e71))
- Fix attributes in the model do not support custom expressions([dc58011](https://github.com/swoft-cloud/swoft-component/pull/457/commits/dc58011cfa90996e6e319365fc738c1cd386f08d))

Update:

- Validator optimization, support for custom validation rules([d959a4f](https://github.com/swoft-cloud/swoft-component/pull/442/commits/d959a4f5facfc24070fbde547f0557213e5184ee))
- Rename the error handling management class `ErrorHanlders` to `ErrorManager` ([f3a8f04b](https://github.com/swoft-cloud/swoft-component/pull/443/commits/f3a8f04ba6d83874b772a419fe29e5449b3dcea9))
- The exception handling of the console component is changed to the unified processing style provided by the error component. ([4f47204](https://github.com/swoft-cloud/swoft-component/pull/443/commits/4f472045e3d80165145907331386f46a2912b2e3))
- Console component allows to set the disable command group([c5a0269](https://github.com/swoft-cloud/swoft-component/pull/454/commits/c5a0269f26ae6b9739401393a48fd3fd91280bbb))
- In the default error handling, the error capture level is allowed to be set. The default level is `E_ALL | E_STRICT` ([afff9029](https://github.com/swoft-cloud/swoft-component/pull/443/commits/afff90296d80085acd30b951441774049d0ca2e7))
- Optimization When the ws server is started, the http processing function is enabled at the same time, and the information panel adds prompts.([83a81170](https://github.com/swoft-cloud/swoft-component/pull/456/commits/83a8117047036da5687fa6dc5236301716dfd59f))
- Optimize Start the ws server and add the rpc server to start. The information panel does not display the rpc server information.([3d1d0d848](https://github.com/swoft-cloud/swoft-component/pull/454/commits/3d1d0d848a8f5ca31a09feb5ee860079f7c018e4))

Extra:

- Document add support search by google
- New apollo Component
- New consul Component
- New breaker Component
- New limter component


## Elegant service governance
Swoft officially recommends that developers use service mesh patterns, such as the Istio/Envoy framework, to separate business and service governance, but Swoft also provides a set of microservices components for small and medium-sized businesses to quickly build microservices.

- [Service registration and discovery](https://en.swoft.org/docs/2.x/en/ms/govern/register-discovery.html)
- [Service breaker](https://en.swoft.org/docs/2.x/en/ms/govern/breaker.html)
- [Service limiter](https://en.swoft.org/docs/2.x/en/ms/govern/limiter.html)
- [Configuration center](https://en.swoft.org/docs/2.x/en/ms/govern/config.html)

### Service registration and discovery

For service registration and discovery, the [swoft-consul](https://en.swoft.org/docs/2.x/en/extra/consul.html) component provided by Swoft is required, if other third parties are similar.
#### Registration and cancellation services

Listen for the `SwooleEvent::START` event, register the service
```php
/**
 * Class RegisterServiceListener
 *
 * @since 2.0
 *
 * @Listener(event=SwooleEvent::START)
 */
class RegisterServiceListener implements EventHandlerInterface
{
    /**
     * @Inject()
     *
     * @var Agent
     */
    private $agent;

    /**
     * @param EventInterface $event
     */
    public function handle(EventInterface $event): void
    {
        /* @var HttpServer $httpServer */
        $httpServer = $event->getTarget();

        $service = [
            // ....
        ];

        $scheduler = Swoole\Coroutine\Scheduler();
        $scheduler->add(function () use ($service) {
            // Register
            $this->agent->registerService($service);
            CLog::info('Swoft http register service success by consul!');
        });
        $scheduler->start();
    }
}
```

Listen for the `SwooleEvent::SHUTDOWN` event, cancel the service
```php
/**
 * Class DeregisterServiceListener
 *
 * @since 2.0
 *
 * @Listener(SwooleEvent::SHUTDOWN)
 */
class DeregisterServiceListener implements EventHandlerInterface
{
    /**
     * @Inject()
     *
     * @var Agent
     */
    private $agent;

    /**
     * @param EventInterface $event
     */
    public function handle(EventInterface $event): void
    {
        /* @var HttpServer $httpServer */
        $httpServer = $event->getTarget();

        $scheduler = Swoole\Coroutine\Scheduler();
        $scheduler->add(function () use ($httpServer) {
            $this->agent->deregisterService('swoft');
        });
        $scheduler->start();
    }
}    
```
#### Service discovery
Defining a service provider
```php
/**
 * Class RpcProvider
 *
 * @since 2.0
 *        
 * @Bean()
 */
class RpcProvider implements ProviderInterface
{
    /**
     * @Inject()
     *
     * @var Agent
     */
    private $agent;

    /**
     * @param Client $client
     *
     * @return array
     * @example
     * [
     *     'host:port'
     * ]
     */
    public function getList(Client $client): array
    {
        // Get health service from consul
        $services = $this->agent->services();

        $services = [

        ];

        return $services;
    }
}
```

Configuration service provider
```php
return [
    'user'           => [
      'class'   => ServiceClient::class,
      'provider' => bean(RpcProvider::class)
      // ...
    ]
];
```
### Service breaker

Swoft uses the `@Breaker` annotation to achieve a blow, which can be blown on any method.
```php
/**
 * Class BreakerLogic
 *
 * @since 2.0
 *
 * @Bean()
 */
class BreakerLogic
{
    /**
     * @Breaker(fallback="funcFallback")
     *
     * @return string
     * @throws Exception
     */
    public function func(): string
    {
        // Do something

        throw new Exception('Breaker exception');
    }
    
    /**
     * @return string
     */
    public function funcFallback(): string
    {
        return 'funcFallback';
    }
}
```
### Service limit
Swoft uses the `@RateLimiter` annotation to implement service throttling, which can be throttled on any method, not just the controller, and KEY also supports [symfony/expression-language](https://symfony.com/doc/current/components/expression_language/syntax.html#component-expression-objects) expression language.
```php
/**
 * Class LimiterController
 *
 * @since 2.0
 *
 * @Controller(prefix="limiter")
 */
class LimiterController
{
    /**
     * @RequestMapping()
     * @RateLimiter(key="request.getUriPath()", fallback="limiterFallback")
     *
     * @param Request $request
     *
     * @return array
     */
    public function requestLimiter(Request $request): array
    {
        $uri = $request->getUriPath();
        return ['requestLimiter', $uri];
    }

    /**
     * @param Request $request
     *
     * @return array
     */
    public function limiterFallback(Request $request): array
    {
        $uri = $request->getUriPath();
        return ['limiterFallback', $uri];
    }
}
```
### Configuration center
The configuration center needs to use the [Swoft-apollo](https://en.swoft.org/docs/2.x/en/extra/apollo.html) component provided by Swoft officially, if other third parties are similar.

#### Declare Agent

```php
/**
 * Class AgentCommand
 *
 * @since 2.0
 *
 * @Command("agent")
 */
class AgentCommand
{
    /**
     * @Inject()
     *
     * @var Config
     */
    private $config;

    /**
     * @CommandMapping(name="index")
     */
    public function index(): void
    {
        $namespaces = [
            'application'
        ];

        while (true) {
            try {
                $this->config->listen($namespaces, [$this, 'updateConfigFile']);
            } catch (Throwable $e) {
                CLog::error('Config agent fail(%s %s %d)!', $e->getMessage(), $e->getFile(), $e->getLine());
            }
        }
    }

    /**
     * @param array $data
     *
     * @throws ContainerException
     * @throws ReflectionException
     */
    public function updateConfigFile(array $data): void
    {
        foreach ($data as $namespace => $namespaceData) {
            $configFile = sprintf('@config/%s.php', $namespace);

            $configKVs = $namespaceData['configurations'] ?? '';
            $content   = '<?php return ' . var_export($configKVs, true) . ';';
            Co::writeFile(alias($configFile), $content, FILE_NO_DEFAULT_CONTEXT);

            CLog::info('Apollo update success！');

            /** @var HttpServer $server */
            $server = bean('httpServer');
            $server->restart();
        }
    }
}
```

#### Start Agent

The Agent only needs to run before the service (Http/RPC/Websocket) is started.
```
php bin/swoft agent:index
```
## Resource
- GitHub: [https://github.com/swoft-cloud/swoft](https://github.com/swoft-cloud/swoft)
- Website：[https://en.swoft.org](https://en.swoft.org)
- Document：[https://en.swoft.org/docs](https://en.swoft.org/docs)
- Discuss：[swoft-cloud/community](https://gitter.im/swoft-cloud/community)