diff --git a/CHANGELOG.md b/CHANGELOG.md index 9ab6ec6..f0cc541 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -39,6 +39,7 @@ - **Email:** Fix Email Can't Send - **Error:** Fix Error Handler - **Invite:** Add Fine-grained control of recycle pending +- **Links:** Remove extra meta section - **Register:** Add captcha checker - **TorrentUpload:** Fix length 0 file cause ParseErrorException - **Tracker:** Add miss port check for field ipv6_port @@ -59,6 +60,7 @@ - **Redis:** Add more Redis arguments in debug output - **Session:** Add Session Format Docs - **SiteConfig:** change namespace `authority.route_` to `route.` +- **Tracker:** Separate announce data update function - **Validator:** Add function buildDefaultValue() - **ext2Icon:** Add more File format diff --git a/README.md b/README.md index cbc6ca7..62aa8c3 100644 --- a/README.md +++ b/README.md @@ -88,7 +88,9 @@ So that tracker can record the peer's ip address. 7. Use the default `php bin/rid-httpd service start -d` to let *RidPT* RUN in the background. Or you can use other daemon work like: - Systemctl: [ridpt.service](migration/ridpt.service) -## Basie Environment in `.env` +## Basie Environment Setting in `.env` + +> Notice: Any change in file `.env` require the restart of Application to Make it effective ### Section `APP` diff --git a/apps/config/http_base.php b/apps/config/http_base.php index 7212859..fa4bbed 100644 --- a/apps/config/http_base.php +++ b/apps/config/http_base.php @@ -215,6 +215,12 @@ 'type' => \Rid\Base\Timer::TICK, 'msec' => 5 * 60 * 1000, // TODO 单位为毫秒,应该为所有contab worker的最小公倍数(应该在面板有所提醒) 'callback' => 'init' + ], + 'tracker' => [ + 'class' => \apps\task\TrackerAnnounceTimer::class, + 'type' => \Rid\Base\Timer::AFTER, + 'msec' => 10 * 1000, + 'callback' => 'init' ] ], diff --git a/apps/config/httpd.php b/apps/config/httpd.php index b003406..17cc6df 100644 --- a/apps/config/httpd.php +++ b/apps/config/httpd.php @@ -63,7 +63,6 @@ 'enable_coroutine' => false, // 开启协程 'reactor_num' => 1, // 连接处理线程数 'worker_num' => 5, // 工作进程数 - 'task_worker_num' => 10, // Task进程数 'pid_file' => '/var/run/rid-httpd.pid', // PID 文件 'log_file' => '/tmp/rid-httpd.log', // 日志文件路径 'max_request' => 3000, // 进程的最大任务数 diff --git a/apps/controllers/TrackerController.php b/apps/controllers/TrackerController.php index 71ecb77..2668012 100644 --- a/apps/controllers/TrackerController.php +++ b/apps/controllers/TrackerController.php @@ -775,9 +775,13 @@ private function checkSession($queries, $seeder, $userInfo, $torrentInfo) private function sendToTaskWorker($queries, $role, $userInfo, $torrentInfo) { - // Push to Redis Queue and quick response - app()->redis->lPush('Tracker:to_deal_queue', json_encode([ - 'worker' => \apps\task\TrackerAnnounceTask::class, + /** + * Push to Redis Queue and quick response + * + * Don't use json_{encode,decode} for the value of info_hash and peer_id will make + * those function return FALSE + */ + return app()->redis->lPush('Tracker:to_deal_queue', serialize([ 'timestamp' => time(), 'queries' => $queries, 'role' => $role, diff --git a/apps/public/static/css/main.css b/apps/public/static/css/main.css index 84f7729..d5adbde 100644 --- a/apps/public/static/css/main.css +++ b/apps/public/static/css/main.css @@ -61,7 +61,7 @@ body{background-color:#f6f6f6} .color-rss{color:#5c7cfa} /* Main Container */ -.main-container{background-color:#eaeaea;padding:10px} +.main-container{background-color:#eaeaea;padding:20px 10px} /* Footer Menu */ #footer_menu{background-color:#383838;color:#C3C0B9;font-size:12px;margin-top:30px;padding-bottom:10px;padding-top:15px} diff --git a/apps/task/CronTabTimer.php b/apps/task/CronTabTimer.php index 309b081..8c2a5d4 100644 --- a/apps/task/CronTabTimer.php +++ b/apps/task/CronTabTimer.php @@ -12,9 +12,9 @@ class CronTabTimer extends Timer { - + private $_print_flag = 1; // FIXME debug model on - + private function print_log($log) { $this->_print_flag = $this->_print_flag ?? config('debug.print_crontab_log'); @@ -41,7 +41,7 @@ public function init() $job_start_time = time(); $this->{$job['job']}($job); $job_end_time = time(); - $hit ++; + $hit++; // Update the run information app()->pdo->createCommand('UPDATE `site_crontab` set last_run_at= NOW() , next_run_at= DATE_ADD(NOW(), interval job_interval second) WHERE id=:id')->bindParams([ @@ -66,7 +66,7 @@ public function init() } } $end_time = time(); - $this->print_log('This Cron Work period Start At ' . $start_time.', Cost Time: ' . number_format($start_time - $end_time, 10) . 's, With ' . $hit . 'Jobs hits.'); + $this->print_log('This Cron Work period Start At ' . $start_time . ', Cost Time: ' . number_format($start_time - $end_time, 10) . 's, With ' . $hit . ' Jobs hits.'); } protected function clean_dead_peer() @@ -79,7 +79,8 @@ protected function clean_dead_peer() $this->print_log('Success clean ' . $affect_peer_count . ' peers from our peer list'); } - protected function clean_expired_session() { + protected function clean_expired_session() + { $timenow = time(); $expired_sessions = app()->redis->zRangeByScore('Site:Sessions:to_expire', 0, $timenow); @@ -90,16 +91,14 @@ protected function clean_expired_session() { } $clean_record_count = app()->redis->zRemRangeByScore('Site:Sessions:to_expire', 0, $timenow); - $this->print_log('Success clean expired Sessions: Database(' . count($expired_sessions) .'), Redis(' . $clean_record_count .').'); + $this->print_log('Success clean expired Sessions: Database(' . count($expired_sessions) . '), Redis(' . $clean_record_count . ').'); } // TODO sync sessions from database to redis to avoid lost (Maybe)... - - protected function expired_invitee () { + protected function expired_invitee() + { app()->pdo->createCommand('UPDATE `invite` SET `used` = -1 WHERE `expire_at` < NOW() AND `used` = 0')->execute(); - - $count = app()->pdo->getRowCount(); $this->print_log('Success Expired ' . $count . ' invites'); } diff --git a/apps/task/TrackerAnnounceTask.php b/apps/task/TrackerAnnounceTimer.php similarity index 92% rename from apps/task/TrackerAnnounceTask.php rename to apps/task/TrackerAnnounceTimer.php index aff48e6..2318c0a 100644 --- a/apps/task/TrackerAnnounceTask.php +++ b/apps/task/TrackerAnnounceTimer.php @@ -2,40 +2,44 @@ /** * Created by PhpStorm. * User: Rhilip - * Date: 2019/3/17 - * Time: 13:52 + * Date: 2019/6/28 + * Time: 9:46 */ namespace apps\task; -use Rid\Base\TaskInterface; -use apps\exceptions\TrackerException; +use Rid\Base\Timer; - -class TrackerAnnounceTask implements TaskInterface +class TrackerAnnounceTimer extends Timer { - - public function run($data) + public function init() { - app()->pdo->beginTransaction(); - try { - $this->processAnnounceRequest($data['queries'], $data['role'], $data['userInfo'], $data['torrentInfo']); - app()->pdo->commit(); - } catch (TrackerException $e) { - app()->pdo->rollback(); - // TODO throw $e; - } catch (\Exception $e) { - app()->pdo->rollback(); - // TODO throw new TrackerException(998, [":msg" => $e->getMessage()]); + while (true) { + $data_raw = app()->redis->rpoplpush('Tracker:to_deal_queue', 'Tracker:backup_queue'); + if ($data_raw !== false) { + $data = unserialize($data_raw); + app()->pdo->beginTransaction(); + try { + $this->processAnnounceRequest($data['queries'], $data['role'], $data['userInfo'], $data['torrentInfo']); + app()->pdo->commit(); + app()->redis->lRem('Tracker:backup_queue', $data_raw, 0); + } catch (\Exception $e) { + println($e->getMessage()); + app()->pdo->rollback(); + // TODO deal with the items in backup_queue + } + } else { + sleep(1); + } } } + /** TODO 2018.12.12 Check Muti-Tracker behaviour when a Transaction begin * @param $queries * @param $seeder * @param $userInfo * @param $torrentInfo - * @throws TrackerException */ private function processAnnounceRequest($queries, $seeder, $userInfo, $torrentInfo) { @@ -64,8 +68,8 @@ private function processAnnounceRequest($queries, $seeder, $userInfo, $torrentIn $trueUploaded = max(0, $queries['uploaded']); $trueDownloaded = max(0, $queries['downloaded']); - app()->pdo->createCommand("INSERT INTO `peers` SET `user_id` =:uid, `torrent_id`= :tid, `peer_id`= :pid, `started_at`= CURRENT_TIMESTAMP - `agent`=:agent, `seeder` = :seeder, {$ipField} , + app()->pdo->createCommand("INSERT INTO `peers` SET `user_id` =:uid, `torrent_id`= :tid, `peer_id`= :pid, `started_at`= CURRENT_TIMESTAMP , `last_action_at` = CURRENT_TIMESTAMP , + `agent`= :agent, `seeder` = :seeder, {$ipField} , `uploaded` = :upload , `downloaded` = :download, `to_go` = :to_go, `corrupt` = :corrupt , `key` = :key ; ")->bindParams([ @@ -122,7 +126,7 @@ private function processAnnounceRequest($queries, $seeder, $userInfo, $torrentIn } else { // if session is exist but event!=stopped , we should continue the old session app()->pdo->createCommand("UPDATE `peers` SET `agent`=:agent, {$ipField}," . - "`seeder`=:seeder, + "`seeder`=:seeder, `uploaded`=`uploaded` + :uploaded, `downloaded`= `downloaded` + :download, `to_go` = :left, `last_action_at`=NOW(), `corrupt`=:corrupt, `key`=:key WHERE `user_id` = :uid AND `torrent_id` = :tid AND `peer_id`=:pid")->bindParams([ @@ -173,7 +177,6 @@ private function processAnnounceRequest($queries, $seeder, $userInfo, $torrentIn * @param $trueUploaded * @param $trueDownloaded * @param $duration - * @throws TrackerException */ private function checkUpspeed($userInfo, $torrentInfo, $trueUploaded, $trueDownloaded, $duration) { @@ -200,7 +203,6 @@ private function checkUpspeed($userInfo, $torrentInfo, $trueUploaded, $trueDownl ])->execute(); app()->redis->del("TRACKER:user_passkey_" . $userInfo["passkey"] . "_content"); - throw new TrackerException(170); } // Uploaded more than 1 GB with uploading rate higher than 25 MByte/S (For Consertive level). This is likely cheating. diff --git a/apps/views/admin/redis_key.php b/apps/views/admin/redis_key.php index 3995bd2..1d8dd67 100644 --- a/apps/views/admin/redis_key.php +++ b/apps/views/admin/redis_key.php @@ -50,7 +50,7 @@
-

