update to 2.2.0 version

add OnPipeMessageEvent.php
add ProcessManager.php
add WorkerCache component
fix route bug
correct Exception to ZMException
This commit is contained in:
jerry
2021-01-20 16:11:04 +08:00
parent f999e689bf
commit c68083484a
12 changed files with 260 additions and 66 deletions

View File

@@ -3,7 +3,7 @@
"description": "High performance QQ robot and web server development framework",
"minimum-stability": "stable",
"license": "Apache-2.0",
"version": "2.1.6",
"version": "2.2.0",
"extra": {
"exclude_annotate": [
"src/ZM"

View File

@@ -43,6 +43,11 @@ $config['light_cache'] = [
'auto_save_interval' => 900
];
$config["worker_cache"] = [
"worker" => 0,
"transaction_timeout" => 30000
];
/** MySQL数据库连接信息host留空则启动时不创建sql连接池 */
$config['sql_config'] = [
'sql_host' => '',

View File

@@ -2,6 +2,8 @@
namespace Module\Example;
use Swoole\Coroutine;
use ZM\Annotation\Http\Controller;
use ZM\Annotation\Http\Middleware;
use ZM\Annotation\Swoole\OnCloseEvent;
use ZM\Annotation\Swoole\OnOpenEvent;
@@ -11,12 +13,16 @@ use ZM\Console\Console;
use ZM\Annotation\CQ\CQCommand;
use ZM\Annotation\Http\RequestMapping;
use ZM\Event\EventDispatcher;
use ZM\Store\Lock\SpinLock;
use ZM\Store\WorkerCache;
use ZM\Utils\ProcessManager;
use ZM\Utils\ZMUtil;
/**
* Class Hello
* @package Module\Example
* @since 2.0
* @Controller("/sdd")
*/
class Hello
{
@@ -79,6 +85,7 @@ class Hello
* @RequestMapping("/")
*/
public function index() {
Console::info("Called!!");
return "Hello Zhamao!";
}
@@ -89,7 +96,42 @@ class Hello
* @return string
*/
public function paramGet($param) {
return "Your name: {$param["name"]}";
//WorkerCache::set("ss_cnt", 0);
return json_encode(Coroutine::stats());
}
/**
* @param $param
* @RequestMapping("/wc/set/{key}/{value}")
* @return string
*/
public function wcSet($param) {
WorkerCache::set($param["key"], $param["value"], true);
//SpinLock::lock("ss");
WorkerCache::add("ss_cnt", 1);
//SpinLock::unlock("ss");
return json_encode(Coroutine::stats());
}
/**
* @param $param
* @return mixed|string
* @RequestMapping("/wc/get/{key}")
* @Middleware("timer")
*/
public function wcGet($param) {
$obj = WorkerCache::get($param["key"]);
if(is_string($obj)) return $obj;
return json_encode($obj);
}
/**
* @return mixed
* @RequestMapping("/task")
*/
public function runTask() {
$r = ProcessManager::runOnTask(["class" => Hello::class, "method" => "index", "params" => []]);
return $r;
}
/**

View File

@@ -0,0 +1,24 @@
<?php
namespace ZM\Annotation\Swoole;
use Doctrine\Common\Annotations\Annotation\Required;
use Doctrine\Common\Annotations\Annotation\Target;
use ZM\Annotation\AnnotationBase;
/**
* Class OnPipeMessageEvent
* @package ZM\Annotation\Swoole
* @Annotation
* @Target("METHOD")
*/
class OnPipeMessageEvent extends AnnotationBase
{
/**
* @var string
* @Required()
*/
public $action;
}

View File

@@ -1,4 +1,6 @@
<?php /** @noinspection PhpComposerExtensionStubsInspection */
<?php /** @noinspection PhpUnreachableStatementInspection */
/** @noinspection PhpComposerExtensionStubsInspection */
namespace ZM\Event;
@@ -14,12 +16,12 @@ use Swoole\Database\PDOConfig;
use Swoole\Database\PDOPool;
use Swoole\Event;
use Swoole\Process;
use Swoole\Timer;
use ZM\Annotation\AnnotationParser;
use ZM\Annotation\Http\RequestMapping;
use ZM\Annotation\Swoole\OnCloseEvent;
use ZM\Annotation\Swoole\OnMessageEvent;
use ZM\Annotation\Swoole\OnOpenEvent;
use ZM\Annotation\Swoole\OnPipeMessageEvent;
use ZM\Annotation\Swoole\OnRequestEvent;
use ZM\Annotation\Swoole\OnStart;
use ZM\Annotation\Swoole\OnSwooleEvent;
@@ -42,6 +44,7 @@ use ZM\Module\QQBot;
use ZM\Store\LightCacheInside;
use ZM\Store\MySQL\SqlPoolStorage;
use ZM\Store\Redis\ZMRedisPool;
use ZM\Store\WorkerCache;
use ZM\Store\ZMBuf;
use ZM\Utils\DataProvider;
use ZM\Utils\HttpUtil;
@@ -258,11 +261,12 @@ class ServerEventHandler
public function onMessage($server, Frame $frame) {
Console::debug("Calling Swoole \"message\" from fd=" . $frame->fd . ": " . TermColor::ITALIC . $frame->data . TermColor::RESET);
unset(Context::$context[\Swoole\Coroutine::getCid()]);
unset(Context::$context[Coroutine::getCid()]);
$conn = ManagerGM::get($frame->fd);
set_coroutine_params(["server" => $server, "frame" => $frame, "connection" => $conn]);
$dispatcher1 = new EventDispatcher(OnMessageEvent::class);
$dispatcher1->setRuleFunction(function ($v) {
/** @noinspection PhpUnreachableStatementInspection */
return ctx()->getConnection()->getName() == $v->connect_type && eval("return " . $v->getRule() . ";");
});
@@ -480,38 +484,73 @@ class ServerEventHandler
* @param $server
* @param $src_worker_id
* @param $data
* @throws InterruptException
*/
public function onPipeMessage(Server $server, $src_worker_id, $data) {
//var_dump($data, $server->worker_id);
//unset(Context::$context[Co::getCid()]);
$data = json_decode($data, true);
switch ($data["action"] ?? '') {
case "resume_ws_message":
$obj = $data["data"];
Co::resume($obj["coroutine"]);
case "getWorkerCache":
$r = WorkerCache::get($data["key"]);
$action = ["action" => "returnWorkerCache", "cid" => $data["cid"], "value" => $r];
$server->sendMessage(json_encode($action, 256), $src_worker_id);
break;
case "stop":
Console::verbose('正在清理 #' . $server->worker_id . ' 的计时器');
Timer::clearAll();
case "setWorkerCache":
$r = WorkerCache::set($data["key"], $data["value"]);
$action = ["action" => "returnWorkerCache", "cid" => $data["cid"], "value" => $r];
$server->sendMessage(json_encode($action, 256), $src_worker_id);
break;
case "terminate":
$server->stop();
case "asyncAddWorkerCache":
WorkerCache::add($data["key"], $data["value"], true);
break;
case 'echo':
Console::success('接收到来自 #' . $src_worker_id . ' 的消息');
case "asyncSubWorkerCache":
WorkerCache::sub($data["key"], $data["value"], true);
break;
case 'send':
$server->sendMessage(json_encode(["action" => "echo"]), $data["target"]);
case "asyncSetWorkerCache":
WorkerCache::set($data["key"], $data["value"], true);
break;
case "addWorkerCache":
$r = WorkerCache::add($data["key"], $data["value"]);
$action = ["action" => "returnWorkerCache", "cid" => $data["cid"], "value" => $r];
$server->sendMessage(json_encode($action, 256), $src_worker_id);
break;
case "subWorkerCache":
$r = WorkerCache::sub($data["key"], $data["value"]);
$action = ["action" => "returnWorkerCache", "cid" => $data["cid"], "value" => $r];
$server->sendMessage(json_encode($action, 256), $src_worker_id);
break;
case "returnWorkerCache":
WorkerCache::$transfer[$data["cid"]] = $data["value"];
zm_resume($data["cid"]);
break;
default:
echo $data . PHP_EOL;
$dispatcher = new EventDispatcher(OnPipeMessageEvent::class);
$dispatcher->setRuleFunction(function (OnPipeMessageEvent $v) use ($data) {
return $v->action == $data["action"];
});
$dispatcher->dispatchEvents($data);
break;
}
}
/**
* @SwooleHandler("task")
* @param Server|null $server
* @param Server\Task $task
* @return mixed
*/
public function onTask() {
public function onTask(?Server $server, Server\Task $task) {
$data = $task->data;
switch($data["action"]) {
case "runMethod":
$c = $data["class"];
$ss = new $c();
$method = $data["method"];
$ps = $data["params"];
$task->finish($ss->$method(...$ps));
}
return null;
}
/**

View File

@@ -8,6 +8,7 @@ use Symfony\Component\Routing\Route;
use Symfony\Component\Routing\RouteCollection;
use ZM\Annotation\Http\Controller;
use ZM\Annotation\Http\RequestMapping;
use ZM\Console\Console;
class RouteManager
{
@@ -25,8 +26,9 @@ class RouteManager
break;
}
}
$route_name = $prefix."/".$vss->route;
$tail = trim($vss->route, "/");
$route_name = $prefix.($tail === "" ? "" : "/").$tail;
Console::debug("添加路由:".$route_name);
$route = new Route($route_name, ['_class' => $class, '_method' => $method]);
$route->setMethods($vss->request_method);

View File

@@ -7,6 +7,7 @@ namespace ZM\Store;
use Exception;
use Swoole\Table;
use ZM\Console\Console;
use ZM\Exception\ZMException;
class LightCache
{
@@ -19,6 +20,11 @@ class LightCache
public static $last_error = '';
/**
* @param $config
* @return bool|mixed
* @throws Exception
*/
public static function init($config) {
self::$config = $config;
self::$kv_table = new Table($config["size"], $config["hash_conflict_proportion"]);
@@ -50,11 +56,11 @@ class LightCache
/**
* @param string $key
* @return null|string
* @throws Exception
* @return null|mixed
* @throws ZMException
*/
public static function get(string $key) {
if (self::$kv_table === null) throw new Exception("not initialized LightCache");
if (self::$kv_table === null) throw new ZMException("not initialized LightCache");
self::checkExpire($key);
$r = self::$kv_table->get($key);
return $r === false ? null : self::parseGet($r);
@@ -63,10 +69,10 @@ class LightCache
/**
* @param string $key
* @return mixed|null
* @throws Exception
* @throws ZMException
*/
public static function getExpire(string $key) {
if (self::$kv_table === null) throw new Exception("not initialized LightCache");
if (self::$kv_table === null) throw new ZMException("not initialized LightCache");
self::checkExpire($key);
$r = self::$kv_table->get($key, "expire");
return $r === false ? null : $r - time();
@@ -77,10 +83,10 @@ class LightCache
* @param string|array|int $value
* @param int $expire
* @return mixed
* @throws Exception
* @throws ZMException
*/
public static function set(string $key, $value, int $expire = -1) {
if (self::$kv_table === null) throw new Exception("not initialized LightCache");
if (self::$kv_table === null) throw new ZMException("not initialized LightCache");
if (is_array($value)) {
$value = json_encode($value, JSON_UNESCAPED_UNICODE);
if (strlen($value) >= self::$config["max_strlen"]) return false;
@@ -93,7 +99,7 @@ class LightCache
$data_type = "bool";
$value = json_encode($value);
} else {
throw new Exception("Only can set string, array and int");
throw new ZMException("Only can set string, array and int");
}
try {
return self::$kv_table->set($key, [
@@ -110,10 +116,10 @@ class LightCache
* @param string $key
* @param $value
* @return bool|mixed
* @throws Exception
* @throws ZMException
*/
public static function update(string $key, $value) {
if (self::$kv_table === null) throw new Exception("not initialized LightCache.");
if (self::$kv_table === null) throw new ZMException("not initialized LightCache.");
if (is_array($value)) {
$value = json_encode($value, JSON_UNESCAPED_UNICODE);
if (strlen($value) >= self::$config["max_strlen"]) return false;
@@ -123,7 +129,7 @@ class LightCache
} elseif (is_int($value)) {
$data_type = "int";
} else {
throw new Exception("Only can set string, array and int");
throw new ZMException("Only can set string, array and int");
}
try {
if (self::$kv_table->get($key) === false) return false;

View File

@@ -0,0 +1,83 @@
<?php
namespace ZM\Store;
use ZM\Config\ZMConfig;
class WorkerCache
{
public static $config = null;
public static $store = [];
public static $transfer = [];
public static function get($key) {
$config = self::$config ?? ZMConfig::get("global", "worker_cache");
if ($config["worker"] === server()->worker_id) {
return self::$store[$key] ?? null;
} else {
$action = ["action" => "getWorkerCache", "key" => $key, "cid" => zm_cid()];
server()->sendMessage(json_encode($action, JSON_UNESCAPED_UNICODE), $config["worker"]);
zm_yield();
$p = self::$transfer[zm_cid()] ?? null;
unset(self::$transfer[zm_cid()]);
return $p;
}
}
public static function set($key, $value, $async = false) {
$config = self::$config ?? ZMConfig::get("global", "worker_cache");
if ($config["worker"] === server()->worker_id) {
self::$store[$key] = $value;
return true;
} else {
$action = ["action" => $async ? "asyncSetWorkerCache" : "setWorkerCache", "key" => $key, "value" => $value, "cid" => zm_cid()];
$ss = server()->sendMessage(json_encode($action, JSON_UNESCAPED_UNICODE), $config["worker"]);
if(!$ss) return false;
if ($async) return true;
zm_yield();
$p = self::$transfer[zm_cid()] ?? null;
unset(self::$transfer[zm_cid()]);
return $p;
}
}
public static function add($key, int $value, $async = false) {
$config = self::$config ?? ZMConfig::get("global", "worker_cache");
if ($config["worker"] === server()->worker_id) {
if(!isset(self::$store[$key])) self::$store[$key] = 0;
self::$store[$key] += $value;
return true;
} else {
$action = ["action" => $async ? "asyncAddWorkerCache" : "addWorkerCache", "key" => $key, "value" => $value, "cid" => zm_cid()];
$ss = server()->sendMessage(json_encode($action, JSON_UNESCAPED_UNICODE), $config["worker"]);
// if(!$ss) return false;
if ($async) return true;
zm_yield();
$p = self::$transfer[zm_cid()] ?? null;
unset(self::$transfer[zm_cid()]);
return $p;
}
}
public static function sub($key, int $value, $async = false) {
$config = self::$config ?? ZMConfig::get("global", "worker_cache");
if ($config["worker"] === server()->worker_id) {
if(!isset(self::$store[$key])) self::$store[$key] = 0;
self::$store[$key] -= $value;
return true;
} else {
$action = ["action" => $async ? "asyncSubWorkerCache" : "subWorkerCache", "key" => $key, "value" => $value, "cid" => zm_cid()];
$ss = server()->sendMessage(json_encode($action, JSON_UNESCAPED_UNICODE), $config["worker"]);
// if(!$ss) return false;
if ($async) return true;
zm_yield();
$p = self::$transfer[zm_cid()] ?? null;
unset(self::$transfer[zm_cid()]);
return $p;
}
}
}

View File

@@ -48,34 +48,4 @@ class CoMessage
if ($result === null) return false;
return $result;
}
public static function resumeByWS() {
$dat = ctx()->getData();
$last = null;
SpinLock::lock("wait_api");
$all = LightCacheInside::get("wait_api", "wait_api") ?? [];
foreach ($all as $k => $v) {
if(!isset($v["compare"])) continue;
foreach ($v["compare"] as $vs) {
if ($v[$vs] != ($dat[$vs] ?? null)) {
continue 2;
}
}
$last = $k;
}
if($last !== null) {
$all[$last]["result"] = $dat;
LightCacheInside::set("wait_api", "wait_api", $all);
SpinLock::unlock("wait_api");
if ($all[$last]["worker_id"] != server()->worker_id) {
ZMUtil::sendActionToWorker($all[$k]["worker_id"], "resume_ws_message", $all[$last]);
} else {
Co::resume($all[$last]["coroutine"]);
}
return true;
} else {
SpinLock::unlock("wait_api");
return false;
}
}
}

View File

@@ -0,0 +1,17 @@
<?php
namespace ZM\Utils;
class ProcessManager
{
public static function runOnTask($param, $timeout = 0.5, $dst_worker_id = -1) {
return server()->taskwait([
"action" => "runMethod",
"class" => $param["class"],
"method" => $param["method"],
"params" => $param["params"]
], $timeout, $dst_worker_id);
}
}

View File

@@ -51,8 +51,4 @@ class ZMUtil
return ZMBuf::$instance[$class];
}
}
public static function sendActionToWorker($target_id, $action, $data) {
server()->sendMessage(json_encode(["action" => $action, "data" => $data]), $target_id);
}
}

10
test/usage_test.php Normal file
View File

@@ -0,0 +1,10 @@
<?php
use ZM\Exception\ZMException;
use ZM\Store\LightCache;
LightCache::getMemoryUsage();
try {
LightCache::getExpire('1');
} catch (ZMException $e) {
}