mirror of
https://github.com/zhamao-robot/zhamao-framework.git
synced 2026-07-22 16:15:34 +08:00
update to 3.0.0-alpha2 (build 610): refactor driver
This commit is contained in:
170
src/ZM/Store/FileSystem.php
Normal file
170
src/ZM/Store/FileSystem.php
Normal file
@@ -0,0 +1,170 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace ZM\Store;
|
||||
|
||||
use RuntimeException;
|
||||
|
||||
class FileSystem
|
||||
{
|
||||
/**
|
||||
* 递归或非递归扫描目录,可返回相对目录的文件列表或绝对目录的文件列表
|
||||
*
|
||||
* @param string $dir 目录
|
||||
* @param bool $recursive 是否递归扫描子目录
|
||||
* @param bool|string $relative 是否返回相对目录,如果为true则返回相对目录,如果为false则返回绝对目录
|
||||
* @return array|false
|
||||
* @since 2.5
|
||||
*/
|
||||
public static function scanDirFiles(string $dir, bool $recursive = true, $relative = false)
|
||||
{
|
||||
$dir = zm_dir($dir);
|
||||
// 不是目录不扫,直接 false 处理
|
||||
if (!is_dir($dir)) {
|
||||
logger()->warning(zm_internal_errcode('E00080') . '扫描目录失败,目录不存在');
|
||||
return false;
|
||||
}
|
||||
logger()->debug('扫描' . $dir);
|
||||
// 套上 zm_dir
|
||||
$scan_list = scandir($dir);
|
||||
if ($scan_list === false) {
|
||||
logger()->warning(zm_internal_errcode('E00080') . '扫描目录失败,目录无法读取: ' . $dir);
|
||||
return false;
|
||||
}
|
||||
$list = [];
|
||||
// 将 relative 置为相对目录的前缀
|
||||
if ($relative === true) {
|
||||
$relative = $dir;
|
||||
}
|
||||
// 遍历目录
|
||||
foreach ($scan_list as $v) {
|
||||
// Unix 系统排除这俩目录
|
||||
if ($v == '.' || $v == '..') {
|
||||
continue;
|
||||
}
|
||||
$sub_file = zm_dir($dir . '/' . $v);
|
||||
if (is_dir($sub_file) && $recursive) { // 如果是目录且设置了递归的话,就递归扫描并合并
|
||||
$list = array_merge($list, self::scanDirFiles($sub_file, $recursive, $relative));
|
||||
} elseif (is_file($sub_file)) { // 如果是文件就直接加入列表
|
||||
if (is_string($relative) && strpos($sub_file, $relative) === 0) {
|
||||
$list[] = ltrim(mb_substr($sub_file, mb_strlen($relative)), '\\/');
|
||||
} elseif ($relative === false) {
|
||||
$list[] = $sub_file;
|
||||
} else {
|
||||
logger()->warning(zm_internal_errcode('E00058') . "Relative path is not generated: wrong base directory ({$relative})");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return $list;
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查路径是否为相对路径(根据第一个字符是否为"/"来判断)
|
||||
*
|
||||
* @param string $path 路径
|
||||
* @return bool 返回结果
|
||||
* @since 2.5
|
||||
*/
|
||||
public static function isRelativePath(string $path): bool
|
||||
{
|
||||
return strlen($path) > 0 && $path[0] !== '/';
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建目录(如果不存在)
|
||||
*
|
||||
* @param string $path 目录路径
|
||||
*/
|
||||
public static function createDir(string $path): void
|
||||
{
|
||||
if (!is_dir($path) && !mkdir($path, 0777, true) && !is_dir($path)) {
|
||||
throw new RuntimeException(sprintf('无法建立目录:%s', $path));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 在工作进程中返回可以通过reload重新加载的php文件列表
|
||||
*
|
||||
* @return string[]|string[][]
|
||||
*/
|
||||
public static function getReloadableFiles(): array
|
||||
{
|
||||
$array_map = [];
|
||||
global $zm_loaded_files;
|
||||
foreach (array_diff(
|
||||
get_included_files(),
|
||||
$zm_loaded_files
|
||||
) as $key => $x) {
|
||||
$array_map[$key] = str_replace(SOURCE_ROOT_DIR . '/', '', $x);
|
||||
}
|
||||
return $array_map;
|
||||
}
|
||||
|
||||
/**
|
||||
* 使用Psr-4标准获取目录下的所有类
|
||||
* @param string $dir 目录
|
||||
* @param string $base_namespace 基础命名空间
|
||||
* @param null|mixed $rule 规则
|
||||
* @param bool|string $return_path_value 是否返回文件路径,返回文件路径的话传入字符串
|
||||
* @return string[]
|
||||
*/
|
||||
public static function getClassesPsr4(string $dir, string $base_namespace, $rule = null, $return_path_value = false): array
|
||||
{
|
||||
// 预先读取下composer的file列表
|
||||
$composer = json_decode(file_get_contents(zm_dir(SOURCE_ROOT_DIR . '/composer.json')), true);
|
||||
$classes = [];
|
||||
// 扫描目录,使用递归模式,相对路径模式,因为下面此路径要用作转换成namespace
|
||||
$files = FileSystem::scanDirFiles($dir, true, true);
|
||||
foreach ($files as $v) {
|
||||
$pathinfo = pathinfo($v);
|
||||
if (($pathinfo['extension'] ?? '') == 'php') {
|
||||
$path = rtrim($dir, '/') . '/' . rtrim($pathinfo['dirname'], './') . '/' . $pathinfo['basename'];
|
||||
|
||||
// 过滤不包含类的文件
|
||||
$tokens = token_get_all(file_get_contents($path));
|
||||
$found = false;
|
||||
foreach ($tokens as $token) {
|
||||
if (!is_array($token)) {
|
||||
continue;
|
||||
}
|
||||
if ($token[0] === T_CLASS) {
|
||||
$found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!$found) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($rule === null) { // 规则未设置回调时候,使用默认的识别过滤规则
|
||||
/*if (substr(file_get_contents($dir . '/' . $v), 6, 6) == '#plain') {
|
||||
continue;
|
||||
}*/
|
||||
if (file_exists($dir . '/' . $pathinfo['basename'] . '.plain')) {
|
||||
continue;
|
||||
}
|
||||
if (mb_substr($pathinfo['basename'], 0, 7) == 'global_' || mb_substr($pathinfo['basename'], 0, 7) == 'script_') {
|
||||
continue;
|
||||
}
|
||||
foreach (($composer['autoload']['files'] ?? []) as $fi) {
|
||||
if (md5_file(SOURCE_ROOT_DIR . '/' . $fi) == md5_file($dir . '/' . $v)) {
|
||||
continue 2;
|
||||
}
|
||||
}
|
||||
} elseif (is_callable($rule) && !$rule($dir, $pathinfo)) {
|
||||
continue;
|
||||
}
|
||||
$dirname = $pathinfo['dirname'] == '.' ? '' : (str_replace('/', '\\', $pathinfo['dirname']) . '\\');
|
||||
$class_name = $base_namespace . '\\' . $dirname . $pathinfo['filename'];
|
||||
if (is_string($return_path_value)) {
|
||||
$classes[$class_name] = $return_path_value . '/' . $v;
|
||||
} else {
|
||||
$classes[] = $class_name;
|
||||
}
|
||||
}
|
||||
}
|
||||
return $classes;
|
||||
}
|
||||
}
|
||||
19
src/ZM/Store/InternalGlobals.php
Normal file
19
src/ZM/Store/InternalGlobals.php
Normal file
@@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace ZM\Store;
|
||||
|
||||
use Symfony\Component\Routing\RouteCollection;
|
||||
|
||||
/**
|
||||
* 框架内部使用的全局变量
|
||||
*/
|
||||
class InternalGlobals
|
||||
{
|
||||
/**
|
||||
* @var null|RouteCollection 用于保存 Route 注解的路由树
|
||||
* @internal
|
||||
*/
|
||||
public static $routes;
|
||||
}
|
||||
@@ -1,347 +0,0 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace ZM\Store;
|
||||
|
||||
use Exception;
|
||||
use Swoole\Table;
|
||||
use ZM\Annotation\Swoole\OnSave;
|
||||
use ZM\Event\EventDispatcher;
|
||||
use ZM\Exception\LightCacheException;
|
||||
use ZM\Exception\ZMException;
|
||||
use ZM\Framework;
|
||||
use ZM\Utils\Manager\WorkerManager;
|
||||
|
||||
class LightCache
|
||||
{
|
||||
public static $last_error = '';
|
||||
|
||||
/** @var null|Table */
|
||||
private static $kv_table;
|
||||
|
||||
private static $config = [];
|
||||
|
||||
/**
|
||||
* @param array $config 配置
|
||||
* @throws Exception
|
||||
* @return bool|mixed 返回失败(false)或创建SwooleTable成功结果
|
||||
*/
|
||||
public static function init(array $config)
|
||||
{
|
||||
self::$config = $config;
|
||||
self::$kv_table = new Table($config['size'], $config['hash_conflict_proportion']);
|
||||
self::$kv_table->column('value', Table::TYPE_STRING, $config['max_strlen']);
|
||||
self::$kv_table->column('expire', Table::TYPE_INT);
|
||||
self::$kv_table->column('data_type', Table::TYPE_STRING, 8);
|
||||
$result = self::$kv_table->create();
|
||||
// 加载内容
|
||||
if ($result === true && isset($config['persistence_path'])) {
|
||||
if (file_exists($config['persistence_path'])) {
|
||||
$r = json_decode(file_get_contents($config['persistence_path']), true);
|
||||
if ($r === null) {
|
||||
$r = [];
|
||||
}
|
||||
foreach ($r as $k => $v) {
|
||||
$write = self::set($k, $v);
|
||||
logger()->debug('Writing LightCache: ' . $k);
|
||||
if ($write === false) {
|
||||
self::$last_error = zm_internal_errcode('E00051') . '可能是由于 Hash 冲突过多导致动态空间无法分配内存';
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if ($result === false) {
|
||||
self::$last_error = zm_internal_errcode('E00050') . '系统内存不足,申请失败';
|
||||
} else {
|
||||
$obj = Framework::loadFrameworkState();
|
||||
foreach (($obj['expiring_light_cache'] ?? []) as $k => $v) {
|
||||
$value = $v['value'];
|
||||
if (is_array($value)) {
|
||||
$value = json_encode($value, JSON_UNESCAPED_UNICODE);
|
||||
if (strlen($value) >= self::$config['max_strlen']) {
|
||||
return false;
|
||||
}
|
||||
$data_type = 'json';
|
||||
} elseif (is_string($value)) {
|
||||
$data_type = '';
|
||||
} elseif (is_int($value)) {
|
||||
$data_type = 'int';
|
||||
} elseif (is_bool($value)) {
|
||||
$data_type = 'bool';
|
||||
$value = json_encode($value);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
$result = self::$kv_table->set($k, [
|
||||
'value' => $value,
|
||||
'expire' => $v['expire'],
|
||||
'data_type' => $data_type,
|
||||
]);
|
||||
if ($result === false) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws ZMException
|
||||
* @return null|mixed
|
||||
*/
|
||||
public static function get(string $key)
|
||||
{
|
||||
if (self::$kv_table === null) {
|
||||
throw new LightCacheException('E00048', 'not initialized LightCache');
|
||||
}
|
||||
self::checkExpire($key);
|
||||
$r = self::$kv_table->get($key);
|
||||
return $r === false ? null : self::parseGet($r);
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws ZMException
|
||||
* @return null|mixed
|
||||
*/
|
||||
public static function getExpire(string $key)
|
||||
{
|
||||
if (self::$kv_table === null) {
|
||||
throw new LightCacheException('E00048', 'not initialized LightCache');
|
||||
}
|
||||
self::checkExpire($key);
|
||||
$r = self::$kv_table->get($key, 'expire');
|
||||
return $r === false ? null : $r - time();
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws ZMException
|
||||
* @return null|mixed
|
||||
* @since 2.4.3
|
||||
*/
|
||||
public static function getExpireTS(string $key)
|
||||
{
|
||||
if (self::$kv_table === null) {
|
||||
throw new LightCacheException('E00048', 'not initialized LightCache');
|
||||
}
|
||||
self::checkExpire($key);
|
||||
$r = self::$kv_table->get($key, 'expire');
|
||||
return $r === false ? null : $r;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array|int|string $value
|
||||
* @throws ZMException
|
||||
* @return bool
|
||||
*/
|
||||
public static function set(string $key, $value, int $expire = -1)
|
||||
{
|
||||
if (self::$kv_table === null) {
|
||||
throw new LightCacheException('E00048', 'not initialized LightCache');
|
||||
}
|
||||
if (is_array($value)) {
|
||||
$value = json_encode($value, JSON_UNESCAPED_UNICODE);
|
||||
if (strlen($value) >= self::$config['max_strlen']) {
|
||||
return false;
|
||||
}
|
||||
$data_type = 'json';
|
||||
} elseif (is_string($value)) {
|
||||
$data_type = '';
|
||||
} elseif (is_int($value)) {
|
||||
$data_type = 'int';
|
||||
} elseif (is_bool($value)) {
|
||||
$data_type = 'bool';
|
||||
$value = json_encode($value);
|
||||
} else {
|
||||
throw new LightCacheException('E00049', 'Only can set string, array and int');
|
||||
}
|
||||
try {
|
||||
return self::$kv_table->set($key, [
|
||||
'value' => $value,
|
||||
'expire' => $expire >= 0 ? $expire + time() : $expire,
|
||||
'data_type' => $data_type,
|
||||
]);
|
||||
} catch (Exception $e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $value
|
||||
* @throws ZMException
|
||||
* @return bool
|
||||
*/
|
||||
public static function update(string $key, $value)
|
||||
{
|
||||
if (self::$kv_table === null) {
|
||||
throw new LightCacheException('E00048', 'not initialized LightCache.');
|
||||
}
|
||||
if (is_array($value)) {
|
||||
$value = json_encode($value, JSON_UNESCAPED_UNICODE);
|
||||
if (strlen($value) >= self::$config['max_strlen']) {
|
||||
return false;
|
||||
}
|
||||
$data_type = 'json';
|
||||
} elseif (is_string($value)) {
|
||||
$data_type = '';
|
||||
} elseif (is_int($value)) {
|
||||
$data_type = 'int';
|
||||
} else {
|
||||
throw new LightCacheException('E00048', 'Only can set string, array and int');
|
||||
}
|
||||
try {
|
||||
if (self::$kv_table->get($key) === false) {
|
||||
return false;
|
||||
}
|
||||
return self::$kv_table->set($key, [
|
||||
'value' => $value,
|
||||
'data_type' => $data_type,
|
||||
]);
|
||||
} catch (Exception $e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static function getMemoryUsage()
|
||||
{
|
||||
return self::$kv_table->getMemorySize();
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws Exception
|
||||
*/
|
||||
public static function isset(string $key): bool
|
||||
{
|
||||
return self::get($key) !== null;
|
||||
}
|
||||
|
||||
public static function unset(string $key)
|
||||
{
|
||||
return self::$kv_table->del($key);
|
||||
}
|
||||
|
||||
public static function getAll(): array
|
||||
{
|
||||
$r = [];
|
||||
$del = [];
|
||||
foreach (self::$kv_table as $k => $v) {
|
||||
if ($v['expire'] <= time() && $v['expire'] >= 0) {
|
||||
$del[] = $k;
|
||||
continue;
|
||||
}
|
||||
$r[$k] = self::parseGet($v);
|
||||
}
|
||||
foreach ($del as $v) {
|
||||
self::unset($v);
|
||||
}
|
||||
return $r;
|
||||
}
|
||||
|
||||
public static function addPersistence($key)
|
||||
{
|
||||
if (file_exists(self::$config['persistence_path'])) {
|
||||
$r = json_decode(file_get_contents(self::$config['persistence_path']), true);
|
||||
if ($r === null) {
|
||||
$r = [];
|
||||
}
|
||||
if (!isset($r[$key])) {
|
||||
$r[$key] = null;
|
||||
}
|
||||
file_put_contents(self::$config['persistence_path'], json_encode($r, 64 | 128 | 256));
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static function removePersistence($key)
|
||||
{
|
||||
if (file_exists(self::$config['persistence_path'])) {
|
||||
$r = json_decode(file_get_contents(self::$config['persistence_path']), true);
|
||||
if ($r === null) {
|
||||
$r = [];
|
||||
}
|
||||
if (isset($r[$key])) {
|
||||
unset($r[$key]);
|
||||
}
|
||||
file_put_contents(self::$config['persistence_path'], json_encode($r, 64 | 128 | 256));
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 这个只能在唯一一个工作进程中执行
|
||||
* @throws Exception
|
||||
*/
|
||||
public static function savePersistence()
|
||||
{
|
||||
if (server()->worker_id !== MAIN_WORKER) {
|
||||
WorkerManager::sendActionToWorker(MAIN_WORKER, 'save_persistence', []);
|
||||
return;
|
||||
}
|
||||
$dispatcher = new EventDispatcher(OnSave::class);
|
||||
$dispatcher->dispatchEvents();
|
||||
|
||||
if (self::$kv_table === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!empty(self::$config['persistence_path'])) {
|
||||
if (file_exists(self::$config['persistence_path'])) {
|
||||
$r = json_decode(file_get_contents(self::$config['persistence_path']), true);
|
||||
} else {
|
||||
$r = [];
|
||||
}
|
||||
if ($r === null) {
|
||||
$r = [];
|
||||
}
|
||||
foreach ($r as $k => $v) {
|
||||
logger()->debug('Saving ' . $k);
|
||||
$r[$k] = self::get($k);
|
||||
}
|
||||
file_put_contents(self::$config['persistence_path'], json_encode($r, 64 | 128 | 256));
|
||||
}
|
||||
|
||||
$obj = Framework::loadFrameworkState();
|
||||
$obj['expiring_light_cache'] = [];
|
||||
$del = [];
|
||||
foreach (self::$kv_table as $k => $v) {
|
||||
if ($v['expire'] <= time() && $v['expire'] >= 0) {
|
||||
$del[] = $k;
|
||||
} elseif ($v['expire'] > time()) {
|
||||
$obj['expiring_light_cache'][$k] = [
|
||||
'expire' => $v['expire'],
|
||||
'value' => self::parseGet($v),
|
||||
];
|
||||
}
|
||||
}
|
||||
foreach ($del as $v) {
|
||||
self::unset($v);
|
||||
}
|
||||
Framework::saveFrameworkState($obj);
|
||||
logger()->debug('Saved.');
|
||||
}
|
||||
|
||||
private static function checkExpire($key)
|
||||
{
|
||||
if (($expire = self::$kv_table->get($key, 'expire')) >= 0) {
|
||||
if ($expire <= time()) {
|
||||
self::$kv_table->del($key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static function parseGet($r)
|
||||
{
|
||||
switch ($r['data_type']) {
|
||||
case 'json':
|
||||
case 'int':
|
||||
case 'bool':
|
||||
return json_decode($r['value'], true);
|
||||
case '':
|
||||
default:
|
||||
return $r['value'];
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,73 +0,0 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace ZM\Store;
|
||||
|
||||
use Exception;
|
||||
use Swoole\Table;
|
||||
use ZM\Exception\LightCacheException;
|
||||
use ZM\Exception\ZMException;
|
||||
|
||||
class LightCacheInside
|
||||
{
|
||||
/** @var null|Table[] */
|
||||
private static $kv_table = [];
|
||||
|
||||
public static function init(): bool
|
||||
{
|
||||
try {
|
||||
self::createTable('wait_api', 3, 65536);
|
||||
self::createTable('connect', 3, 64); // 用于存单机器人模式下的机器人fd的
|
||||
self::createTable('static_route', 64, 256); // 用于存储
|
||||
self::createTable('light_array', 8, 512, 0.6);
|
||||
self::createTable('tmp_kv', 3, 512, 0.6);
|
||||
} catch (ZMException $e) {
|
||||
return false;
|
||||
} // 用于存协程等待的状态内容的
|
||||
// self::createTable("worker_start", 2, 1024);//用于存启动服务器时的状态的
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return null|mixed
|
||||
*/
|
||||
public static function get(string $table, string $key)
|
||||
{
|
||||
$r = self::$kv_table[$table]->get($key);
|
||||
return $r === false ? null : json_decode($r['value'], true);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array|int|string $value
|
||||
*/
|
||||
public static function set(string $table, string $key, $value): bool
|
||||
{
|
||||
try {
|
||||
return self::$kv_table[$table]->set($key, [
|
||||
'value' => json_encode($value, 256),
|
||||
]);
|
||||
} catch (Exception $e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static function unset(string $table, string $key)
|
||||
{
|
||||
return self::$kv_table[$table]->del($key);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param float|int $conflict_proportion
|
||||
* @throws ZMException
|
||||
*/
|
||||
private static function createTable(string $name, int $size, int $str_size, $conflict_proportion = 0)
|
||||
{
|
||||
self::$kv_table[$name] = new Table($size, $conflict_proportion);
|
||||
self::$kv_table[$name]->column('value', Table::TYPE_STRING, $str_size);
|
||||
$r = self::$kv_table[$name]->create();
|
||||
if ($r === false) {
|
||||
throw new LightCacheException('E00050', '内存不足,创建静态表失败!');
|
||||
}
|
||||
}
|
||||
}
|
||||
45
src/ZM/Store/Lock/FileLock.php
Normal file
45
src/ZM/Store/Lock/FileLock.php
Normal file
@@ -0,0 +1,45 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace ZM\Store\Lock;
|
||||
|
||||
use ZM\Exception\ZMKnownException;
|
||||
|
||||
class FileLock
|
||||
{
|
||||
private static $lock_file_handle = [];
|
||||
|
||||
private static $name_hash = [];
|
||||
|
||||
/**
|
||||
* 基于文件的锁,适用于跨进程操作资源用的
|
||||
*
|
||||
* @throws ZMKnownException
|
||||
*/
|
||||
public static function lock(string $name)
|
||||
{
|
||||
self::$name_hash[$name] = self::$name_hash[$name] ?? md5($name);
|
||||
$lock_file = is_dir('/tmp') ? '/tmp' : WORKING_DIR . '.zm_' . zm_instance_id() . self::$name_hash[$name] . '.lock';
|
||||
self::$lock_file_handle[$name] = fopen($lock_file, 'w');
|
||||
if (self::$lock_file_handle[$name] === false) {
|
||||
logger()->critical("Can not create lock file {$lock_file}\n");
|
||||
throw new ZMKnownException('E99999', 'Can not create lock file ' . $lock_file);
|
||||
}
|
||||
if (!flock(self::$lock_file_handle[$name], LOCK_EX)) {
|
||||
logger()->error("File Lock \"{$name}\"already exists.\n");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 解锁
|
||||
*
|
||||
* @param string $name 锁名
|
||||
*/
|
||||
public static function unlock(string $name)
|
||||
{
|
||||
if ((self::$lock_file_handle[$name] ?? false) !== false) {
|
||||
fclose(self::$lock_file_handle[$name]);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,52 +0,0 @@
|
||||
<?php
|
||||
|
||||
/** @noinspection PhpUnused */
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace ZM\Store\Lock;
|
||||
|
||||
use Swoole\Table;
|
||||
|
||||
class SpinLock
|
||||
{
|
||||
/** @var null|Table */
|
||||
private static $kv_lock;
|
||||
|
||||
private static $delay = 1;
|
||||
|
||||
public static function init($key_cnt, $delay = 1)
|
||||
{
|
||||
self::$kv_lock = new Table($key_cnt, 0.7);
|
||||
self::$delay = $delay;
|
||||
self::$kv_lock->column('lock_num', Table::TYPE_INT, 8);
|
||||
return self::$kv_lock->create();
|
||||
}
|
||||
|
||||
public static function lock(string $key)
|
||||
{
|
||||
while (($r = self::$kv_lock->incr($key, 'lock_num')) > 1) { // 此资源已经被锁上了
|
||||
usleep(self::$delay * 1000);
|
||||
}
|
||||
}
|
||||
|
||||
public static function tryLock(string $key): bool
|
||||
{
|
||||
if (($r = self::$kv_lock->incr($key, 'lock_num')) > 1) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public static function unlock(string $key)
|
||||
{
|
||||
return self::$kv_lock->set($key, ['lock_num' => 0]);
|
||||
}
|
||||
|
||||
public static function transaction(string $key, callable $function)
|
||||
{
|
||||
SpinLock::lock($key);
|
||||
$function();
|
||||
SpinLock::unlock($key);
|
||||
}
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace ZM\Store\MySQL;
|
||||
|
||||
use ZM\MySQL\MySQLPool;
|
||||
|
||||
class SqlPoolStorage
|
||||
{
|
||||
/** @var null|MySQLPool */
|
||||
public static $sql_pool;
|
||||
}
|
||||
@@ -1,63 +0,0 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @noinspection PhpComposerExtensionStubsInspection
|
||||
* @noinspection PhpUnused
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace ZM\Store\Redis;
|
||||
|
||||
use Redis;
|
||||
use ZM\Exception\NotInitializedException;
|
||||
|
||||
class ZMRedis
|
||||
{
|
||||
private $conn;
|
||||
|
||||
/**
|
||||
* ZMRedis constructor.
|
||||
* @throws NotInitializedException
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
if (ZMRedisPool::$pool === null) {
|
||||
throw new NotInitializedException('Redis pool is not initialized.');
|
||||
}
|
||||
$this->conn = ZMRedisPool::$pool->get();
|
||||
}
|
||||
|
||||
public function __destruct()
|
||||
{
|
||||
if (isset($this->conn->wasted)) {
|
||||
ZMRedisPool::$pool->put(null);
|
||||
} else {
|
||||
ZMRedisPool::$pool->put($this->conn);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws NotInitializedException
|
||||
* @return mixed
|
||||
*/
|
||||
public static function call(callable $callable)
|
||||
{
|
||||
if (ZMRedisPool::$pool === null) {
|
||||
throw new NotInitializedException('Redis pool is not initialized.');
|
||||
}
|
||||
$r = ZMRedisPool::$pool->get();
|
||||
$result = $callable($r);
|
||||
if (isset($r->wasted)) {
|
||||
ZMRedisPool::$pool->put(null);
|
||||
} else {
|
||||
ZMRedisPool::$pool->put($r);
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
public function get(): Redis
|
||||
{
|
||||
return $this->conn;
|
||||
}
|
||||
}
|
||||
@@ -1,41 +0,0 @@
|
||||
<?php
|
||||
|
||||
/** @noinspection PhpComposerExtensionStubsInspection */
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace ZM\Store\Redis;
|
||||
|
||||
use RedisException;
|
||||
use Swoole\Database\RedisConfig;
|
||||
use Swoole\Database\RedisPool;
|
||||
use ZM\Console\Console;
|
||||
|
||||
class ZMRedisPool
|
||||
{
|
||||
/** @var null|RedisPool */
|
||||
public static $pool;
|
||||
|
||||
public static function init($config)
|
||||
{
|
||||
self::$pool = new RedisPool(
|
||||
(new RedisConfig())
|
||||
->withHost($config['host'])
|
||||
->withPort($config['port'])
|
||||
->withAuth($config['auth'])
|
||||
->withDbIndex($config['db_index'])
|
||||
->withTimeout($config['timeout'] ?? 1)
|
||||
);
|
||||
try {
|
||||
$r = self::$pool->get()->ping('123');
|
||||
if (strpos(strtolower($r), '123') !== false) {
|
||||
logger()->debug('成功连接redis连接池!');
|
||||
} else {
|
||||
var_dump($r);
|
||||
}
|
||||
} catch (RedisException $e) {
|
||||
Console::error(zm_internal_errcode('E00047') . 'Redis init failed! ' . $e->getMessage());
|
||||
self::$pool = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,107 +0,0 @@
|
||||
<?php
|
||||
|
||||
/** @noinspection PhpMissingReturnTypeInspection */
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace ZM\Store;
|
||||
|
||||
use ZM\Config\ZMConfig;
|
||||
|
||||
class WorkerCache
|
||||
{
|
||||
public static $config;
|
||||
|
||||
public static $store = [];
|
||||
|
||||
public static $transfer = [];
|
||||
|
||||
public static function get($key)
|
||||
{
|
||||
$config = self::$config ?? ZMConfig::get('global', 'worker_cache') ?? ['worker' => 0];
|
||||
if ($config['worker'] === server()->worker_id) {
|
||||
return self::$store[$key] ?? null;
|
||||
}
|
||||
$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') ?? ['worker' => 0];
|
||||
if ($config['worker'] === server()->worker_id) {
|
||||
self::$store[$key] = $value;
|
||||
return true;
|
||||
}
|
||||
$action = ['action' => $async ? 'asyncSetWorkerCache' : 'setWorkerCache', 'key' => $key, 'value' => $value, 'cid' => zm_cid()];
|
||||
return self::processRemote($action, $async, $config);
|
||||
}
|
||||
|
||||
public static function hasKey($key, $subkey)
|
||||
{
|
||||
$config = self::$config ?? ZMConfig::get('global', 'worker_cache') ?? ['worker' => 0];
|
||||
if ($config['worker'] === server()->worker_id) {
|
||||
return isset(self::$store[$key][$subkey]);
|
||||
}
|
||||
$action = ['hasKeyWorkerCache', 'key' => $key, 'subkey' => $subkey, 'cid' => zm_cid()];
|
||||
return self::processRemote($action, false, $config);
|
||||
}
|
||||
|
||||
public static function unset($key, $async = false)
|
||||
{
|
||||
$config = self::$config ?? ZMConfig::get('global', 'worker_cache') ?? ['worker' => 0];
|
||||
if ($config['worker'] === server()->worker_id) {
|
||||
unset(self::$store[$key]);
|
||||
return true;
|
||||
}
|
||||
$action = ['action' => $async ? 'asyncUnsetWorkerCache' : 'unsetWorkerCache', 'key' => $key, 'cid' => zm_cid()];
|
||||
return self::processRemote($action, $async, $config);
|
||||
}
|
||||
|
||||
public static function add($key, int $value, $async = false)
|
||||
{
|
||||
$config = self::$config ?? ZMConfig::get('global', 'worker_cache') ?? ['worker' => 0];
|
||||
if ($config['worker'] === server()->worker_id) {
|
||||
if (!isset(self::$store[$key])) {
|
||||
self::$store[$key] = 0;
|
||||
}
|
||||
self::$store[$key] += $value;
|
||||
return true;
|
||||
}
|
||||
$action = ['action' => $async ? 'asyncAddWorkerCache' : 'addWorkerCache', 'key' => $key, 'value' => $value, 'cid' => zm_cid()];
|
||||
return self::processRemote($action, $async, $config);
|
||||
}
|
||||
|
||||
public static function sub($key, int $value, $async = false)
|
||||
{
|
||||
$config = self::$config ?? ZMConfig::get('global', 'worker_cache') ?? ['worker' => 0];
|
||||
if ($config['worker'] === server()->worker_id) {
|
||||
if (!isset(self::$store[$key])) {
|
||||
self::$store[$key] = 0;
|
||||
}
|
||||
self::$store[$key] -= $value;
|
||||
return true;
|
||||
}
|
||||
$action = ['action' => $async ? 'asyncSubWorkerCache' : 'subWorkerCache', 'key' => $key, 'value' => $value, 'cid' => zm_cid()];
|
||||
return self::processRemote($action, $async, $config);
|
||||
}
|
||||
|
||||
private static function processRemote($action, $async, $config)
|
||||
{
|
||||
$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;
|
||||
}
|
||||
}
|
||||
@@ -1,44 +0,0 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace ZM\Store;
|
||||
|
||||
use Swoole\Atomic;
|
||||
use ZM\Config\ZMConfig;
|
||||
|
||||
class ZMAtomic
|
||||
{
|
||||
/** @var Atomic[] */
|
||||
public static $atomics;
|
||||
|
||||
public static function get(string $name): ?Atomic
|
||||
{
|
||||
return self::$atomics[$name] ?? null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化atomic计数器
|
||||
*/
|
||||
public static function init()
|
||||
{
|
||||
foreach ((ZMConfig::get('global', 'init_atomics') ?? []) as $k => $v) {
|
||||
self::$atomics[$k] = new Atomic($v);
|
||||
}
|
||||
self::$atomics['stop_signal'] = new Atomic(0);
|
||||
self::$atomics['_int_is_reload'] = new Atomic(0);
|
||||
self::$atomics['wait_msg_id'] = new Atomic(0);
|
||||
self::$atomics['_event_id'] = new Atomic(0);
|
||||
self::$atomics['server_is_stopped'] = new Atomic(0);
|
||||
if (!defined('ZM_WORKER_NUM')) {
|
||||
define('ZM_WORKER_NUM', 1);
|
||||
}
|
||||
for ($i = 0; $i < ZM_WORKER_NUM; ++$i) {
|
||||
self::$atomics['_#worker_' . $i] = new Atomic(0);
|
||||
}
|
||||
for ($i = 0; $i < 10; ++$i) {
|
||||
self::$atomics['_tmp_' . $i] = new Atomic(0);
|
||||
}
|
||||
self::$atomics['ss'] = new Atomic(1);
|
||||
}
|
||||
}
|
||||
@@ -1,48 +0,0 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* Created by PhpStorm.
|
||||
* User: jerry
|
||||
* Date: 2018/2/25
|
||||
* Time: 下午11:11
|
||||
*/
|
||||
|
||||
namespace ZM\Store;
|
||||
|
||||
use ZM\Context\ContextInterface;
|
||||
|
||||
class ZMBuf
|
||||
{
|
||||
/**
|
||||
* 注册的事件
|
||||
*
|
||||
* @deprecated 不再使用
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $events = [];
|
||||
|
||||
/**
|
||||
* 全局单例容器
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $instance = [];
|
||||
|
||||
/**
|
||||
* 上下文容器
|
||||
*
|
||||
* @var array<int, ContextInterface>
|
||||
*/
|
||||
public static $context_class = [];
|
||||
|
||||
/**
|
||||
* 终端输入流?
|
||||
*
|
||||
* 目前等用于 STDIN
|
||||
*
|
||||
* @var resource
|
||||
*/
|
||||
public static $terminal;
|
||||
}
|
||||
Reference in New Issue
Block a user