Values

+

List Values

  1. diff --git a/bin/rid-httpd b/bin/rid-httpd index 5d7cb29..c95aef2 100644 --- a/bin/rid-httpd +++ b/bin/rid-httpd @@ -1,11 +1,13 @@ #!/usr/bin/env php run(); diff --git a/framework/Base/TaskInterface.php b/framework/Base/TaskInterface.php deleted file mode 100644 index 0eb6451..0000000 --- a/framework/Base/TaskInterface.php +++ /dev/null @@ -1,15 +0,0 @@ -clear(); // 设置定时器 - $timerId = swoole_timer_after($msec, function () use ($callback) { + $timerId = \Swoole\Timer::after($msec, function () use ($callback) { // 执行闭包 try { call_user_func($callback); @@ -74,7 +74,7 @@ public function tick(int $msec, callable $callback) // 清除旧定时器 $this->clear(); // 设置定时器 - $timerId = swoole_timer_tick($msec, function () use ($callback) { + $timerId = \Swoole\Timer::tick($msec, function () use ($callback) { // 执行闭包 try { call_user_func($callback); @@ -95,7 +95,8 @@ public function tick(int $msec, callable $callback) public function clear() { if (isset($this->_timerId)) { - return swoole_timer_clear($this->_timerId); + \Swoole\Timer::clear($this->_timerId); + return true; } return false; } diff --git a/framework/Component/Config.php b/framework/Component/Config.php index 9fb0fbb..40b43ae 100644 --- a/framework/Component/Config.php +++ b/framework/Component/Config.php @@ -13,8 +13,9 @@ class Config extends Component { - /** @var \swoole_table */ + /** @var \Swoole\Table */ private $cacheTable; + private $valueField = 'data'; public function onInitialize(array $config = []) diff --git a/framework/Exceptions/TaskException.php b/framework/Exceptions/TaskException.php deleted file mode 100644 index fe45169..0000000 --- a/framework/Exceptions/TaskException.php +++ /dev/null @@ -1,10 +0,0 @@ - false, // 开启协程 'reactor_num' => 8, // 主进程事件处理线程数 'worker_num' => 8, // 工作进程数 - 'task_worker_num' => 8, // 任务进程数 'max_request' => 10000, // 进程的最大任务数 'reload_async' => true, // 异步安全重启 'max_wait_time' => 60, // 退出等待时间 @@ -45,132 +44,170 @@ class HttpServer extends BaseObject // 端口 protected $_port; - // 初始化 - protected function initialize() + // 启动服务 + public function start() { // 初始化参数 $this->_host = $this->virtualHost['host']; $this->_port = $this->virtualHost['port']; + + // 实例化服务器 + $this->_server = new \Swoole\Http\Server($this->_host, $this->_port); $this->settings += $this->_settings; - $this->createSever(); + $this->_server->set($this->settings); + // 覆盖参数 + $this->_server->set([ + 'enable_coroutine' => false, // 关闭默认协程,回调中有手动开启支持上下文的协程 + ]); + + // 绑定事件 + $this->_server->on('start', [$this, 'onStart']); + $this->_server->on('shutdown', [$this, 'onShutdown']); + $this->_server->on('managerStart', [$this, 'onManagerStart']); + $this->_server->on('managerStop', [$this, 'onManagerStop']); + $this->_server->on('workerStart', [$this, 'onWorkerStart']); + $this->_server->on('workerStop', [$this, 'onWorkerStop']); + $this->_server->on('workerError', [$this, 'onWorkerError']); + $this->_server->on('workerExit', [$this, 'onWorkerExit']); + $this->_server->on('request', [$this, 'onRequest']); + + // 欢迎信息 + $this->welcome(); + + // rid-httpd 模式下,在此处创建全局的 \Swoole\Table + $configTable = new \Swoole\Table(2048); + $configTable->column('data', \Swoole\Table::TYPE_STRING, 256); + $configTable->create(); + $this->_server->configTable = $configTable; + + return $this->_server->start(); } - // 启动服务 - public function start() + /** + * 主进程启动事件 + * 仅允许echo、打印Log、修改进程名称,不得执行其他操作 + * @param \Swoole\Server $server + */ + public function onStart(\Swoole\Server $server) { - $this->initialize(); - $this->welcome(); - $this->onStart(); - $this->onManagerStart(); - $this->onWorkerStart(); - $this->onTask(); - $this->onFinish(); - $this->onRequest(); - $this->_server->set($this->settings); - $this->_server->start(); + ProcessHelper::setTitle("{$this->name}: master {$this->_host}:{$this->_port}"); } - // 主进程启动事件 - protected function onStart() + /** + * 主进程停止事件 + * 请勿在onShutdown中调用任何异步或协程相关API,触发onShutdown时底层已销毁了所有事件循环设施 + * @param \Swoole\Server $server + */ + public function onShutdown(\Swoole\Server $server) { - $this->_server->on('Start', function (\swoole_server $server) { - ProcessHelper::setTitle("rid-httpd: master {$this->_host}:{$this->_port}"); - }); + } - // 管理进程启动事件 - protected function onManagerStart() + /** + * 管理进程启动事件 + * 可以使用基于信号实现的同步模式定时器swoole_timer_tick,不能使用task、async、coroutine等功能 + * @param \Swoole\Server $server + */ + public function onManagerStart(\Swoole\Server $server) { - $this->_server->on('ManagerStart', function (\swoole_server $server) { - // 进程命名 - ProcessHelper::setTitle("rid-httpd: manager"); - }); + ProcessHelper::setTitle("{$this->name}: manager"); // 进程命名 } - // 工作进程启动事件 - protected function onWorkerStart() + /** + * 管理进程停止事件 + * @param \Swoole\Server $server + */ + public function onManagerStop(\Swoole\Server $server) { - $this->_server->on('WorkerStart', function (\swoole_server $server,int $workerId) { - // 刷新OpCode缓存,防止reload重载入时受到影响 - foreach (['apc_clear_cache', 'opcache_reset'] as $func) { - if (function_exists($func)) $func(); - } - // 进程命名 - if ($workerId < $server->setting['worker_num']) { - ProcessHelper::setTitle("rid-httpd: worker #{$workerId}"); - } else { - ProcessHelper::setTitle("rid-httpd: task #{$workerId}"); - } - - // 实例化App - $config = require $this->virtualHost['configFile']; - $app = new Application($config); - $app->setServ($this->_server); - $app->setWorker($workerId); - $app->loadAllComponents(); - - if ($workerId == 0) { // 将系统设置中的 Timer 添加到 worker #0 中 - foreach (app()->env('timer') as $timer_name => $timer_config) { - $timer_class = $timer_config['class']; - $timer = new $timer_class(); - if ($timer instanceof Timer) { - $timer->run($timer_config); - } - } - } - }); } - protected function onTask() + /** + * 工作进程启动事件 + * @param \Swoole\Http\Server $server + * @param int $workerId + */ + public function onWorkerStart(\Swoole\Http\Server $server, int $workerId) { - $this->_server->on('Task', function (\swoole_server $serv, int $task_id, int $src_worker_id, $data) { - $error_msg = "This Task {$task_id} from Worker {$src_worker_id}.\n"; - $data = unserialize($data); - $task_worker_name = $data['worker']; - if (class_exists($task_worker_name)) { - /** @var TaskInterface $task_worker */ - $task_worker = new $task_worker_name(); - if ($task_worker instanceof TaskInterface) { - return $task_worker->run($data); + // 刷新OpCode缓存,防止reload重载入时受到影响 + foreach (['apc_clear_cache', 'opcache_reset'] as $func) { + if (function_exists($func)) $func(); + } + + // 进程命名 + if ($workerId < $server->setting['worker_num']) { + ProcessHelper::setTitle("rid-httpd: worker #{$workerId}"); + } else { + ProcessHelper::setTitle("rid-httpd: task #{$workerId}"); + } + + // 实例化App + $config = require $this->virtualHost['configFile']; + $app = new Application($config); + $app->setServ($this->_server); + $app->setWorker($workerId); + $app->loadAllComponents(); + + if ($workerId == 0) { // 将系统设置中的 Timer 添加到 worker #0 中 + foreach (app()->env('timer') as $timer_name => $timer_config) { + $timer_class = $timer_config['class']; + $timer = new $timer_class(); + if ($timer instanceof Timer) { + $timer->run($timer_config); } - $error_msg .= "Error: The task worker is not instanceof TaskInterface,\nData: " . json_encode($data); - throw new TaskException($error_msg); } - $error_msg .= "No Task Worker model found,\nData: " . json_encode($data); - throw new TaskException($error_msg); - }); + } } - protected function onFinish() { - $this->_server->on('Finish', function (\swoole_server $serv, $task_id, $data) { - //echo "Task#$task_id finished, data_len=".strlen($data).PHP_EOL; - }); + /** + * 工作进程停止事件 + * @param \Swoole\Server $server + * @param int $workerId + */ + public function onWorkerStop(\Swoole\Server $server, int $workerId) + { + } - // 请求事件 - protected function onRequest() + /** + * 工作进程错误事件 + * 当Worker/Task进程发生异常后会在Manager进程内回调此函数。 + * @param \Swoole\Server $server + * @param int $workerId + * @param int $workerPid + * @param int $exitCode + * @param int $signal + */ + public function onWorkerError(\Swoole\Server $server, int $workerId, int $workerPid, int $exitCode, int $signal) { - $this->_server->on('request', function (\swoole_http_request $request,\swoole_http_response $response) { - try { - app()->request->setRequester($request); - app()->response->setResponder($response); - app()->run(); // 执行请求 - } catch (\Throwable $e) { - app()->error->handleException($e); - } - }); + } - protected function createSever() { - // 实例化服务器 - $this->_server = new \Swoole\Http\Server($this->_host, $this->_port); + /** + * 工作进程退出事件 + * 仅在开启reload_async特性后有效。异步重启特性,会先创建新的Worker进程处理新请求,旧的Worker进程自行退出 + * @param \Swoole\Server $server + * @param int $workerId + */ + public function onWorkerExit(\Swoole\Server $server, int $workerId) + { - // rid-httpd 模式下,在此处创建全局的 \Swoole\Table - $configTable = new \Swoole\Table(2048); - $configTable->column('data', \Swoole\Table::TYPE_STRING, 256); - $configTable->create(); - $this->_server->configTable = $configTable; + } + + /** + * 请求事件 + * @param \Swoole\Http\Request $request + * @param \Swoole\Http\Response $response + */ + public function onRequest(\Swoole\Http\Request $request, \Swoole\Http\Response $response) + { + try { + app()->request->setRequester($request); + app()->response->setResponder($response); + app()->run(); // 执行请求 + } catch (\Throwable $e) { + app()->error->handleException($e); + } } // 欢迎信息 @@ -184,14 +221,15 @@ protected function welcome() \ \ \\ \\ \ \/\ \L\ \ \ \/ \ \ \ \ \_\ \_\ \_\ \___,_\ \_\ \ \_\ \/_/\/ /\/_/\/__,_ /\/_/ \/_/ + EOL ); println('───────────────────────────────────────'); - println('Server Name: rid-httpd'); - println('System Name: ' . strtolower(PHP_OS)); + println('Server Name: ' . $this->name); + println('System Name: ' . PHP_OS); println('Framework Version: ' . \Rid::VERSION); println('PHP Version: ' . PHP_VERSION); - println('Swoole Version: ' . swoole_version()); + println('Swoole Version: ' . SWOOLE_VERSION); println('Listen Addr: ' . $this->_host); println('Listen Port: ' . $this->_port); println('Reactor Num: ' . $this->settings['reactor_num']);