Compare commits

..

10 Commits

71 changed files with 1608 additions and 669 deletions

10
.gitignore vendored
View File

@ -1,7 +1,3 @@
/.idea !.gitignore
/.vscode .idea
/vendor .vscode
*.log
.env
/tests/tmp
/tests/.phpunit.result.cache

View File

@ -1,27 +0,0 @@
# 基于docker的webman-jsonrpc
我们在`php:8.1-cli`基础上安装了运行webman必要的一些扩展和开发中常用的扩展以及composer制作成`workerman:8.1`镜像因此在开发环境中不需要再额外配置php环境直接写业务代码即可。
## 依赖
只要系统环境中有docker即可不需要安装php环境
## 使用方法
```sh
# 启动
./start
# composer
./composer install
./conposer require xxxx
```
## 一些有用的文件
| 文件 | 作用 | 用法或说明 |
|---|---|---|
|composer|调起容器中的`composer`命令的shell脚本|`./composer $args...`|
|start|调起容器中的`php start.php`命令的shell脚本|`./start`或`./start -d`|
|docker-compose.yml|运行容器的编排文件|一般不需要理会这个文件composer和start脚本都需要依赖这个文件|
|docker.conf|定义运行服务的容器名称和使用的镜像版本|注意container_name不要与系统中其他服务的容器重名|
|php.ini|映射到容器中的php.ini配置文件|容器中已经安装的扩展都有单独的配置文件,这里不会有体现|

View File

@ -7,15 +7,23 @@ SERVER_LISTEN = http://0.0.0.0:7878
SERVER_COUNT = cpu_count() SERVER_COUNT = cpu_count()
MAX_PACKAGE_SIZE = 1024*1024*5 MAX_PACKAGE_SIZE = 1024*1024*5
# monitor进程
MONITOR_ENABLED = false
# jsonrpc客户端配置
DEFAULT_RPC_SERVER = tcp://127.0.0.1:8022
USER_RPC_SERVER = tcp://127.0.0.1:8022
# jsonrcp服务配置 # jsonrcp服务配置
JSONRPC_ENABLED = true
JSONRPC_SERVER_NAME = jsonrpc JSONRPC_SERVER_NAME = jsonrpc
JSONRPC_SERVER_PORT = 8022 JSONRPC_SERVER_LISTEN = JsonNL://0.0.0.0:8022
JSONRPC_SERVER_COUNT = cpu_count() JSONRPC_SERVER_COUNT = cpu_count()
# REDIS配置 # REDIS配置
REDIS_HOST = 127.0.0.1 REDIS_HOST = 127.0.0.1
REDIS_PORT = 6379 REDIS_PORT = 6379
REDIS_PASSWORD = null REDIS_PASSWORD =
REDIS_DATABASE = 0 REDIS_DATABASE = 0
# 数据库 # 数据库
@ -24,23 +32,22 @@ DB_DEFAULT_DRIVER = pgsql
# mysql # mysql
MYSQL_HOST = 127.0.0.1 MYSQL_HOST = 127.0.0.1
MYSQL_PORT = 3306 MYSQL_PORT = 3306
MYSQL_USERNAME = MYSQL_USERNAME =
MYSQL_PASSWORD = MYSQL_PASSWORD =
MYSQL_DATABASE = MYSQL_DATABASE =
# pgsql # pgsql
PG_HOST = 127.0.0.1 PG_HOST = 127.0.0.1
PG_PORT = 5432 PG_PORT = 5432
PG_USERNAME = PG_USERNAME =
PG_PASSWORD = PG_PASSWORD =
PG_DATABASE = PG_DATABASE =
PG_SCHEMA = default PG_SCHEMA = default
# sqlite # sqlite
SQLITE_DATABASE = "base_path() . '/database.sqlite'" SQLITE_DATABASE = "base_path() . '/database.sqlite'"
SQLITE_PREFIX = SQLITE_PREFIX =
# log # log
LOG_MAX_FIlES = 30 LOG_MAX_FIlES = 30
LOG_MAX_FILES_DEBUG = 3 LOG_MAX_FILES_DEBUG = 3
LOG_MAX_FIlES_ERROR = 90

7
api/.gitignore vendored Normal file
View File

@ -0,0 +1,7 @@
/.idea
/.vscode
/vendor
*.log
.env
/tests/tmp
/tests/.phpunit.result.cache

92
api/README.md Normal file
View File

