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.

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