Comments (4)
Forcibly killing the process will not trigger onShutdown, such as kill -9
Use kill -15 to send the SIGTERM signal to the main process in order to terminate according to the normal process flow
Pressing Ctrl+C in the command line will immediately stop the program, and onShutdown will not be called at the lower level
see https://wiki.swoole.com/en/#/server/events?id=onshutdown
from swoole-src.
I understand, however I signal the master/first process using
kill -QUIT
but but what I then see is that the signal handler is being called (which then converts the signal to SIGTERM) but if you look closely on the output:
Shutting down II ...
XShutting down II ...
XKilled
You may see that the shutdown callback is not getting called even if you send SIGTERM
from swoole-src.
Certain signals, like SIGTERM and SIGALRM, cannot be set as monitored signals in Swoole\Server
from swoole-src.
I got it to work like this:
<?php
require __DIR__ . '/vendor/autoload.php';
use Xin\Swoole\Rpc\Server;
use Xin\Cli\Color;
use Xin\Swoole\Rpc\Handler\Handler;
use Xin\Swoole\Rpc\Enum;
use Xin\Swoole\Rpc\LoggerInterface;
/////////////////////////////////////////////////////////////////////////
define('RPCSERVER_RUN_AS_USER', 'henrik');
define('RPCSERVER_PORT', 11520);
/////////////////////////////////////////////////////////////////////////
$workerPID = new Swoole\Atomic(0);
/////////////////////////////////////////////////////////////////////////
function me() : string {
return basename($_SERVER['argv'][0], (defined('EXT')) ? '.'.EXT : '.php');
}
function logger(string $msg) : void {
echo $msg.PHP_EOL;
}
/////////////////////////////////////////////////////////////////////////
class RPCMethodDocumentation {
public function __construct(public string $handlerName, private \ReflectionMethod $method) {}
public function __toString() {
$comment = $this->method->getDocComment();
if (empty($comment)) $comment = '/**'.PHP_EOL.' * <NO DESCRIPTION>'.PHP_EOL.' */';
$comment = str_replace("\t", "", $comment);
$type = $this->method->getReturnType();
$type = ($type === NULL) ? 'void' : (string) $type;
$out = $comment.PHP_EOL.$this->method->name.'('.$this->renderParams().') : '.$type.PHP_EOL.PHP_EOL;
return $out;
}
private function renderParams() {
$out = [];
foreach($this->method->getParameters() as $refParam) {
$aNull = ($refParam->allowsNull()) ? '?' : '';
$str = $aNull.$refParam->getType().' $'.$refParam->getName();
if ($default = $refParam->getDefaultValue()) {
$str.= ' = '.$default;
}
$out[] = $str;
}
return implode(', ', $out);
}
}
/////////////////////////////////////////////////////////////////////////
function scanForRPCHandlers() : array {
$out = [];
$classes = get_declared_classes();
foreach($classes as $class) {
$c = new \ReflectionClass($class);
if ($c->isSubclassOf(Handler::class)) {
$handlerName = $class;
$methods = $c->getMethods(\ReflectionMethod::IS_PUBLIC);
foreach($methods as $m) {
if ($m->name == '__construct') continue;
if (! isset($out[$handlerName])) {
$out[$handlerName] = [];
}
$out[$handlerName][$m->name] = new RPCMethodDocumentation($handlerName, $m);
}
}
}
return $out;
}
///////////////////////////////////////////////////////////////////////////////////////////
class ElyRPCServer extends \Xin\Swoole\Rpc\Server {
private static $server;
public function serve($host, $port, $config = []) {
if (!extension_loaded('swoole')) {
echo Color::error('The swoole extension is not installed');
return;
}
if (!extension_loaded('signal_handler')) {
echo Color::error('The signal_handler extension is not installed');
return;
}
$this->host = $host;
$this->port = intval($port);
$this->config = $config;
set_time_limit(0);
$server = new \Swoole\Server($this->host, $this->port, SWOOLE_BASE);
$server->set($config);
$server->on('receive', [$this, 'receive']);
$server->on('workerStart', [$this, 'workerStart']);
$this->beforeServerStart($server);
$server->start();
}
public static function shutdownServer() : void {
global $workerPID;
if (getmypid() === $workerPID->get()) return;
$pidFile = '/var/run/'.me().'/'.me().'.pid';
if (file_exists($pidFile)) @unlink($pidFile);
// Uinregister here with WSSERVER
/*
go(function () {
try {
$p = (int) constant(strtoupper(me()).'_PORT');
[$ip, $port] = explode(':', COMMON_WSSERVER_ADDRESS);
$client = new HttpClient($ip, $port);
$client->set(['timeout' => 3]);
$client->upgrade('/');
$ipAndPort = '-'.getLocalIP().':'.$p.'='.me().'-';
$client->push($ipAndPort);
$reply = $client->recv()->data;
logger(sprintf("De-registered at '%s' - Reply was: '%s' ", COMMON_WSSERVER_ADDRESS, $reply));
$client->close();
} catch(Exception) {}
});
*/
$unixSocket = '/var/run/'.me().'.sock';
if (file_exists($unixSocket)) @unlink($unixSocket);
logger('Shutting down ...');
closelog();
}
public function workerStart($server, $workerId) {
global $workerPID;
swoole_set_process_name(me().' [rpc] [worker]');
$workerPID->set(getmypid());
}
public function receive($server, $fd, $reactor_id, $data) {
$data = trim($data);
if ($this->debug) {
echo Color::colorize("fd:{$fd} data:{$data}", Color::FG_LIGHT_GREEN) . PHP_EOL;
}
try {
$data = json_decode($data, true);
$service = $data[Enum::SERVICE];
$method = $data[Enum::METHOD];
$arguments = $data[Enum::ARGUMENTS];
if (!isset($this->services[$service])) {
throw new RpcException('The service handler does not exist!');
}
$ref = new ReflectionClass($this->services[$service]);
$handler = $ref->newInstance($server, $fd, $reactor_id);
$result = $handler->$method(...$arguments);
$response = $this->success($result);
$server->send($fd, json_encode($response));
if ($this->logger && $this->logger instanceof LoggerInterface) {
$this->logger->info($data, $response);
}
} catch (\Exception $ex) {
$response = $this->fail($ex->getCode(), $ex->getMessage());
$server->send($fd, json_encode($response));
if ($this->logger && $this->logger instanceof LoggerInterface) {
$this->logger->error($data, $response, $ex);
}
}
}
public function beforeServerStart($server) {
swoole_set_process_name(me().' [rpc] [master]');
if (posix_isatty(\STDOUT)) {
$me = me();
echo Color::colorize("-----------------------------------------------------------------------------------", Color::FG_LIGHT_GREEN) . PHP_EOL;
echo Color::colorize(" {$me} :: RPC Server - Listening on TCP {$this->port} ", Color::FG_CYAN , Color::FG_LIGHT_GREEN) . PHP_EOL;
echo Color::colorize("-----------------------------------------------------------------------------------", Color::FG_LIGHT_GREEN) . PHP_EOL;
}
}
}
class RPCLogger implements LoggerInterface {
public function info($request, $response) {
var_dump($request, $response);
$info = '';
// TODO
logger('INFO [ RPC ] :: '.$info);
}
public function error($request, $response, Exception $ex) {
// TODO
}
}
/////////////////////////////////////////////////////////////////////////////////////////
class ElyRPCHandler extends Handler {
public function __construct($server, $fd, $reactorId) {
$this->server = $server;
$this->fd = $fd;
$this->reactorId = $reactorId;
}
}
/////////////////////////////////////////////////////////////////////////////////////////
class TestHandler extends ElyRPCHandler {
/**
* Returns 'success' !
*/
public function test() : string {
return 'success';
}
public function test2(array $foo = []) {
return array_reverse($foo);
}
private function foobar() {
return 'HELLO';
}
}
/////////////////////////////////////////////////////////////////////////////////////////
$handlers = scanForRPCHandlers();
//var_dump($handlers);
foreach($handlers as $handler => $methods) {
echo $handler.PHP_EOL;
echo "------------------------------------".PHP_EOL;
foreach($methods as $method) {
echo (string) $method.PHP_EOL;
}
}
if (posix_getuid() !== 0) {
echo "Error: Must be started as root\n";
die(1);
}
$daemonize = TRUE;
$rpcDebug = FALSE;
$rpcService = 'test';
$rpcServiceClass = TestHandler::class;
$server = new ElyRPCServer();
$server->logger = new \RPCLogger();
$server->setDebug($rpcDebug);
$server->setHandler($rpcService, $rpcServiceClass);
if (! $daemonize) {
attach_signal(SIGINT, function() {
ElyRPCServer::shutdownServer();
sleep(1);
posix_kill(getmypid(), SIGKILL);
});
} else {
attach_signal(SIGTERM, function() {
global $workerPID;
ElyRPCServer::shutdownServer();
sleep(1);
posix_kill($workerPID->get(), SIGKILL);
posix_kill(getmypid(), SIGKILL);
});
attach_signal(SIGQUIT, function() {
global $workerPID;
ElyRPCServer::shutdownServer();
sleep(1);
posix_kill($workerPID->get(), SIGKILL);
posix_kill(getmypid(), SIGKILL);
});
}
$userInfo=posix_getpwnam(constant(strtoupper(me()).'_RUN_AS_USER'));
$userID=$userInfo["uid"];
$pidFile = '/var/run/'.me().'/'.me().'.pid';
@mkdir(dirname($pidFile), 0777);
chmod(dirname($pidFile), 0777);
chown(dirname($pidFile), $userID);
touch($pidFile);
chmod($pidFile, 0777);
chown($pidFile, $userID);
// TODO: Log file
// Become the proper user
posix_setuid($userID);
$port = (int) constant(strtoupper(me()).'_PORT');
// Start server
$server->serve('0.0.0.0', $port, [
'log_file' => $logFile,
'pid_file' => $pidFile,
'daemonize' => $daemonize,
'max_request' => 500, // Total number of requests to be processed
'open_eof_check' => TRUE,
'package_eof' => "\r\n",
]);
from swoole-src.
Related Issues (20)
- windows swoole-cli-v4.8.13-cygwin-x64.zip 在windows上 run(function{}) 内的curl 问题 HOT 2
- 在 Swoole\Coroutine\Http\Server 中的 http server 使用 sendfile 在高并发时会导致 PHP 假死并且内存泄露 HOT 57
- 🪶 在 C++ 中,有提供了Socket sendfile的方法(zerocopy),可以不通过CPU去调度资源,避免重复拷贝两次DMZ(或者更多) HOT 15
- windows swoole-cli-v4.8.13-cygwin-x64 使用协程监听的Channel数据然后插入数据库,插入没几条就报错了! HOT 6
- syntax error near unexpected token `SQLITE,' HOT 4
- @swoole/library/ext/sockets.php(217) E_DEPRECATED:Creation of dynamic property Swoole\Coroutine\Socket::$__ext_sockets_nonblock is deprecated HOT 3
- MACOS M1 SIGRTMIN HOT 2
- swoole:5.1.2-php8.3 - Maximum call stack size of 8339456 bytes (zend.max_allowed_stack_size - zend.reserved_stack_size) reached. HOT 4
- 编译swoole所用libcurl头文件和PHP 8.3所用版本不同导致Segmentation fault HOT 5
- tracker现在没了吗 HOT 1
- swoole-cli 运行项目时如何使用xlswriter HOT 2
- 面向aop开发 HOT 4
- server.pid file is not created HOT 2
- free(): invalid pointer when curl support is on HOT 10
- Lines to stderr gets tangled
- WebSocket: After calling $webSockerServer->push() from within onMessage event, the WebSocket connection closes by calling onClose event. HOT 8
- cancel coroutine ! HOT 5
- WebSocket: $this->socket->recv(); on client-side ends up with warning and websocket connection auto disconnects. HOT 1
- Can not install Swoole v5.1.3 due to unavailability of package "libbrotlienc" on Ubuntu 22.04. The package is also not locatable in Ubuntu Repos. HOT 4
- While installing Swoole, i get warning "configure: WARNING: unrecognized options: --enable-http2, --enable-swoole-json, --with-postgres" HOT 2
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from swoole-src.