@ -0,0 +1,92 @@
# 基于docker的webman-jsonrpc
我们在`php:8.1-cli`基础上安装了运行webman必要的一些扩展和开发中常用的扩展以及composer制作成`workerman:8.1`镜像因此在开发环境中不需要再额外配置php环境直接写业务代码即可。
## 依赖
只要系统环境中有docker即可不需要安装php环境
## 使用方法
```sh
# composer
./composer install
./composer require xxxx
# 服务控制
./server start|restart|stop|status|reload|connections|logs
# 自动生成.env.example文件
./envexample
# 执行其它php命令
./php -m
./php webman build:bin
```
## 一些有用的文件
| 文件 | 作用 | 用法或说明 |
|---|---|---|
|composer|调起容器中的`composer`命令的shell脚本|用法与主机中的compose完全一致`./composer $args...`|
|server|服务控制脚本|`./server`可以查看用法|
|php|调起容器中的`php`命令的shell脚本|用法与主机中的php一致但注意执行`./php start.php start`命令时不要加-d参数|
|docker-compose.yml|运行容器的编排文件||
|php.ini|映射到容器中的php.ini配置文件|容器中已经安装的扩展都有单独的配置文件,这里不会有体现|
## 服务端口
在.env文件中可以定义服务端口默认情况下docker容器是在host网络下启动的当然实际使用中也可以改成端口映射模式。
## 已安装PHP扩展
```sh
php -m
[PHP Modules]
Core
ctype
curl
date
dom
event
exif
fileinfo
filter
ftp
gd
hash
iconv
json
libxml
mbstring
mysqli
mysqlnd
openssl
pcntl
pcre
PDO
pdo_mysql
pdo_pgsql
pdo_sqlite
pgsql
Phar
posix
readline
redis
Reflection
session
SimpleXML
sockets
sodium
SPL
sqlite3
standard
tokenizer
xlswriter
xml
xmlreader
xmlwriter
Zend OPcache
zip
zlib
[Zend Modules]
Zend OPcache
```

35
api/app/biz/ResCode.php Normal file
View File

@ -0,0 +1,35 @@
<?php
namespace app\biz;
class ResCode
{
const ERROR = 0; // 系统错误
const SUCCESS = 200; // 正确
const FAILED = -1; // 通用一般错误
const HEARTBEAT = 1; // 自定义1表示心跳消息可忽略
const INTERNAL_ERROR = 500; // 500错误
const LOGIN_FAILED = 301; // 自定义301表示登录失败
const NOT_LOGGED = 306; // 自定义306表示未登录
const NOT_ALLOWED = 308; // 自定义308表示没有权限
const TOKEN_ERROR = 309; // 自定义309错误表示token失效
const TIMEOUT = 400; // 400超时
const NOT_FOUND = 404; // 404错误
const NO_DATA = 407; // 自定义407表示数据不存在
const NO_CHANGE = 408; // 自定义408表示无数据变化
const CODE_MESSAGES = [
self::ERROR => '内部错误',
self::SUCCESS => '操作成功',
self::FAILED => '操作失败',
self::NOT_LOGGED => '用户未登录',
self::HEARTBEAT => '心跳',
self::NOT_ALLOWED => '没有权限',
self::NOT_FOUND => '请求路径不正确',
self::INTERNAL_ERROR => '系统错误',
self::NO_DATA => '数据不存在',
self::LOGIN_FAILED => '登录失败',
self::NO_CHANGE => '无数据变化',
self::TOKEN_ERROR => '令牌无效',
self::TIMEOUT => '超时',
];
}

View File

