├── .gitignore ├── LICENSE ├── README.md ├── app ├── Controllers │ ├── ConsulTestController.php │ ├── DbTestController.php │ ├── IndexController.php │ ├── RouteTestController.php │ └── RpcTestController.php ├── Listeners │ └── StartListener.php ├── Providers │ ├── RouteServerProvider.php │ └── RpcServerPriovder.php ├── README.md ├── Rpc │ ├── Client │ │ └── TestClient.php │ └── Server │ │ └── TestServer.php └── StopListener.php ├── bin └── darts ├── composer.json ├── config ├── app.php ├── consul.php ├── dartswoole.php ├── database.php ├── event.php ├── http_server.php ├── rpc_client.php └── rpc_server.php ├── public └── index.php ├── route ├── http.php └── ws.php ├── storage ├── app │ └── .gitignore ├── framework │ └── .gitignore └── logs │ └── .gitignore └── tests └── rpc.php /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | .idea 3 | composer.lock 4 | vendor -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 BY 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Darts Framework 2 | 3 | ## 概述 4 | 5 | Darts Framework 是基于 Swoole 开发的一款高新能的微服务框架 6 | 7 | ## Label 8 | 9 | [![php](https://img.shields.io/badge/php-%3E7.0-blue)](https://www.php.net) 10 | [![swoole](https://img.shields.io/badge/swoole-%3E4.5-blue)](https://www.swoole.com) 11 | [![issues](https://img.shields.io/github/issues/jefferyjob/darts)](https://github.com/jefferyjob/darts/issues) 12 | [![GitHub forks](https://img.shields.io/github/forks/jefferyjob/darts)](https://github.com/jefferyjob/darts) 13 | [![GitHub stars](https://img.shields.io/github/stars/jefferyjob/darts)](https://github.com/jefferyjob/darts) 14 | [![GitHub license](https://img.shields.io/github/license/jefferyjob/darts)](https://github.com/jefferyjob/darts/blob/master/LICENSE) 15 | 16 | ## 初次安装 17 | 18 | - 请先将 Library类库 [dartswoole](https://github.com/jefferyjob/dartswoole) 置于 `darts` 项目的同级目录 19 | - 然后执行 `composer install --no-dev` 安装 `dartswoole` 扩展 20 | 21 | ## 性能测试 22 | 23 | **压测方法:** 24 | 25 | ```shell 26 | ab -c 100 -n 1000000 -k http://127.0.0.1:9500/ 27 | ``` 28 | 29 | **压测结果:** 30 | 31 | | 软件 | QPS | 软件版本 | 32 | | ---- | ---- | ---- | 33 | | darts 1.0 | 267.39 | centos7.5(1核2G) / php7.2 / swoole4.6 | 34 | | ThinkPHP 6.0 | xxx | centos7.5(1核2G) / php7.2 / nginx1.18 | 35 | | Laravel 8.0 | xxx | centos7.5(1核2G) / php7.2 / nginx1.18 | 36 | 37 | 38 | ## 目录简介 39 | 40 | ```text 41 | app 42 | -----Controller-------------控制器 43 | -----Providers--------------服务提供者加载 44 | -------------------RouteServerProvider--------路由服务提供者自定义扩展 45 | -------------------RpcServerProvider----------RPC微服务提供者自定义扩展 46 | -----Listeners--------------事件监听绑定 47 | -------------------StartListener--------------swoole启动服务绑定的事件 48 | -------------------StopListener---------------swoole停止服务绑定的事件 49 | -----Rpc--------------------RPC微服务处理 50 | bin 51 | -----darts------------------cli命令操作 52 | config 53 | -----app.php----------------框架主配置文件 54 | -----dartswoole.php---------dartswoole类型初始化加载配置文件 55 | -----event.php--------------事件绑定配置文件 56 | -----http_server.php--------swoole的http服务配置 57 | -----consul.php-------------基于consul开发的rpc微服务 58 | -----rpc_client.php---------项目作为客户端rpc服务配置 59 | -----rpc_server.php---------项目作为服务端rpc服务配置 60 | public 61 | -----index.php--------------项目入口文件 62 | route 63 | -----http.php---------------路由文件 64 | storage 65 | -----app--------------------应用缓存 66 | -----framework--------------框架缓存 67 | -----logs-------------------日志缓存 68 | ``` 69 | 70 | ## HTTP服务 71 | 72 | 基于 swoole 的 HTTP服务器 实现http协议的解析与处理。 73 | 74 | ## RPC微服务 75 | 76 | ### darts中微服务实现的两种方式: 77 | 78 | #### 1、Swoole->TCP 79 | 80 | 使用 swoole 的 TCP 协议使用 `config/rpc_client.php` 配置的rpc服务, 对 RpcServer 和 RpcClient 进行通信。 81 | 82 | 该服务的启动后,读取的配置如下: 83 | - RpcServer ---read---> config/rpc_server.php 84 | - RpcClient ---read---> config/rpc_clien.php 85 | 86 | #### 2、Swoole->Consul->TCP 87 | 88 | RpcClient 首先从 consul 服务中读取健康服务的 ip 和端口,然后使用 swoole 的 TCP 协议和 RpcServer 进行通信。 89 | 90 | 该服务的启动后,读取的配置如下: 91 | - RpcServer ---read---> config/rpc_server.php 92 | - RpcClient ---read---> config/consul.php 93 | 94 | ### 如何测试RPC 95 | 96 | **#1测试** 97 | 98 | 由于 RPC 服务的实现,需要一个客户端(client)和一个服务端(server)。所以对于 darts 项目需要准备两份,一个充当客户端一个充当服务端。 99 | 100 | 101 | 客户端(client)项目: 102 | - 在 `config/rpc_server.php` 中 `flag` 设为 `false`,关闭RPC服务 103 | 104 | 服务端(server)项目: 105 | - 在 `config/rpc_server.php` 中 `flag` 设为 `true`,关闭RPC服务 106 | 107 | 108 | **#2测试** 109 | 110 | - 在 `config/rpc_server.php` 中 `flag` 设为 `true`,开启RPC服务 111 | - 在 `config/dartswoole.php` 中取消 `Dartswoole\Consul\ConsulServerPriovder::class` 的注释,开放 consul 服务的加载 112 | 113 | 114 | ## 相关问题 115 | 116 | ### 为什么真实连接大于配置的数据库连接数量 117 | 118 | 在对数据库压测过程中,我们用 `SHOW PROCESSLIST;` 查看数据库的连接数量,会发现真实的连接远大于在配置文件中对于连接池的配置。 119 | 因为在服务器中,darts 项目启动后,会根据服务器的核心数,启动对应数量的 [Worker](https://wiki.swoole.com/#/server/setting?id=worker_num) 进程,而每个 `Worker` 进程是相互独立的,如果服务器的核心数是 s,数据库进程池中配置的数量是 n,那么查询连接数是 s*n。服务器中查看 Worker 进程的命令是:`pstree -ap | grep darts` 120 | 121 | ## License 122 | 123 | 遵循 MIT 许可证,有关详细请参阅 LICENSE。 -------------------------------------------------------------------------------- /app/Controllers/ConsulTestController.php: -------------------------------------------------------------------------------- 1 | services(); 19 | return json_encode($result); 20 | } 21 | 22 | public function health() 23 | { 24 | $result = app('consul-agent')->health('darts_server'); 25 | return json_encode($result); 26 | } 27 | 28 | public function rpc() 29 | { 30 | return (new TestClient())->json(); 31 | } 32 | } -------------------------------------------------------------------------------- /app/Controllers/DbTestController.php: -------------------------------------------------------------------------------- 1 | json(); 11 | } 12 | 13 | public function rpc2() 14 | { 15 | return (new TestClient())->str(); 16 | } 17 | } -------------------------------------------------------------------------------- /app/Listeners/StartListener.php: -------------------------------------------------------------------------------- 1 | app->make('config')->get('rpc_server.flag')) { 19 | Coroutine::create(function (){ 20 | $this->registerConsul(); 21 | }); 22 | } 23 | } 24 | 25 | /** 26 | * 注册 consul 服务 27 | */ 28 | public function registerConsul() 29 | { 30 | // 获取 consul 服务 31 | $consul = $this->app->make('consul-agent'); 32 | 33 | // 获取 consul 配置信息 34 | $config = $this->app->make('config')->get('consul.service'); 35 | 36 | // 注册 consul 服务 37 | $consul->registerService($config); 38 | 39 | // echo 40 | Debug::info('注册 consul 服务 ===== Success'); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /app/Providers/RouteServerProvider.php: -------------------------------------------------------------------------------- 1 | mapRouteHttp(); 11 | $this->mapRouteWs(); 12 | 13 | parent::boot(); 14 | } 15 | 16 | /** 17 | * http 路由服务 18 | */ 19 | public function mapRouteHttp() 20 | { 21 | $this->map['http'] = array( 22 | 'namespace' => 'App\Controllers', 23 | 'path' => $this->app->getBasePath()."/route/http.php", 24 | 'flag' => 'http' 25 | ); 26 | } 27 | 28 | /** 29 | * webSocket 路由服务 30 | */ 31 | public function mapRouteWs() 32 | { 33 | $this->map['ws'] = array( 34 | 'namespace' => 'App\Controllers', 35 | 'path' => $this->app->getBasePath()."/route/ws.php", 36 | 'flag' => 'ws' 37 | ); 38 | } 39 | } -------------------------------------------------------------------------------- /app/Providers/RpcServerPriovder.php: -------------------------------------------------------------------------------- 1 | services = function ($consul_name) { 14 | $services = $this->app->make('consul-agent')->health($consul_name); 15 | $address = array(); 16 | foreach ($services as $key => $value) { 17 | $address[] = array( 18 | 'host' => $value["Service"]["Address"], 19 | 'port' => $value["Service"]["Port"] 20 | ); 21 | } 22 | return $address; 23 | }; 24 | } 25 | 26 | public function boot() 27 | { 28 | parent::boot(); 29 | } 30 | } -------------------------------------------------------------------------------- /app/README.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jefferyjob/darts/39e6e9c6f5e8026dbf497bbc8dd4d012b80596a8/app/README.md -------------------------------------------------------------------------------- /app/Rpc/Client/TestClient.php: -------------------------------------------------------------------------------- 1 | '张三' 9 | ); 10 | } 11 | 12 | public function str() { 13 | return 'this is string'; 14 | } 15 | 16 | public function json() { 17 | return json_encode(array( 18 | 'age' => 20, 19 | 'class' => 19 20 | )); 21 | } 22 | } -------------------------------------------------------------------------------- /app/StopListener.php: -------------------------------------------------------------------------------- 1 | app->make('config')->get('rpc_server.flag')) { 18 | Coroutine::create(function (){ 19 | $this->deregisterService(); 20 | }); 21 | } 22 | } 23 | 24 | /** 25 | * 注销 consul 服务 26 | */ 27 | public function deregisterService() 28 | { 29 | // 获取 consul 服务 30 | $consul = $this->app->make('consul-agent'); 31 | 32 | // 获取 consul 配置信息 33 | $config = $this->app->make('config')->get('consul.service'); 34 | 35 | // 注册 consul 服务 36 | $consul->deregisterService($config['ID']); 37 | 38 | // echo 39 | Debug::info('注销注销注销 consul 服务'); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /bin/darts: -------------------------------------------------------------------------------- 1 | run($argv); -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "jefferyjob/darts", 3 | "description": "Darts Framework", 4 | "type": "project", 5 | "license": "MIT", 6 | "authors": [ 7 | { 8 | "name": "libin", 9 | "email": "libinjob@163.com" 10 | } 11 | ], 12 | "autoload": { 13 | "psr-4": { 14 | "App\\": "app" 15 | } 16 | }, 17 | "repositories": { 18 | "dartswoole": { 19 | "type": "path", 20 | "url": "../dartswoole" 21 | } 22 | }, 23 | "minimum-stability": "dev", 24 | "require": { 25 | "php": ">=7.0", 26 | "jefferyjob/dartswoole": "dev-master" 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /config/app.php: -------------------------------------------------------------------------------- 1 | array( 11 | 'host' => '127.0.0.1', 12 | 'port' => 8500, 13 | ), 14 | 15 | /** 16 | * consul 服务信息 17 | */ 18 | 'service' => array( 19 | // 服务id 20 | 'ID' => "darts_server_1", 21 | // 服务名称 22 | 'Name' => "darts_server", 23 | 'Tags' => [ 24 | 'rpc-server' 25 | ], 26 | // 服务ip 27 | 'Address' => "127.0.0.1", 28 | // 服务端口 29 | 'Port' => 9600, 30 | 31 | // 健康检查 32 | // "Check" => [ 33 | // "name" => "swoft.goods.server", 34 | // // 192.168.169.200 这是服务宿主机地址 35 | // "tcp" => "192.168.169.200:".env("CONSUL_CHECK_PORT"), 36 | // "interval" => '10s', 37 | // "timeout" => '2s' 38 | // ], 39 | ) 40 | 41 | ); -------------------------------------------------------------------------------- /config/dartswoole.php: -------------------------------------------------------------------------------- 1 | [ 14 | 15 | /** 16 | * dartswoole 类库中的核心服务 17 | */ 18 | // 事件服务 19 | Dartswoole\Event\EventServerPriovder::class, 20 | // 数据库服务 21 | Dartswoole\Database\DatabaseServerPriovder::class, 22 | // consul微服务治理服务 23 | // Dartswoole\Consul\ConsulServerPriovder::class, 24 | 25 | /** 26 | * dartswoole 类库中的某些服务的扩展重写 27 | */ 28 | // 路由服务 29 | App\Providers\RouteServerProvider::class, 30 | // RPC实现服务 31 | App\Providers\RpcServerPriovder::class, 32 | 33 | ], 34 | ); -------------------------------------------------------------------------------- /config/database.php: -------------------------------------------------------------------------------- 1 | 'mysql', 7 | 8 | 'mysql' => array( 9 | 'host' => '127.0.0.1', 10 | 'port' => 3306, 11 | 'database' => 'test', 12 | 'username' => 'root', 13 | 'password' => 'rootroot', 14 | 15 | // 连接池配置 16 | 'pool' => [ 17 | 'size' => 10 18 | ] 19 | 20 | ) 21 | ); -------------------------------------------------------------------------------- /config/event.php: -------------------------------------------------------------------------------- 1 | [ 13 | [ 14 | "path" => "/app/Listeners", 15 | "namespace" => "App\\Listeners\\" 16 | ], 17 | ], 18 | "events" => [ 19 | \App\StopListener::class, 20 | ] 21 | ]; 22 | -------------------------------------------------------------------------------- /config/http_server.php: -------------------------------------------------------------------------------- 1 | '0.0.0.0', 8 | 9 | // 访问端口 10 | 'port' => 9500, 11 | ); -------------------------------------------------------------------------------- /config/rpc_client.php: -------------------------------------------------------------------------------- 1 | [ 9 | "host" => "127.0.0.1", 10 | "port" => 9600 11 | ], 12 | ); -------------------------------------------------------------------------------- /config/rpc_server.php: -------------------------------------------------------------------------------- 1 | false, 9 | 10 | // 服务类型定义 11 | 'type' => SWOOLE_SOCK_TCP, 12 | 13 | // ip定义 14 | 'host' => '127.0.0.1', 15 | 16 | // 端口定义 17 | 'port' => 9600, 18 | 19 | // swoole 相关配置 20 | 'swoole' => [ 21 | 22 | ] 23 | ); -------------------------------------------------------------------------------- /public/index.php: -------------------------------------------------------------------------------- 1 | connect('127.0.0.1', 9600, 0.5)) 14 | { 15 | echo "connect failed. Error: {$client->errCode}\n"; 16 | } 17 | $client->send(json_encode(array( 18 | 'method' => '\App\Rpc\Server\TestServer::json', 19 | 'params' => array() 20 | ))); 21 | echo $client->recv(); 22 | $client->close(); 23 | --------------------------------------------------------------------------------