├── .gitignore ├── main.php ├── app ├── common │ ├── Constants.php │ └── Error.php ├── api │ └── module │ │ ├── admin │ │ └── Console.php │ │ └── home │ │ └── Api.php ├── cache │ └── SampleCache.php ├── task │ └── SampleTask.php └── server │ └── HttpServer.php ├── config └── debug │ ├── route.php │ ├── config.php │ ├── component.php │ └── pool.php ├── composer.json ├── run.php └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | .idea/ 2 | vendor/ 3 | composer.lock 4 | .DS_Store 5 | *.pid -------------------------------------------------------------------------------- /main.php: -------------------------------------------------------------------------------- 1 | [ 10 | '/home/Api/testApi', 11 | '/home/Api/testMulti', 12 | '/home/Api/testCache', 13 | '/home/Api/testTimer', 14 | 15 | '/home/Index/index', 16 | 17 | '/admin/Console/stats', 18 | ] 19 | ]; -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "cat-sys/cat-api-app", 3 | "description": "Cat Http API Application", 4 | "authors": [ 5 | { 6 | "name": "lidanyang", 7 | "email": "lancelot2014@foxmail.com" 8 | } 9 | ], 10 | "require": { 11 | "cat-sys/cat-api": "v0.1.0" 12 | }, 13 | "autoload": { 14 | "psr-4": { 15 | "app\\":"app/" 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /app/api/module/admin/Console.php: -------------------------------------------------------------------------------- 1 | Error::SUCCESS, 25 | 'data' => $this->request->getSocket()->stats() 26 | ]; 27 | } 28 | 29 | public function shutdown() 30 | { 31 | $this->request->getSocket()->shutdown(); 32 | } 33 | } -------------------------------------------------------------------------------- /app/cache/SampleCache.php: -------------------------------------------------------------------------------- 1 | id = Constants::CACHE_SAMPLE; 23 | $this->tick = 60; // 每60个tick更新一次,即600s 24 | } 25 | 26 | public function load(Promise $promise) 27 | { 28 | // 更新缓存数据, 结果使用$promise->resolve()返回 29 | 30 | $promise->resolve([ 31 | 'code' => Error::SUCCESS, 32 | 'data' => "Hello World" 33 | ]); 34 | } 35 | 36 | 37 | } 38 | -------------------------------------------------------------------------------- /config/debug/config.php: -------------------------------------------------------------------------------- 1 | true, 11 | 12 | 'project'=>array( 13 | 'pid_path' => __DIR__ . '/../../', 14 | 'project_name' => 'micro_service', 15 | 16 | 'ctrl_path' => 'app\\api\\module', 17 | 'main_callback' => "app\\server\\HttpServer", 18 | ), 19 | 20 | 'server' => array( 21 | 'host' => '0.0.0.0', 22 | 'port' => 9501, 23 | 24 | 'socket_type' => 'http', 25 | ), 26 | 27 | 'swoole' => [ 28 | 'daemonize' => 0, 29 | 30 | 'worker_num' => 1, 31 | 'dispatch_mode' => 2, 32 | 33 | 'task_worker_num' => 1, 34 | 35 | 'package_max_length' => 524288, 36 | ], 37 | ); 38 | -------------------------------------------------------------------------------- /app/task/SampleTask.php: -------------------------------------------------------------------------------- 1 | redis_pool = PoolManager::getInstance()->get('redis_master'); 23 | } 24 | 25 | public function sample_task($name, $id) 26 | { 27 | //TODO: 实现任务逻辑 28 | $redis_result = yield $this->redis_pool->pop()->get('cache'); 29 | Globals::var_dump($redis_result['data']); 30 | 31 | return [ 32 | 'code' => Error::SUCCESS, 33 | 'data' => $redis_result 34 | ]; 35 | } 36 | } -------------------------------------------------------------------------------- /config/debug/component.php: -------------------------------------------------------------------------------- 1 | [ 14 | /** 15 | * 日志组件 16 | */ 17 | 'log' => [ 18 | 'open_log' => true, // 是否开启日志 19 | 'adapter' => 'Debug', // 日志模块 20 | 'log_level' => 1, // 日志记录等级 21 | 22 | // 各个模块的定制化配置 23 | 'path' => '/var/log/', // 日志文件存储路径 24 | ], 25 | 26 | /** 27 | * 异步任务组件 28 | */ 29 | 'task' => [ 30 | 'open_task' => true, // 是否开启异步任务模块 31 | 'task_path' => '/app/task/', // 任务文件路径 32 | ], 33 | 34 | /** 35 | * 内存缓存组件 36 | */ 37 | 'cache' => [ 38 | 'open_cache' => true, // 是否开启内存缓存模块 39 | 'cache_tick' => 10000, // 缓存刷新的间隔时间,单位为ms 40 | 'cache_path' => '/app/cache/', // 缓存更新文件的路径 41 | ] 42 | ] 43 | ]; -------------------------------------------------------------------------------- /app/api/module/home/Api.php: -------------------------------------------------------------------------------- 1 | mysql_pool = PoolManager::getInstance()->get('mysql_master'); 26 | $this->redis_pool = PoolManager::getInstance()->get('redis_master'); 27 | } 28 | 29 | public function testApi() 30 | { 31 | $redis_result = yield $this->redis_pool->pop()->multi(Redis::PIPELINE); 32 | if($redis_result['code'] != Error::SUCCESS) 33 | { 34 | return $this->error($redis_result['code']); 35 | } 36 | return $redis_result; 37 | } 38 | 39 | public function testMulti() 40 | { 41 | $task = new AsyncTask("SampleTask"); 42 | $result = yield $task->sample_task("hello", 1); 43 | return $result; 44 | } 45 | 46 | public function testCache() 47 | { 48 | 49 | } 50 | 51 | public function testHttp() 52 | { 53 | 54 | } 55 | 56 | public function testTimer() 57 | { 58 | 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /run.php: -------------------------------------------------------------------------------- 1 | [ 17 | /** 18 | * MySQL 连接池 19 | */ 20 | 'mysql_master' => [ 21 | 'type' => 'mysql', // 连接池类型 22 | 'size' => 5, // 连接池大小 23 | 24 | 'args' => [ // 连接参数 25 | 'host' => '127.0.0.1', // 主机名 26 | 'port' => 3306, // 端口号 27 | 'user' => 'root', // 用户名 28 | 'password' => '123456', // 密码 29 | 'database' => 'Test', // 数据库名称 30 | 'open_log' => false 31 | ] 32 | ], 33 | 34 | /** 35 | * Redis 连接池 36 | */ 37 | 'redis_master' => [ 38 | 'type' => 'redis', // 连接池类型 39 | // 'size' => 1, // 默认为 1 连接, 无需设置 40 | 41 | 'args' => [ 42 | 'host' => '127.0.0.1', // 主机名 43 | 'port' => 6379, // 端口号 44 | 'select' => 0, // 库编号 45 | 'pwd' => '123456' // 口令 46 | ] 47 | ], 48 | ] 49 | /*********************** Pool Config end *************************/ 50 | ]; -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## 介绍 2 | Swoole Http API 应用 3 | 4 | ## 依赖 5 | 6 | [CatApi](https://github.com/CatsSystem/CatApi) 7 | 8 | 9 | ## 安装 10 | 11 | ### Composer安装 12 | 13 | ```bash 14 | composer create-project --no-dev cat-sys/cat-api-app {project_name} 15 | ``` 16 | 17 | > 注: 测试阶段请使用 `composer create-project --stability=dev --no-dev cat-sys/cat-api-app {project_name}`命令安装 18 | 19 | 20 | ## 异步API 21 | 22 | ### 异步Task 23 | 24 | ```php 25 | // 实例化异步任务 26 | $task = new AsyncTask('TestTask'); 27 | // 发送任务请求 28 | $result = yield $task->test_task(1, "test", [1, 2, 3 ]); 29 | ``` 30 | 31 | ### Redis访问 32 | 33 | ```php 34 | // 获取连接池 35 | $redis_pool = PoolManager::getInstance()->get('redis_master'); 36 | 37 | // 发起请求 38 | $redis_result = yield $redis_pool->pop()->get('cache'); 39 | 40 | ``` 41 | 42 | ### MySQL访问 43 | 44 | ```php 45 | 46 | // 获取连接池 47 | $mysql_pool = PoolManager::getInstance()->get('mysql_master'); 48 | 49 | // 发起请求 50 | $sql_result = yield MySQLStatement::prepare() 51 | ->select("Test", "*") 52 | ->limit(0,2) 53 | ->query($mysql_pool->pop()); 54 | 55 | ``` 56 | 57 | ### Http请求 58 | 59 | ```php 60 | $http = new Http("www.baidu.com"); 61 | yield $http->init(); 62 | $result = yield $http->get('/'); 63 | 64 | ``` 65 | 66 | ## 环境支持 67 | 68 | * PHP 7
69 | * MySQLi 扩展 70 | * Swoole 1.8.8 以上版本 (不适用于Swoole 2.0以上版本) 71 | * [hiredis 库](https://github.com/redis/hiredis) 72 | * [phpredis 扩展](https://github.com/phpredis/phpredis) 73 | * [swoole_serialize 扩展](https://github.com/swoole/swoole_serialize) 74 | 75 | ## 配置 76 | 77 | 配置文件均在`config`目录下 78 | 79 | ## 运行 80 | 81 | 在项目目录下,执行以下命令 82 | ```bash 83 | php run.php start 84 | ``` 85 | 进入DEBUG模式。 86 | 87 | 执行以下命令 88 | ```bash 89 | php run.php start -c release 90 | ``` 91 | 指定配置文件目录 92 | 93 | ## 请求方式 94 | 95 | 请求使用Http POST方式, 参数格式为JSON编码, `Content-Type`限定为`application/json` 96 | 97 | 访问地址格式为: `http://ip:port/module/controller/method` 98 | 99 | `module`为模块名 100 | `controller`为控制器名 101 | `method`为方法名 102 | 103 | 路由配置在`route.php`配置文件中 -------------------------------------------------------------------------------- /app/server/HttpServer.php: -------------------------------------------------------------------------------- 1 | init('mysql_master'); 31 | PoolManager::getInstance()->init('redis_master'); 32 | 33 | /** 34 | * 初始化内存缓存 35 | */ 36 | $cache_config = Config::getField('component', 'cache'); 37 | CacheLoader::getInstance()->init(Entrance::$rootPath . $cache_config['cache_path'], 38 | $cache_config['cache_path']); 39 | } 40 | 41 | public function beforeStart() 42 | { 43 | // 打开内存Cache进程 44 | $this->openCacheProcess(function(){ 45 | Globals::$server = SwooleServer::getInstance()->getServer(); 46 | Globals::setProcessName(Config::getField('project', 'project_name') . 'cache process'); 47 | $cache_config = Config::getField('component', 'cache'); 48 | 49 | PoolManager::getInstance()->init('mysql_master'); 50 | PoolManager::getInstance()->init('redis_master'); 51 | 52 | CacheLoader::getInstance()->init(Entrance::$rootPath . $cache_config['cache_path'], 53 | $cache_config['cache_path']); 54 | 55 | return $cache_config['cache_tick']; 56 | }); 57 | } 58 | 59 | /** 60 | * @param \swoole_http_request $request 61 | * @param \swoole_http_response $response 62 | */ 63 | public function onRequest(\swoole_http_request $request, \swoole_http_response $response) 64 | { 65 | if( !in_array($request->server['path_info'], Config::get('route'))) { 66 | $response->status(403); 67 | $response->end(""); 68 | return; 69 | } 70 | $path = explode('/' , $request->server['path_info']); 71 | $module = isset( $path[1] ) ? $path[1] : ""; 72 | $controller = isset( $path[2] ) ? $path[2] : ""; 73 | $method = isset( $path[3] ) ? $path[3] : ""; 74 | 75 | $handle = new Request(); 76 | $handle->setRequest($request); 77 | $handle->setResponse($response); 78 | $handle->setSocket($this->server); 79 | $handle->init($module, $controller, $method, 80 | isset( $request->post ) ? $request->post : $request->rawContent()); 81 | 82 | try { 83 | $result = yield Route::route($handle); 84 | $response->header('Content-Type', 'application/json'); 85 | 86 | if( is_array($result) ) { 87 | $result = json_encode($result, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE); 88 | } else if( !is_string($result) ) { 89 | $response->status(503); 90 | $response->end(var_export($result, true)); 91 | return; 92 | } 93 | $response->end($result); 94 | } catch ( \Exception $e ) { 95 | Log::ERROR('Exception', var_export($e, true)); 96 | $response->status(502); 97 | $response->end(""); 98 | } catch ( \Error $e ) { 99 | Log::ERROR('Exception', var_export($e, true)); 100 | $response->status(502); 101 | $response->end(""); 102 | } 103 | } 104 | } 105 | --------------------------------------------------------------------------------