@ -2,6 +2,7 @@
namespace app\controller; namespace app\controller;
use support\Container;
use support\Log; use support\Log;
use support\Request; use support\Request;
use Wandoubaba\Res; use Wandoubaba\Res;
@ -10,7 +11,7 @@ class IndexController
{ {
public function index(Request $request) public function index(Request $request)
{ {
$res = new Res(); $res = Container::make(Res::class);
$res1 = \jsonrpc\Client::service('default', 'Demo')->hello('Json'); $res1 = \jsonrpc\Client::service('default', 'Demo')->hello('Json');
$res2 = \jsonrpc\Client::service('user', 'User')->login('admin', '123456'); $res2 = \jsonrpc\Client::service('user', 'User')->login('admin', '123456');
$res3 = \jsonrpc\Client::service('default', 'Demo')->text(); $res3 = \jsonrpc\Client::service('default', 'Demo')->text();

View File

@ -0,0 +1,26 @@
<?php
namespace app\controller;
use Wandoubaba\Res;
use support\Request;
use support\Container;
use support\Db;
use support\Log;
class TestController
{
public function index(Request $request)
{
$res = Container::make(Res::class);
// Log::channel('default')->info(__CLASS__ . DIRECTORY_SEPARATOR . __FUNCTION__, $_SERVER);
$logs = Db::connection('mongolog')->collection('request')->get();
// foreach ($logs as $log) {
// $log->delete();
// }
// $logs->delete();
$res->setData($logs);
return json($res);
}
}

View File

@ -0,0 +1,23 @@
<?php
namespace app\middleware;
use Webman\MiddlewareInterface;
use Webman\Http\Response;
use Webman\Http\Request;
class AccessControl implements MiddlewareInterface
{
public function process(Request $request, callable $handler): Response
{
$response = $request->method() == 'OPTIONS' ? response('') : $handler($request);
$response->withHeaders([
'Access-Control-Allow-Credentials' => 'true',
'Access-Control-Allow-Origin' => $request->header('Origin', '*'),
'Access-Control-Allow-Methods' => '*',
'Access-Control-Allow-Headers' => '*,ACCESSTOKEN',
]);
return $response;
}
}

View File

@ -0,0 +1,68 @@
<?php
namespace app\middleware;
use support\Log;
use Webman\MiddlewareInterface;
use Webman\Http\Response;
use Webman\Http\Request;
class RequestLog implements MiddlewareInterface
{
/**
* 需要中间件拦截的控制器清单,全部小写
*
* @var array
*/
protected $controllerScope = [
'app\controller\indexcontroller',
'app\controller\testcontroller',
];
/**
* 在拦截的控制器中不需要被拦截的例外方法,如用户登录
*
* @var array
*/
protected $actionWhiteList = [
'app\controller\testcontroller\index', // oss里的download不校验
];
public function process(Request $request, callable $next) : Response
{
$controller = strtolower($request->controller);
$action = strtolower($request->action);
$path = "{$controller}\\{$action}"; // 完整的控制器\方法字符串
// 白名单方法的请求不拦截
if (in_array($path, $this->actionWhiteList)) {
return $next($request);
}
// 只拦截指定控制器的请求
if (!in_array($controller, $this->controllerScope)) {
return $next($request);
}
$info = [
'session_id' => $request->sessionId(),
'protocol_version' => $request->protocolVersion(),
'method' => $request->method(),
'ip' => $request->getRemoteIp(),
'real_ip' => $request->getRealIp(),
'host' => $request->host(),
'path' => $request->path(),
'uri' => $request->uri(),
'url' => $request->url(),
'full_url' => $request->fullUrl(),
'query_string' => $request->queryString(),
'get_params' => $request->get(),
'post_params' => $request->post(),
'local_ip' => $request->getLocalIp(),
'local_port' => $request->getLocalPort(),
'is_ajax' => $request->isAjax(),
'is_pjax' => $request->isPjax(),
'expects_json' => $request->expectsJson(),
'accept_json' => $request->acceptJson(),
];
Log::channel('request')->info('', $info);
return $next($request);
}
}

3
api/composer Executable file
View File

@ -0,0 +1,3 @@
#!/bin/bash
docker compose run --rm webman-jsonrpc composer $@

View File

@ -26,15 +26,16 @@
"require": { "require": {
"php": ">=7.2", "php": ">=7.2",
"workerman/webman-framework": "^1.5.0", "workerman/webman-framework": "^1.5.0",
"monolog/monolog": "^2.0", "monolog/monolog": "^3.0",
"vlucas/phpdotenv": "^5.5", "vlucas/phpdotenv": "^5.5",
"illuminate/database": "^8.83", "illuminate/database": "^10.0",
"illuminate/pagination": "^8.83", "illuminate/pagination": "^10.0",
"illuminate/events": "^8.83", "illuminate/events": "^10.0",
"symfony/var-dumper": "^5.4", "symfony/var-dumper": "^6.0",
"illuminate/redis": "^8.83", "illuminate/redis": "^10.0",
"wandoubaba/res": "^1.0", "wandoubaba/res": "^1.0",
"webman/console": "^1.2" "webman/console": "^1.2",
"mongodb/laravel-mongodb": "^4.1"
}, },
"suggest": { "suggest": {
"ext-event": "For better performance. " "ext-event": "For better performance. "

File diff suppressed because it is too large Load Diff

View File

@ -12,4 +12,10 @@
* @license http://www.opensource.org/licenses/mit-license.php MIT License * @license http://www.opensource.org/licenses/mit-license.php MIT License
*/ */
return new Webman\Container; // return new Webman\Container;
$builder = new \DI\ContainerBuilder();
$builder->addDefinitions(config('dependence', []));
$builder->useAutowiring(true);
$builder->useAttributes(true);
return $builder->build();

View File

@ -46,5 +46,18 @@ return [
'schema' => env('PG_SCHEMA', 'public'), 'schema' => env('PG_SCHEMA', 'public'),
'sslmode' => 'prefer', 'sslmode' => 'prefer',
], ],
'mongolog' => [
'driver' => 'mongodb',
'host' => env('MONGOLOG_HOST', '127.0.0.1'),
'port' => env('MONGOLOG_PORT', 27017),
'database' => env('MONGOLOG_DATABASE', 'log'),
'username' => env('MONGOLOG_USERNAME', null),
'password' => env('MONGOLOG_PASSWORD', null),
'options' => [
// here you can pass more settings to the Mongo Driver Manager
// see https://www.php.net/manual/en/mongodb-driver-manager.construct.php under "Uri Options" for a list of complete parameters that you can use
'appname' => 'homestead'
],
],
] ]
]; ];

View File

@ -12,4 +12,7 @@
* @license http://www.opensource.org/licenses/mit-license.php MIT License * @license http://www.opensource.org/licenses/mit-license.php MIT License
*/ */
return []; return [
// 注入自定义错误码和错误提示
'code_messages' => app\biz\ResCode::CODE_MESSAGES,
];

View File

@ -6,10 +6,10 @@
return [ return [
'default' => [ 'default' => [
'tcp://127.0.0.1:' . env('JSONRPC_SERVER_PORT', 8021), // rpc服务端的地址和端口 env('DEFAULT_RPC_SERVER', 'tcp://127.0.0.1:8021'), // rpc服务端的地址和端口
], ],
'user' => [ 'user' => [
'tcp://127.0.0.1:8022', env('USER_RPC_SERVER', 'tcp://127.0.0.1:8022'), // rpc服务端的地址和端口
], ],
// 'server2' => [ // 'server2' => [
// /// ... // /// ...

View File

@ -1,4 +1,6 @@
<?php <?php
use MongoDB\Client;
/** /**
* This file is part of webman. * This file is part of webman.
* *
@ -19,13 +21,21 @@ return [
'class' => Monolog\Handler\RotatingFileHandler::class, 'class' => Monolog\Handler\RotatingFileHandler::class,
'constructor' => [ 'constructor' => [
runtime_path() . '/logs/webman.log', runtime_path() . '/logs/webman.log',
7, //$maxFiles env('LOG_MAX_FIlES', 7), //$maxFiles
Monolog\Logger::DEBUG, Monolog\Level::Debug,
], ],
'formatter' => [ 'formatter' => [
'class' => Monolog\Formatter\LineFormatter::class, 'class' => Monolog\Formatter\LineFormatter::class,
'constructor' => [null, 'Y-m-d H:i:s', true], 'constructor' => [null, 'Y-m-d H:i:s', true],
], ],
], [
'class' => Monolog\Handler\MongoDBHandler::class,
'constructor' => [
new Client(env('MONGOLOG_URL', 'mongodb://localhost:27017')),
env('MONGOLOG_DATABASE', 'log'),
'default',
Monolog\Level::Debug,
],
] ]
], ],
], ],
@ -36,24 +46,32 @@ return [
'constructor' => [ 'constructor' => [
runtime_path() . '/logs/debug/debug.log', runtime_path() . '/logs/debug/debug.log',
env('LOG_MAX_FILES_DEBUG') ?: env('LOG_MAX_FIlES') ?: 7, //$maxFiles env('LOG_MAX_FILES_DEBUG') ?: env('LOG_MAX_FIlES') ?: 7, //$maxFiles
Monolog\Logger::DEBUG, Monolog\Level::Debug,
], ],
'formatter' => [ 'formatter' => [
'class' => Monolog\Formatter\LineFormatter::class, 'class' => Monolog\Formatter\LineFormatter::class,
'constructor' => [null, 'Y-m-d H:i:s.u', true, true], 'constructor' => [null, 'Y-m-d H:i:s.u', true, true],
], ],
], [
'class' => Monolog\Handler\MongoDBHandler::class,
'constructor' => [
new Client(env('MONGOLOG_URL', 'mongodb://localhost:27017')),
env('MONGOLOG_DATABASE', 'log'),
'debug',
Monolog\Level::Debug,
],
], [ ], [
// 把日志输出到控制台 // 把日志输出到控制台
'class' => Monolog\Handler\StreamHandler::class, 'class' => Monolog\Handler\StreamHandler::class,
'constructor' => [ 'constructor' => [
'php://stdout', 'php://stdout',
Monolog\Logger::DEBUG, Monolog\Level::Debug,
], ],
'formatter' => [ 'formatter' => [
'class' => Monolog\Formatter\LineFormatter::class, 'class' => Monolog\Formatter\LineFormatter::class,
'constructor' => [null, 'Y-m-d H:i:s.u', true, true], 'constructor' => [null, 'Y-m-d H:i:s.u', true, true],
], ],
], ]
], ],
], ],
'error' => [ 'error' => [
@ -63,18 +81,26 @@ return [
'constructor' => [ 'constructor' => [
runtime_path() . '/logs/error/error.log', runtime_path() . '/logs/error/error.log',
env('LOG_MAX_FILES_ERROR') ?: env('LOG_MAX_FIlES') ?: 7, //$maxFiles env('LOG_MAX_FILES_ERROR') ?: env('LOG_MAX_FIlES') ?: 7, //$maxFiles
Monolog\Logger::DEBUG, Monolog\Level::Debug,
], ],
'formatter' => [ 'formatter' => [
'class' => Monolog\Formatter\LineFormatter::class, 'class' => Monolog\Formatter\LineFormatter::class,
'constructor' => [null, 'Y-m-d H:i:s.u', true, true], 'constructor' => [null, 'Y-m-d H:i:s.u', true, true],
], ],
], [
'class' => Monolog\Handler\MongoDBHandler::class,
'constructor' => [
new Client(env('MONGOLOG_URL', 'mongodb://localhost:27017')),
env('MONGOLOG_DATABASE', 'log'),
'error',
Monolog\Level::Debug,
],
], [ ], [
// 把日志输出到控制台 // 把日志输出到控制台
'class' => Monolog\Handler\StreamHandler::class, 'class' => Monolog\Handler\StreamHandler::class,
'constructor' => [ 'constructor' => [
'php://stdout', 'php://stdout',
Monolog\Logger::DEBUG, Monolog\Level::Debug,
], ],
'formatter' => [ 'formatter' => [
'class' => Monolog\Formatter\LineFormatter::class, 'class' => Monolog\Formatter\LineFormatter::class,
@ -83,5 +109,18 @@ return [
], ],
], ],
], ],
// 记录请求日志
'request' => [
'handlers' => [
[
'class' => Monolog\Handler\MongoDBHandler::class,
'constructor' => [
new Client(env('MONGOLOG_URL', 'mongodb://localhost:27017')),
env('MONGOLOG_DATABASE', 'log'),
'request',
Monolog\Level::Debug,
],
],
],
],
]; ];

View File

@ -12,4 +12,13 @@
* @license http://www.opensource.org/licenses/mit-license.php MIT License * @license http://www.opensource.org/licenses/mit-license.php MIT License
*/ */
return []; return [
'' => [
// 静态文件保护
app\middleware\StaticFile::class,
// 跨域请求
app\middleware\AccessControl::class,
// 记录请求日志
app\middleware\RequestLog::class,
],
];

View File

@ -15,9 +15,11 @@
global $argv; global $argv;
return [ $server_process = [];
// File update detection and automatic reload
'monitor' => [ // 是否启用monitor
if (env('MONITOR_ENABLED', true)) {
$server_process['monitor'] = [
'handler' => process\Monitor::class, 'handler' => process\Monitor::class,
'reloadable' => false, 'reloadable' => false,
'constructor' => [ 'constructor' => [
@ -39,10 +41,16 @@ return [
'enable_memory_monitor' => DIRECTORY_SEPARATOR === '/', 'enable_memory_monitor' => DIRECTORY_SEPARATOR === '/',
] ]
] ]
], ];
env('JSONRPC_SERVER_NAME', 'jsonrpc') => [ }
// 是否启用jsonrpc
if (env('JSONRPC_ENABLED', true)) {
$server_process[env('JSONRPC_SERVER_NAME', 'jsonrpc')] = [
'handler' => jsonrpc\Server::class, 'handler' => jsonrpc\Server::class,
'listen' => 'JsonNL://0.0.0.0:' . env('JSONRPC_SERVER_PORT', '8021'), 'listen' => env('JSONRPC_SERVER_LISTEN', 'JsonNL://0.0.0.0:8021'),
'count' => eval("return " . env('JSONRPC_SERVER_COUNT', cpu_count() * 4) . ";"), 'count' => eval("return " . env('JSONRPC_SERVER_COUNT', cpu_count() * 4) . ";"),
], ];
]; }
return $server_process;

12
api/docker-compose.yml Normal file
View File

@ -0,0 +1,12 @@
version: "3.1"
services:
webman-jsonrpc:
image: wandoubaba517/workerman:8.1
container_name: webman-jsonrpc
network_mode: host
volumes:
- ./:/app/service
- ./php.ini:/usr/local/etc/php/php.ini
working_dir: /app/service
stdin_open: true

31
api/envexample Executable file
View File

@ -0,0 +1,31 @@
#!/bin/bash
# 自动生成.env文件对应的.env.example文件
generate_env_example() {
# 清空原有的.env.example文件中的内容
> .env.example
# 逐行读取.env文件
while IFS= read -r line; do
# 去除首尾空格
line=$(echo "$line" | sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//')
# 如果是注释行或空白行,直接写入.env.example文件
if [[ $line == \#* || -z $line ]]; then
echo "$line" >> .env.example
# 如果是键值对行,判断键名是否包含指定字符串
elif [[ $line == *=* ]]; then
key=$(echo "$line" | cut -d= -f1)
value=$(echo "$line" | cut -d= -f2-)
# 如果键名包含指定字符串,将值清空写入.env.example文件
if [[ $key == *KEY || $key == *PASSWORD* || $key == *SECRET* || $key == *PASS || $key == *TOKEN || $key == *ID ]]; then
echo "$key=" >> .env.example
# 否则,清除首尾空格后原样写入.env.example文件
else
echo "$key=$value" >> .env.example
fi
fi
done < .env
}
# 每次run后都自动生成.env.example文件
generate_env_example

View File

@ -17,62 +17,30 @@ namespace jsonrpc;
use Protocols\JsonNL; use Protocols\JsonNL;
/**
* Aaron修改注释
* 需要在config目录下创建jsonrpc.php文件内容如下:
*
return [
'default' => [
'tcp://127.0.0.1:11002', // rpc服务端的地址和端口
],
'server1' => [
/// ...
],
'server2' => [
/// ...
]
];
*
* 客户端的调用方法
// 调用default服务组中Demo类中的hello方法并传入参数$name
$res = RpcClient::service('default', 'Demo')->hello($name);
// 调用server1服务组中User类中的login方法并传入参数$name, $password,
$res = RpcClient::service('server1', 'User')->login($userName, $userPassword);
*/
/** // ======== jsonrpc客户端使用方法 ========
* //
* RpcClient Rpc客户端 // 需要在config目录下创建jsonrpc.php文件内容如下:
* //
* // return [
* 示例 // 'default' => [
* // 服务端列表 // 'tcp://127.0.0.1:11002', // rpc服务端的地址和端口
$address_array = array( // ],
'tcp://127.0.0.1:2015', // 'server1' => [
'tcp://127.0.0.1:2015' // /// ...
); // ],
// 配置服务端列表 // 'server2' => [
RpcClient::config($address_array); // /// ...
// ]
$uid = 567; // ];
$user_client = RpcClient::instance('User'); //
// ==同步调用== // 客户端的调用方法:
$ret_sync = $user_client->getInfoByUid($uid); //
// **** 调用default服务组中Demo类中的hello方法并传入参数$name
// ==异步调用== // $res = RpcClient::service('default', 'Demo')->hello($name);
// 异步发送数据 // **** 调用server1服务组中User类中的login方法并传入参数$name, $password,
$user_client->asend_getInfoByUid($uid); // $res = RpcClient::service('server1', 'User')->login($userName, $userPassword);
$user_client->asend_getEmail($uid);
这里是其它的业务代码
..............................................
// 异步接收数据
$ret_async1 = $user_client->arecv_getEmail($uid);
$ret_async2 = $user_client->arecv_getInfoByUid($uid);
*
* @author walkor <worker-man@qq.com>
*/
class Client class Client
{ {
/** /**
@ -101,13 +69,13 @@ class Client
/** /**
* 异步调用实例 * 异步调用实例
* @var string * @var array
*/ */
protected static $asyncInstances = array(); protected static $asyncInstances = array();
/** /**
* 同步调用实例 * 同步调用实例
* @var string * @var array
*/ */
protected static $instances = array(); protected static $instances = array();
@ -182,7 +150,7 @@ class Client
* @throws Exception * @throws Exception
* @return * @return
*/ */
public function __call($method, $arguments) public function __call($method, $arguments = [])
{ {
// 判断是否是异步发送 // 判断是否是异步发送
if (0 === strpos($method, self::ASYNC_SEND_PREFIX)) { if (0 === strpos($method, self::ASYNC_SEND_PREFIX)) {
@ -219,9 +187,9 @@ class Client
{ {
$this->openConnection(); $this->openConnection();
$bin_data = JsonNL::encode(array( $bin_data = JsonNL::encode(array(
'class' => $this->serviceName, 'class' => $this->serviceName,
'method' => $method, 'method' => $method,
'param_array' => $arguments, 'param_array' => $arguments,
)); ));
if (fwrite($this->connection, $bin_data) !== strlen($bin_data)) { if (fwrite($this->connection, $bin_data) !== strlen($bin_data)) {
throw new \Exception('Can not send data'); throw new \Exception('Can not send data');
@ -267,4 +235,4 @@ class Client
fclose($this->connection); fclose($this->connection);
$this->connection = null; $this->connection = null;
} }
} }

3
api/php Executable file
View File

@ -0,0 +1,3 @@
#!/bin/bash
docker compose run --rm webman-jsonrpc php "$@"

View File

Before

Width:  |  Height:  |  Size: 4.2 KiB

After

Width:  |  Height:  |  Size: 4.2 KiB

47
api/server Executable file
View File

@ -0,0 +1,47 @@
#!/bin/bash
set -e
if [ -z "$1" ]; then
echo "Usage: $0 [start|stop|logs|restart|status|reload|connections]"
exit 1
fi
container_name="webman-jsonrpc-api"
if [ "$1" = "stop" ]; then
if [ "$(docker inspect -f {{.State.Running}} $container_name 2>/dev/null)" = "true" ]; then
docker stop $container_name
else
echo "Container $container_name is not running."
fi
else
# 判断容器是否存在
# if [ ! "$(docker ps -a -q -f name=$container_name)" ]; then
# # 调用init脚本
# ./init
# fi
# 判断容器是否在运行
if [ ! "$(docker inspect -f {{.State.Running}} $container_name 2>/dev/null)" = "true" ]; then
# 启动容器
docker start $container_name
fi
if [ "$1" = "logs" ]; then
docker logs -f $container_name
elif [ "$1" = "start" ]; then
# docker exec -itd $container_name bash -c "php start.php start"
:
elif [ "$1" = "restart" ]; then
docker restart $container_name
elif [ "$1" = "status" ]; then
docker exec -it $container_name bash -c "php start.php status"
elif [ "$1" = "reload" ]; then
docker exec -it $container_name bash -c "php start.php reload"
elif [ "$1" = "connections" ]; then
docker exec -it $container_name bash -c "php start.php connections"
else
echo "Invalid command"
echo "Usage: $0 [start|stop|logs|restart|status|reload|connections]"
exit 1
fi
fi

View File

@ -380,7 +380,7 @@ function copy_dir(string $source, string $dest, bool $overwrite = false)
$files = scandir($source); $files = scandir($source);
foreach ($files as $file) { foreach ($files as $file) {
if ($file !== "." && $file !== "..") { if ($file !== "." && $file !== "..") {
copy_dir("$source/$file", "$dest/$file"); copy_dir("$source/$file", "$dest/$file", $overwrite);
} }
} }
} else if (file_exists($source) && ($overwrite || !file_exists($dest))) { } else if (file_exists($source) && ($overwrite || !file_exists($dest))) {
@ -515,3 +515,14 @@ function cpu_count(): int
} }
return $count > 0 ? $count : 4; return $count > 0 ? $count : 4;
} }
/**
* get GET or POST request parameters, if no parameter name is passed, an array of all values is returned, default values is supported
* @param string|null $param param's name
* @param string|null $default default value
* @return mixed|null
*/
function input(string $param = null, string $default = null)
{
return is_null($param) ? request()->all() : request()->input($param, $default);
}

View File

@ -36,8 +36,20 @@ foreach (config('plugin', []) as $firm => $projects) {
if (!is_array($project)) { if (!is_array($project)) {
continue; continue;
} }
foreach ($project['command'] ?? [] as $command) { foreach ($project['command'] ?? [] as $class_name) {
$cli->add(Container::get($command)); $reflection = new \ReflectionClass($class_name);
if ($reflection->isAbstract()) {
continue;
}
$properties = $reflection->getStaticProperties();
$name = $properties['defaultName'];
if (!$name) {
throw new RuntimeException("Command {$class_name} has no defaultName");
}
$description = $properties['defaultDescription'] ?? '';
$command = Container::get($class_name);
$command->setName($name)->setDescription($description);
$cli->add($command);
} }
} }
} }

View File

@ -61,6 +61,7 @@ function write_process_file($runtimeProcessPath, $processName, $firm): string
require_once __DIR__ . '/../../vendor/autoload.php'; require_once __DIR__ . '/../../vendor/autoload.php';
use Workerman\Worker; use Workerman\Worker;
use Workerman\Connection\TcpConnection;
use Webman\Config; use Webman\Config;
use support\App; use support\App;
@ -77,6 +78,7 @@ worker_start('$processParam', $configParam);
if (DIRECTORY_SEPARATOR != "/") { if (DIRECTORY_SEPARATOR != "/") {
Worker::\$logFile = config('server')['log_file'] ?? Worker::\$logFile; Worker::\$logFile = config('server')['log_file'] ?? Worker::\$logFile;
TcpConnection::\$defaultMaxPackageSize = config('server')['max_package_size'] ?? 10*1024*1024;
} }
Worker::runAll(); Worker::runAll();

View File

@ -1,15 +0,0 @@
#!/bin/bash
# 读取docker.conf文件
source docker.conf
# 设置系统环境变量
export WORKERMAN_CONTAINER_NAME=$container_name
export WORKERMAN_IMAGE_VERSION=$image_version
# 判断WORKERMAN_CONTAINER_NAME的docker容器是否存在如果存在删除它
if docker ps -a --format '{{.Names}}' | grep -q "^$WORKERMAN_CONTAINER_NAME$"; then
docker rm -f $WORKERMAN_CONTAINER_NAME
fi
docker compose run --rm $WORKERMAN_CONTAINER_NAME composer "$@"

19
console
View File

@ -1,19 +0,0 @@
#!/bin/bash
# 读取docker.conf文件
source docker.conf
# 设置系统环境变量
export WORKERMAN_CONTAINER_NAME=$container_name
export WORKERMAN_IMAGE_VERSION=$image_version
# 判断WORKERMAN_CONTAINER_NAME的docker容器是否存在如果存在删除它
if docker ps -a --format '{{.Names}}' | grep -q "^$WORKERMAN_CONTAINER_NAME$"; then
docker rm -f $WORKERMAN_CONTAINER_NAME
fi
if [[ $1 == "build" ]]; then
docker compose run --rm $WORKERMAN_CONTAINER_NAME php webman build:bin $image_version && cp .env build/
else
docker compose run --rm $WORKERMAN_CONTAINER_NAME php webman "$@"
fi

View File

@ -1,15 +1,26 @@
version: "3.1" version: "3.1"
services: services:
webman-jsonrpc: api:
image: wandoubaba517/workerman:${WORKERMAN_IMAGE_VERSION} image: wandoubaba517/workerman:8.1
container_name: webman-jsonrpc container_name: webman-jsonrpc-api
network_mode: host network_mode: host
restart: always restart: always
volumes: volumes:
- ./:/app/service - ./api/:/app/service
- ./php.ini:/usr/local/etc/php/php.ini - ./api/php.ini:/usr/local/etc/php/php.ini
working_dir: /app/service working_dir: /app/service
stdin_open: true stdin_open: true
command: ["php", "start.php", "start"] depends_on:
- log
command: ['php', 'start.php', 'start']
log:
image: mongo:6.0
container_name: webman-jsonrpc-log
restart: always
volumes:
- ./log/db:/data/db
- ./log/mongod.conf:/etc/mongod.conf
network_mode: host
command: ['mongod', '--config', '/etc/mongod.conf']

View File

@ -1,2 +0,0 @@
container_name=webman-jsonrpc
image_version=8.1

2
log/.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
!.gitignore
db/*

40
log/mongod.conf Normal file
View File

@ -0,0 +1,40 @@
# mongod.conf
# for documentation of all options, see:
# http://docs.mongodb.org/manual/reference/configuration-options/
# Where and how to store data.
storage:
dbPath: /data/db
# engine:
# wiredTiger:
# where to write logging data.
# systemLog:
# destination: file
# logAppend: true
# path: /var/log/mongodb/mongod.log
# network interfaces
net:
port: 27019
bindIp: 0.0.0.0
# how the process runs
# processManagement:
# timeZoneInfo: /usr/share/zoneinfo
#security:
#operationProfiling:
#replication:
#sharding:
## Enterprise-Only Options:
#auditLog:
#snmp:

61
start
View File

@ -1,61 +0,0 @@
#!/bin/bash
# 读取docker.conf文件
source docker.conf
# 设置系统环境变量
export WORKERMAN_CONTAINER_NAME=$container_name
export WORKERMAN_IMAGE_VERSION=$image_version
# 检查docker-compose.yml文件是否存在
if [ ! -f "docker-compose.yml" ]; then
echo "Error: docker-compose.yml file not found"
exit 1
fi
# 定义帮助函数
show_help() {
echo "Usage: ./start [COMMAND]"
echo ""
echo "Commands:"
echo " run once"
echo " start run once"
echo " -d run in daemon mode"
echo " status service status(daemon mode only)"
echo " reload reload files(daemon mode only)"
echo " restart restart service(daemon mode only)"
echo " stop stop service and container"
echo " help show this help message"
}
# 定义安装依赖的函数
install_dependencies() {
# 执行一次composer install
docker compose run --rm $WORKERMAN_CONTAINER_NAME composer install
}
# 根据参数执行不同的命令
if [[ $# -eq 0 || $1 == "start" ]]; then
install_dependencies
docker compose run --rm $WORKERMAN_CONTAINER_NAME php start.php start
elif [[ $1 == "-d" ]]; then
install_dependencies
docker compose up -d
elif [[ $1 == "help" ]]; then
show_help
else
# 判断WORKERMAN_CONTAINER_NAME的docker容器是否存在如果不存在直接退出
if ! docker ps -a --format '{{.Names}}' | grep -q "^$WORKERMAN_CONTAINER_NAME$"; then
echo "container not exist."
exit 1
fi
if ! docker ps --format '{{.Names}}' | grep -q "^$WORKERMAN_CONTAINER_NAME$"; then
echo "container is not running."
exit 1
fi
# 执行php start命令
docker exec -it $WORKERMAN_CONTAINER_NAME php start.php "$@"
if [[ $1 == "stop" ]]; then
docker stop $WORKERMAN_CONTAINER_NAME
fi
fi