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