DEV Community

Cover image for Swoft 2.0.3 major update, launching elegant microservices governance
stelin
stelin

Posted on

Swoft 2.0.3 major update, launching elegant microservices governance

What is Swoft?

Swoft is a PHP microservices coroutine framework based on the Swoole 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)

Enhancement

  • Add interface dependency injection(6169f84)
  • Add getFile to obtain information after file upload and save(fe7e3a6)
  • Add restart() to restart service(2ffec37)
  • Add call 1.x RPC service support(30d73c3)
  • Add AOP class name matching supports regular expressions(bc5e479)
  • Add RPC Server /Http server Middleware namespace use error prompt(b1cec04)
  • Add validator exclusion attribute field withunfields(b1bf44f)
  • Add auto write timestamp(dc58011)
  • Add model action event(dc58011)
  • Add database migration script(26bb464)
  • Add entity automatically interact with json and arrays(dc58011)
  • Add model batch update method withbatchUpdateByIds(dc58011)

Fixed:

  • Fix cookies some problems when setting up, add some related methods with withCookie(b05afbb01)
  • Fix no capture processing error when the console uses the coroutine mode to run the command.(8a5418bf)
  • Fix restart websocket server command does not stop the old server problem first(db2d935)
  • Fix task return value is null(a69347c)
  • Fix RPC Server only class middleware can't be used(204bc7f)
  • Fix RPC Server return value is null(4d091be)
  • Fix Logger and CLog log level cannot be overwritten and invalid(8eb8aba)
  • Fix attributes in the model do not support custom expressions(dc58011)

Update:

  • Validator optimization, support for custom validation rules(d959a4f)
  • Rename the error handling management class ErrorHanlders to ErrorManager (f3a8f04b)
  • The exception handling of the console component is changed to the unified processing style provided by the error component. (4f47204)
  • Console component allows to set the disable command group(c5a0269)
  • In the default error handling, the error capture level is allowed to be set. The default level is E_ALL | E_STRICT (afff9029)
  • Optimization When the ws server is started, the http processing function is enabled at the same time, and the information panel adds prompts.(83a81170)
  • Optimize Start the ws server and add the rpc server to start. The information panel does not display the rpc server information.(3d1d0d848)

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

For service registration and discovery, the swoft-consul 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

/**
 * 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

/**
 * 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

/**
 * 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

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.

/**
 * 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 expression language.

/**
 * 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 component provided by Swoft officially, if other third parties are similar.

Declare Agent

/**
 * 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

Top comments (0)