基于 Yii 2.0 实现的命令行脚本,持续运行期间,占用内存过高的分析与解决
1、当在 MySQL 与 Redis 中的数据量较小时,Docker 容器的 CPU:0.02%,内存:298MB,如图1、图2
2、查看 MySQL 的实例监控情况,内存占用:2328MB,如图3
3、查看 Redis 的实例监控情况,已使用容量:33MB,如图4
4、命令行脚本 \console\controllers\CmcConsoleUserController.php 、\console\controllers\ConfigColumnUserController.php 代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 | <?php /** * Created by PhpStorm. * User: Qiang Wang * Date: 2019/01/09 * Time: 13:55 */ namespace console\controllers; use Yii; use console\models\redis\cmc_console\User as RedisCmcConsoleUser; use console\services\CmcConsoleUserService; use yii\console\Controller; use yii\console\ExitCode; use yii\helpers\ArrayHelper; use yii\web\HttpException; use yii\web\ServerErrorHttpException; use common\logics\http\im\IMIndependentMode; /** * 框架服务控制台的用户 * * @author Qiang Wang <shuijingwanwq@163.com> * @since 1.0 */ class CmcConsoleUserController extends Controller { /** * 同步框架服务控制台的用户模型(Http)至框架服务控制台的用户模型(Redis) * * @throws ServerErrorHttpException * @throws HttpException 如果登录超时 */ public function actionSync() { // 查询框架服务控制台的用户模型(Redis)的租户ID(以租户 ID 索引结果集) $redisCmcConsoleUserIndexByGroupIdItems = RedisCmcConsoleUser::find()->indexBy( 'group_id' )->all(); /* 判断 $redisCmcConsoleUserIndexByGroupIdItems 是否为空,如果为空,则成功退出 */ if ( empty ( $redisCmcConsoleUserIndexByGroupIdItems )) { // 延缓执行 60 * 60 秒 sleep(Yii:: $app ->params[ 'cmcConsoleUser' ][ 'isEmptyYesSleepTime' ]); return ExitCode::OK; } foreach ( $redisCmcConsoleUserIndexByGroupIdItems as $redisCmcConsoleUserIndexByGroupIdItemKey => $redisCmcConsoleUserIndexByGroupIdItemValue ) { $isLockExist = RedisCmcConsoleUser::isLockExist( $redisCmcConsoleUserIndexByGroupIdItemKey ); // 返回 true,表示锁定存在,即已经被其他客户端锁定 if ( $isLockExist === true) { continue ; } // HTTP请求,获取租户ID下的用户列表 $httpGetUserListData = [ 'groupId' => $redisCmcConsoleUserIndexByGroupIdItemKey , 'loginId' => '' , 'loginTid' => '' , ]; $getUserListData = CmcConsoleUserService::httpGetUserList( $httpGetUserListData ); // 框架服务控制台的用户模型(Http),重建数组索引(以 ID 索引结果集) $httpCmcConsoleUserItems = ArrayHelper::index( $getUserListData [ 'list' ], 'id' ); // 基于租户ID查询框架服务控制台的用户模型(Redis)(以 ID 索引结果集) $redisCmcConsoleUserItems = RedisCmcConsoleUser::find()->where([ 'group_id' => $redisCmcConsoleUserIndexByGroupIdItemKey ])->indexBy( 'id' )->all(); // 使用键名比较计算数组的差集,如果不为空,则删除出现在 Redis 中但是未出现在 Http 中的记录 $redisArrayDiffItems = array_diff_key ( $redisCmcConsoleUserItems , $httpCmcConsoleUserItems ); if (! empty ( $redisArrayDiffItems )) { $redisArrayDiffIds = array_keys ( $redisArrayDiffItems ); if (RedisCmcConsoleUser::deleteAllByIds( $redisCmcConsoleUserIndexByGroupIdItemKey , $redisArrayDiffIds ) === false) { continue ; } } $im = new IMIndependentMode(); // 遍历框架服务控制台的用户模型(Http),判断在 Redis 中是否存在,如果不存在,则插入,如果存在且更新时间不相等,则更新 foreach ( $httpCmcConsoleUserItems as $httpCmcConsoleUserItemValue ) { $redisCmcConsoleUserItem = RedisCmcConsoleUser::find()->where([ 'id' => $httpCmcConsoleUserItemValue [ 'id' ] ])->one(); $attributes = [ 'id' => $httpCmcConsoleUserItemValue [ 'id' ], 'group_id' => $getUserListData [ 'group_info' ][ 'group_id' ], 'login_name' => $httpCmcConsoleUserItemValue [ 'login_name' ], 'user_token' => $httpCmcConsoleUserItemValue [ 'user_token' ], 'user_nick' => $httpCmcConsoleUserItemValue [ 'user_nick' ], 'user_pic' => $httpCmcConsoleUserItemValue [ 'user_pic' ], 'user_mobile' => $httpCmcConsoleUserItemValue [ 'user_mobile' ] ? $httpCmcConsoleUserItemValue [ 'user_mobile' ] : '' , 'user_email' => $httpCmcConsoleUserItemValue [ 'user_email' ] ? $httpCmcConsoleUserItemValue [ 'user_email' ] : '' , 'user_sex' => $httpCmcConsoleUserItemValue [ 'user_sex' ], 'user_type' => $httpCmcConsoleUserItemValue [ 'user_type' ], 'user_birthday' => $httpCmcConsoleUserItemValue [ 'user_birthday' ], 'user_chat_id' => $httpCmcConsoleUserItemValue [ 'user_chat_id' ] ? $httpCmcConsoleUserItemValue [ 'user_chat_id' ] : '' , 'is_open' => $httpCmcConsoleUserItemValue [ 'is_open' ], 'add_time' => $httpCmcConsoleUserItemValue [ 'add_time' ], 'update_time' => $httpCmcConsoleUserItemValue [ 'update_time' ], 'im_identity' => md5( $getUserListData [ 'group_info' ][ 'group_id' ] . $httpCmcConsoleUserItemValue [ 'login_name' ]), ]; if (!isset( $redisCmcConsoleUserItem )) { $redisCmcConsoleUser = new RedisCmcConsoleUser(); $redisCmcConsoleUser ->attributes = $attributes ; $redisCmcConsoleUser ->insert(); } else if (isset( $redisCmcConsoleUserItem ) && $httpCmcConsoleUserItemValue [ 'update_time' ] != $redisCmcConsoleUserItem [ 'update_time' ]) { $redisCmcConsoleUserItem ->attributes = $attributes ; $redisCmcConsoleUserItem ->save(); } $this ->addIMAccount( $im , $attributes [ 'im_identity' ], $attributes [ 'user_nick' ], $attributes [ 'user_pic' ]); } } // 延缓执行 60 秒 sleep(Yii:: $app ->params[ 'cmcConsoleUser' ][ 'isEmptyNoSleepTime' ]); return ExitCode::OK; } private function addIMAccount(IMIndependentMode $im , $identifier , $nick , $faceUrl ) { $im ->generateUserSig(); $ret = $im ->getAccountProfile( $identifier ); if (isset( $ret [ 'ActionStatus' ]) && $ret [ 'ActionStatus' ] != 'OK' ) { $im ->accountImport( $identifier , $nick , $faceUrl ); } } } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 | <?php /** * Created by PhpStorm. * User: Qiang Wang * Date: 2019/03/01 * Time: 13:17 */ namespace console\controllers; use Yii; use console\models\redis\cmc_console\User as RedisCmcConsoleUser; use console\models\ConfigColumnUser; use yii\console\Controller; use yii\console\ExitCode; /** * 栏目人员配置 * * @author Qiang Wang <shuijingwanwq@163.com> * @since 1.0 */ class ConfigColumnUserController extends Controller { /** * 同步框架服务控制台的用户模型(Redis)至栏目人员配置模型(MySQL) * */ public function actionSync() { // 查询框架服务控制台的用户模型(Redis)的租户ID(以租户 ID 索引结果集) $redisCmcConsoleUserIndexByGroupIdItems = RedisCmcConsoleUser::find()->indexBy( 'group_id' )->all(); /* 判断 $redisCmcConsoleUserIndexByGroupIdItems 是否为空,如果为空,则成功退出 */ if ( empty ( $redisCmcConsoleUserIndexByGroupIdItems )) { // 延缓执行 60 * 60 秒 sleep(Yii:: $app ->params[ 'configColumnUser' ][ 'isEmptyYesSleepTime' ]); return ExitCode::OK; } foreach ( $redisCmcConsoleUserIndexByGroupIdItems as $redisCmcConsoleUserIndexByGroupIdItemKey => $redisCmcConsoleUserIndexByGroupIdItemValue ) { $isLockExist = RedisCmcConsoleUser::isLockExist( $redisCmcConsoleUserIndexByGroupIdItemKey ); // 返回 true,表示锁定存在,即已经被其他客户端锁定 if ( $isLockExist === true) { continue ; } // 基于租户ID查询框架服务控制台的用户模型(Redis)(以 ID 索引结果集) $redisCmcConsoleUserItems = RedisCmcConsoleUser::find()->where([ 'group_id' => $redisCmcConsoleUserIndexByGroupIdItemKey ])->indexBy( 'id' )->all(); // 基于租户ID查询栏目人员配置模型(MySQL)(以 用户ID 索引结果集) $configColumnUserItems = ConfigColumnUser::find()->where([ 'group_id' => $redisCmcConsoleUserIndexByGroupIdItemKey ])->isDeletedNo()->indexBy( 'user_id' )->all(); // 使用键名比较计算数组的差集,如果不为空,则删除 (软删除) 出现在 栏目人员配置模型(MySQL) 中但是未出现在 框架服务控制台的用户模型(Redis) 中的记录 $diffItems = array_diff_key ( $configColumnUserItems , $redisCmcConsoleUserItems ); if (! empty ( $diffItems )) { foreach ( $diffItems as $diffItem ) { /* @var $diffItem \console\models\ConfigColumnUser */ $diffItem ->softDelete(); } continue ; } } // 延缓执行 60 秒 sleep(Yii:: $app ->params[ 'configColumnUser' ][ 'isEmptyNoSleepTime' ]); return ExitCode::OK; } } |
5、命令行脚本的运行基于 Supervisor 提供支持,/etc/supervisord.d/yii-cmc-console-user-sync.ini、/etc/supervisord.d/yii-config-column-user-sync.ini,如图5
1 2 3 4 5 6 7 | [program:yii-cmc-console-user-sync] command = php /sobey/www/pcs-api/yii cmc-console-user/sync autorestart = true startsecs = 0 stopwaitsecs = 10 stderr_logfile = /data/logs/yii-cmc-console-user-sync-stderr.log stdout_logfile = /data/logs/yii-cmc-console-user-sync-stdout.log |
1 2 3 4 5 6 7 | [program:yii-config-column-user-sync] command = php /sobey/www/pcs-api/yii config-column-user/sync autorestart = true startsecs = 0 stopwaitsecs = 10 stderr_logfile = /data/logs/yii-config-column-user-sync-stderr.log stdout_logfile = /data/logs/yii-config-column-user-sync-stdout.log |
6、在 8 个租户下批量导入用户数据,让每个租户下的导入的用户数量为 999 个,总计 8000 左右,如图6
租户名称:广东省、广州市、深圳市、南山区、罗湖区、蛇口街道、招商街道、DEFAULT租户
7、在本地环境的 Redis 中,用户数量为 8223,在开发环境的 Redis 中,对接的同一个框架,但用户数量一直为 3000 左右,始终未达到 8223,如图7、图8
8、最终发现问题所在,原因在于有 2 个容器在同时运行,每个容器中皆在运行命令行:cmc-console-user/sync,数据相互冲突覆盖,停止掉另一个容器中的命令行脚本(计划后续将命令行单独部署至一个容器中,隔离开,以为后续集群部署做准备),如图9
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | [root@79af01f496bb pcs-api]# supervisorctl status cronolog RUNNING pid 426, uptime 2:08:17 nginx RUNNING pid 422, uptime 2:08:17 php-fpm RUNNING pid 424, uptime 2:08:17 yii-cmc-console-user-sync RUNNING pid 20685, uptime 0:04:42 yii-config-column-user-sync RUNNING pid 23504, uptime 0:00:45 [root@79af01f496bb pcs-api]# supervisorctl stop yii-cmc-console-user-sync yii-cmc-console-user-sync: stopped [root@79af01f496bb pcs-api]# supervisorctl status cronolog RUNNING pid 426, uptime 2:08:41 nginx RUNNING pid 422, uptime 2:08:41 php-fpm RUNNING pid 424, uptime 2:08:41 yii-cmc-console-user-sync STOPPED Jul 08 01:32 PM yii-config-column-user-sync RUN |
9、在本地环境的 Redis 中,用户数量为 8223,在开发环境的 Redis 中,用户数量为 8223,已经一致,如图10
10、在 8 个租户的栏目设置中,每个租户下添加 2 个栏目,每个栏目中添加全部用户,即每个租户下添加的数据数量为 2000 左右,总计 16000 左右,如图11
11、在 第 6 步骤执行完毕后,Docker 容器的 CPU:0.05%,内存:382MB,内存增加了 80MB 左右,每 1000 条数据的添加,内存会增加 10 MB 左右。在 第 10 步骤执行完毕后,Docker 容器的 CPU:9.9%,内存:541MB,内存增加了 160MB 左右,每 1000 条数据的添加,内存会增加 20 MB 左右。因此,优化的重点在于第 2 个命令行脚本。如图12、图13
12、编辑 \console\controllers\CmcConsoleUserController.php、\console\controllers\ConfigColumnUserController.php,查看实际使用的内存量、系统分配总的内存量
1 2 3 4 | file_put_contents ( 'E:/wwwroot/pcs-api/console/runtime/logs/cmc-console-user-sync-memory-get-usage-' . time() . '.txt' , memory_get_usage() / 1024 / 1024 . 'MB' ); file_put_contents ( 'E:/wwwroot/pcs-api/console/runtime/logs/cmc-console-user-sync-memory-get-peak-usage-' . time() . '.txt' , memory_get_peak_usage() / 1024 / 1024 . 'MB' ); return ExitCode::OK; |
1 2 3 4 | file_put_contents ( 'E:/wwwroot/pcs-api/console/runtime/logs/config-column-user-sync-memory-get-usage-' . time() . '.txt' , memory_get_usage() / 1024 / 1024 . 'MB' ); file_put_contents ( 'E:/wwwroot/pcs-api/console/runtime/logs/config-column-user-sync-memory-get-peak-usage-' . time() . '.txt' , memory_get_peak_usage() / 1024 / 1024 . 'MB' ); return ExitCode::OK; |
13、运行第 1 个命令行脚本,日志如下:
1 | 8.9912643432617MB |
1 | 40.378349304199MB |
14、运行第 2 个命令行脚本,日志如下:总结:与第 1 个命令行脚本的主要差异在于实际使用的内存量上,相差 17 MB 左右。
1 | 26.464874267578MB |
1 | 40.366722106934MB |
15、分析优化第 1 个命令行脚本,\console\controllers\CmcConsoleUserController.php,基于 unset() 实现,实际使用的内存量减少 2 MB 左右,系统分配总的内存量无变化
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 | <?php /** * Created by PhpStorm. * User: Qiang Wang * Date: 2019/01/09 * Time: 13:55 */ namespace console\controllers; use Yii; use console\models\redis\cmc_console\User as RedisCmcConsoleUser; use console\services\CmcConsoleUserService; use yii\console\Controller; use yii\console\ExitCode; use yii\helpers\ArrayHelper; use yii\web\HttpException; use yii\web\ServerErrorHttpException; use common\logics\http\im\IMIndependentMode; /** * 框架服务控制台的用户 * * @author Qiang Wang <shuijingwanwq@163.com> * @since 1.0 */ class CmcConsoleUserController extends Controller { /** * 同步框架服务控制台的用户模型(Http)至框架服务控制台的用户模型(Redis) * * @throws ServerErrorHttpException * @throws HttpException 如果登录超时 */ public function actionSync() { // 查询框架服务控制台的用户模型(Redis)的租户ID(以租户 ID 索引结果集) $redisCmcConsoleUserIndexByGroupIdItems = RedisCmcConsoleUser::find()->indexBy( 'group_id' )->all(); /* 判断 $redisCmcConsoleUserIndexByGroupIdItems 是否为空,如果为空,则成功退出 */ if ( empty ( $redisCmcConsoleUserIndexByGroupIdItems )) { // 延缓执行 60 * 60 秒 sleep(Yii:: $app ->params[ 'cmcConsoleUser' ][ 'isEmptyYesSleepTime' ]); return ExitCode::OK; } foreach ( $redisCmcConsoleUserIndexByGroupIdItems as $redisCmcConsoleUserIndexByGroupIdItemKey => $redisCmcConsoleUserIndexByGroupIdItemValue ) { $isLockExist = RedisCmcConsoleUser::isLockExist( $redisCmcConsoleUserIndexByGroupIdItemKey ); // 返回 true,表示锁定存在,即已经被其他客户端锁定 if ( $isLockExist === true) { continue ; } // HTTP请求,获取租户ID下的用户列表 $httpGetUserListData = [ 'groupId' => $redisCmcConsoleUserIndexByGroupIdItemKey , 'loginId' => '' , 'loginTid' => '' , ]; $getUserListData = CmcConsoleUserService::httpGetUserList( $httpGetUserListData ); // 框架服务控制台的用户模型(Http),重建数组索引(以 ID 索引结果集) $httpCmcConsoleUserItems = ArrayHelper::index( $getUserListData [ 'list' ], 'id' ); // 基于租户ID查询框架服务控制台的用户模型(Redis)(以 ID 索引结果集) $redisCmcConsoleUserItems = RedisCmcConsoleUser::find()->where([ 'group_id' => $redisCmcConsoleUserIndexByGroupIdItemKey ])->indexBy( 'id' )->all(); // 使用键名比较计算数组的差集,如果不为空,则删除出现在 Redis 中但是未出现在 Http 中的记录 $redisArrayDiffItems = array_diff_key ( $redisCmcConsoleUserItems , $httpCmcConsoleUserItems ); // 销毁变量 unset( $redisCmcConsoleUserItems ); if (! empty ( $redisArrayDiffItems )) { $redisArrayDiffIds = array_keys ( $redisArrayDiffItems ); // 销毁变量 unset( $redisArrayDiffItems ); if (RedisCmcConsoleUser::deleteAllByIds( $redisCmcConsoleUserIndexByGroupIdItemKey , $redisArrayDiffIds ) === false) { continue ; } } $im = new IMIndependentMode(); // 遍历框架服务控制台的用户模型(Http),判断在 Redis 中是否存在,如果不存在,则插入,如果存在且更新时间不相等,则更新 foreach ( $httpCmcConsoleUserItems as $httpCmcConsoleUserItemValue ) { $redisCmcConsoleUserItem = RedisCmcConsoleUser::find()->where([ 'id' => $httpCmcConsoleUserItemValue [ 'id' ]])->one(); $attributes = [ 'id' => $httpCmcConsoleUserItemValue [ 'id' ], 'group_id' => $getUserListData [ 'group_info' ][ 'group_id' ], 'login_name' => $httpCmcConsoleUserItemValue [ 'login_name' ], 'user_token' => $httpCmcConsoleUserItemValue [ 'user_token' ], 'user_nick' => $httpCmcConsoleUserItemValue [ 'user_nick' ], 'user_pic' => $httpCmcConsoleUserItemValue [ 'user_pic' ], 'user_mobile' => $httpCmcConsoleUserItemValue [ 'user_mobile' ] ? $httpCmcConsoleUserItemValue [ 'user_mobile' ] : '' , 'user_email' => $httpCmcConsoleUserItemValue [ 'user_email' ] ? $httpCmcConsoleUserItemValue [ 'user_email' ] : '' , 'user_sex' => $httpCmcConsoleUserItemValue [ 'user_sex' ], 'user_type' => $httpCmcConsoleUserItemValue [ 'user_type' ], 'user_birthday' => $httpCmcConsoleUserItemValue [ 'user_birthday' ], 'user_chat_id' => $httpCmcConsoleUserItemValue [ 'user_chat_id' ] ? $httpCmcConsoleUserItemValue [ 'user_chat_id' ] : '' , 'is_open' => $httpCmcConsoleUserItemValue [ 'is_open' ], 'add_time' => $httpCmcConsoleUserItemValue [ 'add_time' ], 'update_time' => $httpCmcConsoleUserItemValue [ 'update_time' ], 'im_identity' => md5( $getUserListData [ 'group_info' ][ 'group_id' ] . $httpCmcConsoleUserItemValue [ 'login_name' ]), ]; if (!isset( $redisCmcConsoleUserItem )) { $redisCmcConsoleUser = new RedisCmcConsoleUser(); $redisCmcConsoleUser ->attributes = $attributes ; $redisCmcConsoleUser ->insert(); } else { if (isset( $redisCmcConsoleUserItem ) && $httpCmcConsoleUserItemValue [ 'update_time' ] != $redisCmcConsoleUserItem [ 'update_time' ]) { $redisCmcConsoleUserItem ->attributes = $attributes ; $redisCmcConsoleUserItem ->save(); } } $this ->addIMAccount( $im , $attributes [ 'im_identity' ], $attributes [ 'user_nick' ], $attributes [ 'user_pic' ]); } } // 延缓执行 60 秒 sleep(Yii:: $app ->params[ 'cmcConsoleUser' ][ 'isEmptyNoSleepTime' ]); file_put_contents ( 'E:/wwwroot/pcs-api/console/runtime/logs/cmc-console-user-sync-memory-get-usage-' . time() . '.txt' , memory_get_usage() / 1024 / 1024 . 'MB' ); file_put_contents ( 'E:/wwwroot/pcs-api/console/runtime/logs/cmc-console-user-sync-memory-get-peak-usage-' . time() . '.txt' , memory_get_peak_usage() / 1024 / 1024 . 'MB' ); return ExitCode::OK; } private function addIMAccount(IMIndependentMode $im , $identifier , $nick , $faceUrl ) { $im ->generateUserSig(); $ret = $im ->getAccountProfile( $identifier ); if (isset( $ret [ 'ActionStatus' ]) && $ret [ 'ActionStatus' ] != 'OK' ) { $im ->accountImport( $identifier , $nick , $faceUrl ); } } } |
1 2 | 8.9912643432617MB //优化前 6.6167755126953MB //优化后 |
1 2 | 40.378349304199MB //优化前 40.378349304199MB //优化后 |
16、分析优化第 2 个命令行脚本,\console\controllers\ConfigColumnUserController.php,基于 unset() 实现,实际使用的内存量减少 9 MB 左右,系统分配总的内存量无变化
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 | <?php /** * Created by PhpStorm. * User: Qiang Wang * Date: 2019/03/01 * Time: 13:17 */ namespace console\controllers; use Yii; use console\models\redis\cmc_console\User as RedisCmcConsoleUser; use console\models\ConfigColumnUser; use yii\console\Controller; use yii\console\ExitCode; /** * 栏目人员配置 * * @author Qiang Wang <shuijingwanwq@163.com> * @since 1.0 */ class ConfigColumnUserController extends Controller { /** * 同步框架服务控制台的用户模型(Redis)至栏目人员配置模型(MySQL) * */ public function actionSync() { // 查询框架服务控制台的用户模型(Redis)的租户ID(以租户 ID 索引结果集) $redisCmcConsoleUserIndexByGroupIdItems = RedisCmcConsoleUser::find()->indexBy( 'group_id' )->all(); /* 判断 $redisCmcConsoleUserIndexByGroupIdItems 是否为空,如果为空,则成功退出 */ if ( empty ( $redisCmcConsoleUserIndexByGroupIdItems )) { // 延缓执行 60 * 60 秒 sleep(Yii:: $app ->params[ 'configColumnUser' ][ 'isEmptyYesSleepTime' ]); return ExitCode::OK; } foreach ( $redisCmcConsoleUserIndexByGroupIdItems as $redisCmcConsoleUserIndexByGroupIdItemKey => $redisCmcConsoleUserIndexByGroupIdItemValue ) { $isLockExist = RedisCmcConsoleUser::isLockExist( $redisCmcConsoleUserIndexByGroupIdItemKey ); // 返回 true,表示锁定存在,即已经被其他客户端锁定 if ( $isLockExist === true) { continue ; } // 基于租户ID查询框架服务控制台的用户模型(Redis)(以 ID 索引结果集) $redisCmcConsoleUserItems = RedisCmcConsoleUser::find()->where([ 'group_id' => $redisCmcConsoleUserIndexByGroupIdItemKey ])->indexBy( 'id' )->all(); // 基于租户ID查询栏目人员配置模型(MySQL)(以 用户ID 索引结果集) $configColumnUserItems = ConfigColumnUser::find()->where([ 'group_id' => $redisCmcConsoleUserIndexByGroupIdItemKey ])->isDeletedNo()->indexBy( 'user_id' )->all(); // 使用键名比较计算数组的差集,如果不为空,则删除 (软删除) 出现在 栏目人员配置模型(MySQL) 中但是未出现在 框架服务控制台的用户模型(Redis) 中的记录 $diffItems = array_diff_key ( $configColumnUserItems , $redisCmcConsoleUserItems ); // 销毁变量 unset( $redisCmcConsoleUserItems , $configColumnUserItems ); if (! empty ( $diffItems )) { foreach ( $diffItems as $diffItem ) { /* @var $diffItem ConfigColumnUser */ $diffItem ->softDelete(); } continue ; } } // 延缓执行 60 秒 sleep(Yii:: $app ->params[ 'configColumnUser' ][ 'isEmptyNoSleepTime' ]); file_put_contents ( 'E:/wwwroot/pcs-api/console/runtime/logs/config-column-user-sync-memory-get-usage-' . time() . '.txt' , memory_get_usage() / 1024 / 1024 . 'MB' ); file_put_contents ( 'E:/wwwroot/pcs-api/console/runtime/logs/config-column-user-sync-memory-get-peak-usage-' . time() . '.txt' , memory_get_peak_usage() / 1024 / 1024 . 'MB' ); return ExitCode::OK; } } |
1 2 | 26.464874267578MB //优化前 17.696464538574MB //优化后 |
1 2 | 40.366722106934MB //优化前 40.366722106934MB //优化后 |
17、升级至开发环境,Docker 容器的 CPU:5.4%,内存:371MB,内存减少了 (541MB – 371MB) = 170MB 左右,证明基于 unset() 实现的方案是可行的。如图14
18、分析优化第 1 个命令行脚本,\console\controllers\CmcConsoleUserController.php,以减少执行时间为目标,从 30 分钟左右减少至 1 分钟左右。删除掉:addIMAccount() 相关的实现,其在遍历过程中,执行 HTTP 请求,消耗大量的时间。实际使用的内存量减少 0.09 MB 左右,系统分配总的内存量减少 0.002 MB 左右
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 | <?php /** * Created by PhpStorm. * User: Qiang Wang * Date: 2019/01/09 * Time: 13:55 */ namespace console\controllers; use Yii; use console\models\redis\cmc_console\User as RedisCmcConsoleUser; use console\services\CmcConsoleUserService; use yii\console\Controller; use yii\console\ExitCode; use yii\helpers\ArrayHelper; use yii\web\HttpException; use yii\web\ServerErrorHttpException; /** * 框架服务控制台的用户 * * @author Qiang Wang <shuijingwanwq@163.com> * @since 1.0 */ class CmcConsoleUserController extends Controller { /** * 同步框架服务控制台的用户模型(Http)至框架服务控制台的用户模型(Redis) * * @throws ServerErrorHttpException * @throws HttpException 如果登录超时 */ public function actionSync() { // 查询框架服务控制台的用户模型(Redis)的租户ID(以租户 ID 索引结果集) $redisCmcConsoleUserIndexByGroupIdItems = RedisCmcConsoleUser::find()->indexBy( 'group_id' )->all(); /* 判断 $redisCmcConsoleUserIndexByGroupIdItems 是否为空,如果为空,则成功退出 */ if ( empty ( $redisCmcConsoleUserIndexByGroupIdItems )) { // 延缓执行 60 * 60 秒 sleep(Yii:: $app ->params[ 'cmcConsoleUser' ][ 'isEmptyYesSleepTime' ]); return ExitCode::OK; } foreach ( $redisCmcConsoleUserIndexByGroupIdItems as $redisCmcConsoleUserIndexByGroupIdItemKey => $redisCmcConsoleUserIndexByGroupIdItemValue ) { $isLockExist = RedisCmcConsoleUser::isLockExist( $redisCmcConsoleUserIndexByGroupIdItemKey ); // 返回 true,表示锁定存在,即已经被其他客户端锁定 if ( $isLockExist === true) { continue ; } // HTTP请求,获取租户ID下的用户列表 $httpGetUserListData = [ 'groupId' => $redisCmcConsoleUserIndexByGroupIdItemKey , 'loginId' => '' , 'loginTid' => '' , ]; $getUserListData = CmcConsoleUserService::httpGetUserList( $httpGetUserListData ); // 框架服务控制台的用户模型(Http),重建数组索引(以 ID 索引结果集) $httpCmcConsoleUserItems = ArrayHelper::index( $getUserListData [ 'list' ], 'id' ); // 基于租户ID查询框架服务控制台的用户模型(Redis)(以 ID 索引结果集) $redisCmcConsoleUserItems = RedisCmcConsoleUser::find()->where([ 'group_id' => $redisCmcConsoleUserIndexByGroupIdItemKey ])->indexBy( 'id' )->all(); // 使用键名比较计算数组的差集,如果不为空,则删除出现在 Redis 中但是未出现在 Http 中的记录 $redisArrayDiffItems = array_diff_key ( $redisCmcConsoleUserItems , $httpCmcConsoleUserItems ); // 销毁变量 unset( $redisCmcConsoleUserItems ); if (! empty ( $redisArrayDiffItems )) { $redisArrayDiffIds = array_keys ( $redisArrayDiffItems ); // 销毁变量 unset( $redisArrayDiffItems ); if (RedisCmcConsoleUser::deleteAllByIds( $redisCmcConsoleUserIndexByGroupIdItemKey , $redisArrayDiffIds ) === false) { continue ; } } // 遍历框架服务控制台的用户模型(Http),判断在 Redis 中是否存在,如果不存在,则插入,如果存在且更新时间不相等,则更新 foreach ( $httpCmcConsoleUserItems as $httpCmcConsoleUserItemValue ) { $redisCmcConsoleUserItem = RedisCmcConsoleUser::find()->where([ 'id' => $httpCmcConsoleUserItemValue [ 'id' ]])->one(); $attributes = [ 'id' => $httpCmcConsoleUserItemValue [ 'id' ], 'group_id' => $getUserListData [ 'group_info' ][ 'group_id' ], 'login_name' => $httpCmcConsoleUserItemValue [ 'login_name' ], 'user_token' => $httpCmcConsoleUserItemValue [ 'user_token' ], 'user_nick' => $httpCmcConsoleUserItemValue [ 'user_nick' ], 'user_pic' => $httpCmcConsoleUserItemValue [ 'user_pic' ], 'user_mobile' => $httpCmcConsoleUserItemValue [ 'user_mobile' ] ? $httpCmcConsoleUserItemValue [ 'user_mobile' ] : '' , 'user_email' => $httpCmcConsoleUserItemValue [ 'user_email' ] ? $httpCmcConsoleUserItemValue [ 'user_email' ] : '' , 'user_sex' => $httpCmcConsoleUserItemValue [ 'user_sex' ], 'user_type' => $httpCmcConsoleUserItemValue [ 'user_type' ], 'user_birthday' => $httpCmcConsoleUserItemValue [ 'user_birthday' ], 'user_chat_id' => $httpCmcConsoleUserItemValue [ 'user_chat_id' ] ? $httpCmcConsoleUserItemValue [ 'user_chat_id' ] : '' , 'is_open' => $httpCmcConsoleUserItemValue [ 'is_open' ], 'add_time' => $httpCmcConsoleUserItemValue [ 'add_time' ], 'update_time' => $httpCmcConsoleUserItemValue [ 'update_time' ], 'im_identity' => md5( $getUserListData [ 'group_info' ][ 'group_id' ] . $httpCmcConsoleUserItemValue [ 'login_name' ]), ]; if (!isset( $redisCmcConsoleUserItem )) { $redisCmcConsoleUser = new RedisCmcConsoleUser(); $redisCmcConsoleUser ->attributes = $attributes ; $redisCmcConsoleUser ->insert(); } else { if (isset( $redisCmcConsoleUserItem ) && $httpCmcConsoleUserItemValue [ 'update_time' ] != $redisCmcConsoleUserItem [ 'update_time' ]) { $redisCmcConsoleUserItem ->attributes = $attributes ; $redisCmcConsoleUserItem ->save(); } } } } // 延缓执行 60 秒 sleep(Yii:: $app ->params[ 'cmcConsoleUser' ][ 'isEmptyNoSleepTime' ]); file_put_contents ( 'E:/wwwroot/pcs-api/console/runtime/logs/cmc-console-user-sync-memory-get-usage-' . time() . '.txt' , memory_get_usage() / 1024 / 1024 . 'MB' ); file_put_contents ( 'E:/wwwroot/pcs-api/console/runtime/logs/cmc-console-user-sync-memory-get-peak-usage-' . time() . '.txt' , memory_get_peak_usage() / 1024 / 1024 . 'MB' ); return ExitCode::OK; } } |
1 2 | 6.6167755126953MB //优化前 6.5267791748047MB //优化后 |
1 2 | 40.378349304199MB //优化前 40.37670135498MB //优化后 |
19、升级至开发环境,Docker 容器的 CPU:5.4%,内存:280MB,内存减少了 (371MB – 371MB) = 0MB 左右,证明减少命令行的执行时间,对于内存的使用优化无意义。虽然在本地开发环境中,实际使用的内存量减少 0.09 MB 左右,系统分配总的内存量减少 0.002 MB 左右,减少的内存量很小。如图15
20、分析优化第 1 个命令行脚本,\console\controllers\CmcConsoleUserController.php,以数组形式获取数据,在查询方法前调用 asArray() 方法,来获取 PHP 数组形式的结果。实际使用的内存量减少 0.09 MB 左右,系统分配总的内存量减少 8.96 MB 左右
1 2 3 4 5 | // 查询框架服务控制台的用户模型(Redis)的租户ID(以租户 ID 索引结果集) $redisCmcConsoleUserIndexByGroupIdItems = RedisCmcConsoleUser::find()->indexBy( 'group_id' )->asArray()->all(); // 基于租户ID查询框架服务控制台的用户模型(Redis)(以 ID 索引结果集) $redisCmcConsoleUserItems = RedisCmcConsoleUser::find()->where([ 'group_id' => $redisCmcConsoleUserIndexByGroupIdItemKey ])->indexBy( 'id' )->asArray()->all(); |
1 2 | 6.5267791748047MB //优化前 6.4357604980469MB //优化后 |
1 2 | 40.37670135498MB //优化前 31.42064666748MB //优化后 |
21、分析优化第 2 个命令行脚本,\console\controllers\ConfigColumnUserController.php,以数组形式获取数据,在查询方法前调用 asArray() 方法,来获取 PHP 数组形式的结果。实际使用的内存量减少 12.72 MB 左右,系统分配总的内存量减少 8.95 MB 左右
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 | <?php /** * Created by PhpStorm. * User: Qiang Wang * Date: 2019/03/01 * Time: 13:17 */ namespace console\controllers; use Yii; use console\models\redis\cmc_console\User as RedisCmcConsoleUser; use console\models\ConfigColumnUser; use yii\console\Controller; use yii\console\ExitCode; /** * 栏目人员配置 * * @author Qiang Wang <shuijingwanwq@163.com> * @since 1.0 */ class ConfigColumnUserController extends Controller { /** * 同步框架服务控制台的用户模型(Redis)至栏目人员配置模型(MySQL) * */ public function actionSync() { // 查询框架服务控制台的用户模型(Redis)的租户ID(以租户 ID 索引结果集) $redisCmcConsoleUserIndexByGroupIdItems = RedisCmcConsoleUser::find()->indexBy( 'group_id' )->asArray()->all(); /* 判断 $redisCmcConsoleUserIndexByGroupIdItems 是否为空,如果为空,则成功退出 */ if ( empty ( $redisCmcConsoleUserIndexByGroupIdItems )) { // 延缓执行 60 * 60 秒 sleep(Yii:: $app ->params[ 'configColumnUser' ][ 'isEmptyYesSleepTime' ]); return ExitCode::OK; } foreach ( $redisCmcConsoleUserIndexByGroupIdItems as $redisCmcConsoleUserIndexByGroupIdItemKey => $redisCmcConsoleUserIndexByGroupIdItemValue ) { $isLockExist = RedisCmcConsoleUser::isLockExist( $redisCmcConsoleUserIndexByGroupIdItemKey ); // 返回 true,表示锁定存在,即已经被其他客户端锁定 if ( $isLockExist === true) { continue ; } // 基于租户ID查询框架服务控制台的用户模型(Redis)(以 ID 索引结果集) $redisCmcConsoleUserItems = RedisCmcConsoleUser::find()->where([ 'group_id' => $redisCmcConsoleUserIndexByGroupIdItemKey ])->indexBy( 'id' )->asArray()->all(); // 基于租户ID查询栏目人员配置模型(MySQL)(以 用户ID 索引结果集) $configColumnUserItems = ConfigColumnUser::find()->where([ 'group_id' => $redisCmcConsoleUserIndexByGroupIdItemKey ])->isDeletedNo()->indexBy( 'user_id' )->asArray()->all(); // 使用键名比较计算数组的差集,如果不为空,则删除 (软删除) 出现在 栏目人员配置模型(MySQL) 中但是未出现在 框架服务控制台的用户模型(Redis) 中的记录 $diffItems = array_diff_key ( $configColumnUserItems , $redisCmcConsoleUserItems ); // 销毁变量 unset( $redisCmcConsoleUserItems , $configColumnUserItems ); if (! empty ( $diffItems )) { // 基于租户ID、用户ID查询栏目人员配置模型(MySQL)(待删除) $toBeDeletedConfigColumnUserItems = ConfigColumnUser::find()->where([ 'and' , [ 'group_id' => $redisCmcConsoleUserIndexByGroupIdItemKey ], [ 'in' , 'user_id' , $diffItems ], ])->isDeletedNo()->all(); foreach ( $toBeDeletedConfigColumnUserItems as $toBeDeletedConfigColumnUserItem ) { /* @var $toBeDeletedConfigColumnUserItem ConfigColumnUser */ $toBeDeletedConfigColumnUserItem ->softDelete(); } continue ; } } // 延缓执行 60 秒 sleep(Yii:: $app ->params[ 'configColumnUser' ][ 'isEmptyNoSleepTime' ]); // file_put_contents('E:/wwwroot/pcs-api/console/runtime/logs/config-column-user-sync-memory-get-usage-' . time() . '.txt', memory_get_usage() / 1024 / 1024 . 'MB'); // file_put_contents('E:/wwwroot/pcs-api/console/runtime/logs/config-column-user-sync-memory-get-peak-usage-' . time() . '.txt', memory_get_peak_usage() / 1024 / 1024 . 'MB'); return ExitCode::OK; } } |
1 2 | 17.696464538574MB //优化前 4.9719772338867MB //优化后 |
1 2 | 40.366722106934MB //优化前 31.416854858398MB //优化后 |
22、分析优化第 1 个命令行脚本,\console\controllers\CmcConsoleUserController.php,变量 $attributes 在每一次遍历中皆有定义,实际上在大部分遍历中皆未使用,因此,仅在有使用的情况下才定义。实际使用的内存量减少 0.0007 MB 左右,系统分配总的内存量无变化
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 | // 遍历框架服务控制台的用户模型(Http),判断在 Redis 中是否存在,如果不存在,则插入,如果存在且更新时间不相等,则更新 foreach ( $httpCmcConsoleUserItems as $httpCmcConsoleUserItemValue ) { $redisCmcConsoleUserItem = RedisCmcConsoleUser::find()->where([ 'id' => $httpCmcConsoleUserItemValue [ 'id' ]])->one(); if (!isset( $redisCmcConsoleUserItem )) { $redisCmcConsoleUser = new RedisCmcConsoleUser(); $attributes = $this ->getAttributes( $getUserListData [ 'group_info' ][ 'group_id' ], $httpCmcConsoleUserItemValue ); $redisCmcConsoleUser ->attributes = $attributes ; $redisCmcConsoleUser ->insert(); } else { if ( $httpCmcConsoleUserItemValue [ 'update_time' ] != $redisCmcConsoleUserItem [ 'update_time' ]) { $attributes = $this ->getAttributes( $getUserListData [ 'group_info' ][ 'group_id' ], $httpCmcConsoleUserItemValue ); $redisCmcConsoleUserItem ->attributes = $attributes ; $redisCmcConsoleUserItem ->save(); } } } /** * 获取属性列表 * @param string $groupId 租户ID * @param array $httpCmcConsoleUser 框架服务控制台的用户模型(Http) * * @return array */ private function getAttributes( $groupId , $httpCmcConsoleUser ) { return [ 'id' => $httpCmcConsoleUser [ 'id' ], 'group_id' => $groupId , 'login_name' => $httpCmcConsoleUser [ 'login_name' ], 'user_token' => $httpCmcConsoleUser [ 'user_token' ], 'user_nick' => $httpCmcConsoleUser [ 'user_nick' ], 'user_pic' => $httpCmcConsoleUser [ 'user_pic' ], 'user_mobile' => $httpCmcConsoleUser [ 'user_mobile' ] ? $httpCmcConsoleUser [ 'user_mobile' ] : '' , 'user_email' => $httpCmcConsoleUser [ 'user_email' ] ? $httpCmcConsoleUser [ 'user_email' ] : '' , 'user_sex' => $httpCmcConsoleUser [ 'user_sex' ], 'user_type' => $httpCmcConsoleUser [ 'user_type' ], 'user_birthday' => $httpCmcConsoleUser [ 'user_birthday' ], 'user_chat_id' => $httpCmcConsoleUser [ 'user_chat_id' ] ? $httpCmcConsoleUser [ 'user_chat_id' ] : '' , 'is_open' => $httpCmcConsoleUser [ 'is_open' ], 'add_time' => $httpCmcConsoleUser [ 'add_time' ], 'update_time' => $httpCmcConsoleUser [ 'update_time' ], 'im_identity' => md5( $groupId . $httpCmcConsoleUser [ 'login_name' ]), ]; } |
1 2 | 6.4357604980469MB //优化前 6.4350357055664MB //优化后 |
1 2 | 31.42064666748MB //优化前 31.42064666748MB //优化后 |
23、升级至开发环境,Docker 容器的 CPU:0.04%,内存:283MB,内存减少了 (371MB – 283MB) = 90MB 左右,证明以数组形式获取数据,在查询方法前调用 asArray() 方法,来获取 PHP 数组形式的结果。优化的方案是可行的。如图16
24、分析优化第 1 个命令行脚本,\console\controllers\CmcConsoleUserController.php,基于 unset() 实现,实际使用的内存量减少 0.067 MB 左右,系统分配总的内存量无变化
1 2 3 4 5 | // 框架服务控制台的用户模型(Http),重建数组索引(以 ID 索引结果集) $httpCmcConsoleUserItems = ArrayHelper::index( $getUserListData [ 'list' ], 'id' ); // 销毁变量 unset( $getUserListData [ 'list' ]); |
1 2 | 6.4350357055664MB //优化前 6.3685455322266MB //优化后 |
1 2 | 31.42064666748MB //优化前 31.42064666748MB //优化后 |
25、分析优化第 1 个命令行脚本,\console\controllers\CmcConsoleUserController.php,得出结论,内存占用过大的根本原因在于 RedisCmcConsoleUser::find()。其在 8000 左右的 Redis AR 模型记录中查找出 8 条记录。
1 2 3 4 5 | // 查询框架服务控制台的用户模型(Redis)的租户ID(以租户 ID 索引结果集) $redisCmcConsoleUserIndexByGroupIdItems = RedisCmcConsoleUser::find()->indexBy( 'group_id' )->asArray()->all(); file_put_contents ( 'E:/wwwroot/pcs-api/console/runtime/logs/cmc-console-user-sync-memory-get-usage-' . time() . '.txt' , memory_get_usage() / 1024 / 1024 . 'MB' ); file_put_contents ( 'E:/wwwroot/pcs-api/console/runtime/logs/cmc-console-user-sync-memory-get-peak-usage-' . time() . '.txt' , memory_get_peak_usage() / 1024 / 1024 . 'MB' ); |
1 2 | 3.6807098388672MB //RedisCmcConsoleUser::find() 执行后 6.3685455322266MB //命令行执行后 |
1 2 | 31.42064666748MB //RedisCmcConsoleUser::find() 执行后 31.42064666748MB //命令行执行后 |
26、分析优化第 1 个命令行脚本,\console\controllers\CmcConsoleUserController.php,删除 RedisCmcConsoleUser::find(),以另外一种形式来获取租户ID列表。
1 2 3 4 5 6 7 | // HTTP 请求,获取开通有效服务的租户ID列表 $cmcApiGroupIds = CmcApiGroupService::httpGetGroupIds(); file_put_contents ( 'E:/wwwroot/pcs-api/console/runtime/logs/cmc-console-user-sync-memory-get-usage-' . time() . '.txt' , memory_get_usage() / 1024 / 1024 . 'MB' ); file_put_contents ( 'E:/wwwroot/pcs-api/console/runtime/logs/cmc-console-user-sync-memory-get-peak-usage-' . time() . '.txt' , memory_get_peak_usage() / 1024 / 1024 . 'MB' ); exit ; |
1 2 | 3.6807098388672MB //RedisCmcConsoleUser::find() 执行后 3.1283721923828MB //CmcApiGroupService::httpGetGroupIds() 执行后 |
1 2 | 31.42064666748MB //RedisCmcConsoleUser::find() 执行后 3.1799774169922MB //CmcApiGroupService::httpGetGroupIds() 执行后 |
27、分析优化第 1 个命令行脚本,\console\controllers\CmcConsoleUserController.php,删除 RedisCmcConsoleUser::find(),以另外一种形式来获取租户ID列表,实际使用的内存量减少 1.72 MB 左右,系统分配总的内存量减少 21.17 MB 左右
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 | /** * 同步框架服务控制台的用户模型(Http)至框架服务控制台的用户模型(Redis) * * @throws ServerErrorHttpException * @throws HttpException 如果登录超时 */ public function actionSync() { // HTTP 请求,获取开通有效服务的租户ID列表 $cmcApiGroupIds = CmcApiGroupService::httpGetGroupIds(); /* 判断 $cmcApiGroupIds 是否为空,如果为空,则成功退出 */ if ( empty ( $cmcApiGroupIds )) { // 延缓执行 60 * 60 秒 sleep(Yii:: $app ->params[ 'cmcConsoleUser' ][ 'isEmptyYesSleepTime' ]); return ExitCode::OK; } foreach ( $cmcApiGroupIds as $cmcApiGroupId ) { $isLockExist = RedisCmcConsoleUser::isLockExist( $cmcApiGroupId ); // 返回 true,表示锁定存在,即已经被其他客户端锁定 if ( $isLockExist === true) { continue ; } // HTTP请求,获取租户ID下的用户列表 $httpGetUserListData = [ 'groupId' => $cmcApiGroupId , 'loginId' => '' , 'loginTid' => '' , ]; $getUserListData = CmcConsoleUserService::httpGetUserList( $httpGetUserListData ); // 框架服务控制台的用户模型(Http),重建数组索引(以 ID 索引结果集) $httpCmcConsoleUserItems = ArrayHelper::index( $getUserListData [ 'list' ], 'id' ); // 销毁变量 unset( $getUserListData [ 'list' ]); // 基于租户ID查询框架服务控制台的用户模型(Redis)(以 ID 索引结果集) $redisCmcConsoleUserItems = RedisCmcConsoleUser::find()->where([ 'group_id' => $cmcApiGroupId ])->indexBy( 'id' )->asArray()->all(); // 使用键名比较计算数组的差集,如果不为空,则删除出现在 Redis 中但是未出现在 Http 中的记录 $redisArrayDiffItems = array_diff_key ( $redisCmcConsoleUserItems , $httpCmcConsoleUserItems ); // 销毁变量 unset( $redisCmcConsoleUserItems ); if (! empty ( $redisArrayDiffItems )) { $redisArrayDiffIds = array_keys ( $redisArrayDiffItems ); // 销毁变量 unset( $redisArrayDiffItems ); if (RedisCmcConsoleUser::deleteAllByIds( $cmcApiGroupId , $redisArrayDiffIds ) === false) { continue ; } } // 遍历框架服务控制台的用户模型(Http),判断在 Redis 中是否存在,如果不存在,则插入,如果存在且更新时间不相等,则更新 foreach ( $httpCmcConsoleUserItems as $httpCmcConsoleUserItemValue ) { $redisCmcConsoleUserItem = RedisCmcConsoleUser::find()->where([ 'id' => $httpCmcConsoleUserItemValue [ 'id' ]])->one(); if (!isset( $redisCmcConsoleUserItem )) { $redisCmcConsoleUser = new RedisCmcConsoleUser(); $attributes = $this ->getAttributes( $getUserListData [ 'group_info' ][ 'group_id' ], $httpCmcConsoleUserItemValue ); $redisCmcConsoleUser ->attributes = $attributes ; $redisCmcConsoleUser ->insert(); } else { if ( $httpCmcConsoleUserItemValue [ 'update_time' ] != $redisCmcConsoleUserItem [ 'update_time' ]) { $attributes = $this ->getAttributes( $getUserListData [ 'group_info' ][ 'group_id' ], $httpCmcConsoleUserItemValue ); $redisCmcConsoleUserItem ->attributes = $attributes ; $redisCmcConsoleUserItem ->save(); } } } } // 延缓执行 60 秒 sleep(Yii:: $app ->params[ 'cmcConsoleUser' ][ 'isEmptyNoSleepTime' ]); file_put_contents ( 'E:/wwwroot/pcs-api/console/runtime/logs/cmc-console-user-sync-memory-get-usage-' . time() . '.txt' , memory_get_usage() / 1024 / 1024 . 'MB' ); file_put_contents ( 'E:/wwwroot/pcs-api/console/runtime/logs/cmc-console-user-sync-memory-get-peak-usage-' . time() . '.txt' , memory_get_peak_usage() / 1024 / 1024 . 'MB' ); return ExitCode::OK; } |
1 2 | 6.3685455322266MB //优化前 4.6499099731445MB //优化后 |
1 2 | 31.42064666748MB //优化前 10.250221252441MB //优化后 |
28、分析优化第 2 个命令行脚本,\console\controllers\ConfigColumnUserController.php,删除 RedisCmcConsoleUser::find(),以另外一种形式来获取租户ID列表,实际使用的内存量增加 0.37 MB 左右,系统分配总的内存量减少 19.39 MB 左右
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 | /** * 同步框架服务控制台的用户模型(Redis)至栏目人员配置模型(MySQL) * */ public function actionSync() { // HTTP 请求,获取开通有效服务的租户ID列表 $cmcApiGroupIds = CmcApiGroupService::httpGetGroupIds(); /* 判断 $cmcApiGroupIds 是否为空,如果为空,则成功退出 */ if ( empty ( $cmcApiGroupIds )) { // 延缓执行 60 * 60 秒 sleep(Yii:: $app ->params[ 'configColumnUser' ][ 'isEmptyYesSleepTime' ]); return ExitCode::OK; } foreach ( $cmcApiGroupIds as $cmcApiGroupId ) { $isLockExist = RedisCmcConsoleUser::isLockExist( $cmcApiGroupId ); // 返回 true,表示锁定存在,即已经被其他客户端锁定 if ( $isLockExist === true) { continue ; } // 基于租户ID查询框架服务控制台的用户模型(Redis)(以 ID 索引结果集) $redisCmcConsoleUserItems = RedisCmcConsoleUser::find()->where([ 'group_id' => $cmcApiGroupId ])->indexBy( 'id' )->asArray()->all(); // 基于租户ID查询栏目人员配置模型(MySQL)(以 用户ID 索引结果集) $configColumnUserItems = ConfigColumnUser::find()->where([ 'group_id' => $cmcApiGroupId ])->isDeletedNo()->indexBy( 'user_id' )->asArray()->all(); // 使用键名比较计算数组的差集,如果不为空,则删除 (软删除) 出现在 栏目人员配置模型(MySQL) 中但是未出现在 框架服务控制台的用户模型(Redis) 中的记录 $diffItems = array_diff_key ( $configColumnUserItems , $redisCmcConsoleUserItems ); // 销毁变量 unset( $redisCmcConsoleUserItems , $configColumnUserItems ); if (! empty ( $diffItems )) { // 基于租户ID、用户ID查询栏目人员配置模型(MySQL)(待删除) $toBeDeletedConfigColumnUserItems = ConfigColumnUser::find()->where([ 'and' , [ 'group_id' => $cmcApiGroupId ], [ 'in' , 'user_id' , $diffItems ], ])->isDeletedNo()->all(); foreach ( $toBeDeletedConfigColumnUserItems as $toBeDeletedConfigColumnUserItem ) { /* @var $toBeDeletedConfigColumnUserItem ConfigColumnUser */ $toBeDeletedConfigColumnUserItem ->softDelete(); } continue ; } } // 延缓执行 60 秒 sleep(Yii:: $app ->params[ 'configColumnUser' ][ 'isEmptyNoSleepTime' ]); file_put_contents ( 'E:/wwwroot/pcs-api/console/runtime/logs/config-column-user-sync-memory-get-usage-' . time() . '.txt' , memory_get_usage() / 1024 / 1024 . 'MB' ); file_put_contents ( 'E:/wwwroot/pcs-api/console/runtime/logs/config-column-user-sync-memory-get-peak-usage-' . time() . '.txt' , memory_get_peak_usage() / 1024 / 1024 . 'MB' ); return ExitCode::OK; } |
1 2 | 4.9719772338867MB //优化前 5.3451461791992MB //优化后 |
1 2 | 31.416854858398MB //优化前 12.024124145508MB //优化后 |
29、升级至开发环境,Docker 容器的 CPU:92%,内存:435MB,CPU 增加了 (92% – 0.04%) = 91.96% 左右,内存增加了 (435MB – 283MB) = 152MB 左右。如图17、图18
30、分析具体原因,基于 Yii 2 的 HTTP 客户端扩展在 Linux 中必须添加:setData([]),否则响应 400,如图19、图20
31、解决了 响应 400 的 Bug 之后,升级至开发环境,Docker 容器的 CPU:0.04%,内存:341MB,CPU无变化,内存增加了 (341MB – 283MB) = 60MB 左右。证明实际使用的内存量增加,系统分配总的内存量减少,Docker 的内存占用是在增加的(主要受到实际使用的内存量的影响)。如图21、图22
32、延缓执行 60 秒,决定不再使用 sleep(),因为,在 sleep() 期间,内存一直处于占用中的,实际上延长了内存占用的时间。虽然可能降低了 CPU 的占用百分比。
1 2 | 4.6499099731445MB //延缓执行 60 秒前 4.6499099731445MB //延缓执行 60 秒后 |
1 2 | 10.250221252441MB //延缓执行 60 秒前 10.250221252441MB //延缓执行 60 秒后 |
33、分析优化第 1 个命令行脚本,\console\controllers\CmcConsoleUserController.php,设置同步标识的缓存,如果不存在,则同步,将数据在缓存中保留 60 秒。如果存在,则不同步(注:在同步成功后的 60 秒内,内存占用会很小,见第 2 个优化后的内存占用情况)。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 | <?php /** * Created by PhpStorm. * User: Qiang Wang * Date: 2019/01/09 * Time: 13:55 */ namespace console\controllers; use Yii; use console\models\redis\cmc_console\User as RedisCmcConsoleUser; use console\services\CmcApiGroupService; use console\services\CmcConsoleUserService; use yii\console\Controller; use yii\console\ExitCode; use yii\helpers\ArrayHelper; use yii\web\HttpException; use yii\web\ServerErrorHttpException; /** * 框架服务控制台的用户 * * @author Qiang Wang <shuijingwanwq@163.com> * @since 1.0 */ class CmcConsoleUserController extends Controller { /** * 同步框架服务控制台的用户模型(Http)至框架服务控制台的用户模型(Redis) * * @throws ServerErrorHttpException * @throws HttpException 如果登录超时 */ public function actionSync() { // 设置同步标识、租户ID列表的缓存键 $redisCache = Yii:: $app ->redisCache; $redisCacheIdentityKey = 'cmc_console_user_sync' ; $redisCacheGroupIdsKey = 'cmc_api_group_ids' ; // 从缓存中取回同步标识、租户ID列表 $redisCacheIdentityData = $redisCache [ $redisCacheIdentityKey ]; $redisCacheGroupIdsData = $redisCache [ $redisCacheGroupIdsKey ]; if ( $redisCacheIdentityData === false) { if ( $redisCacheGroupIdsData === false) { // HTTP 请求,获取开通有效服务的租户ID列表 $cmcApiGroupIds = CmcApiGroupService::httpGetGroupIds(); // 将租户ID列表存放到缓存供下次使用,将数据在缓存中保留 60 * 60 秒 $redisCache ->set( $redisCacheGroupIdsKey , $cmcApiGroupIds , Yii:: $app ->params[ 'cmcConsoleUser' ][ 'isEmptyYesSleepTime' ]); } else { $cmcApiGroupIds = $redisCacheGroupIdsData ; } /* 判断 $cmcApiGroupIds 是否为空,如果为空,则成功退出 */ if ( empty ( $cmcApiGroupIds )) { // 延缓执行 60 * 60 秒 sleep(Yii:: $app ->params[ 'cmcConsoleUser' ][ 'isEmptyYesSleepTime' ]); return ExitCode::OK; } foreach ( $cmcApiGroupIds as $cmcApiGroupId ) { $isLockExist = RedisCmcConsoleUser::isLockExist( $cmcApiGroupId ); // 返回 true,表示锁定存在,即已经被其他客户端锁定 if ( $isLockExist === true) { continue ; } // HTTP请求,获取租户ID下的用户列表 $httpGetUserListData = [ 'groupId' => $cmcApiGroupId , 'loginId' => '' , 'loginTid' => '' , ]; $getUserListData = CmcConsoleUserService::httpGetUserList( $httpGetUserListData ); // 框架服务控制台的用户模型(Http),重建数组索引(以 ID 索引结果集) $httpCmcConsoleUserItems = ArrayHelper::index( $getUserListData [ 'list' ], 'id' ); // 销毁变量 unset( $getUserListData [ 'list' ]); // 基于租户ID查询框架服务控制台的用户模型(Redis)(以 ID 索引结果集) $redisCmcConsoleUserItems = RedisCmcConsoleUser::find()->where([ 'group_id' => $cmcApiGroupId ])->indexBy( 'id' )->asArray()->all(); // 使用键名比较计算数组的差集,如果不为空,则删除出现在 Redis 中但是未出现在 Http 中的记录 $redisArrayDiffItems = array_diff_key ( $redisCmcConsoleUserItems , $httpCmcConsoleUserItems ); // 销毁变量 unset( $redisCmcConsoleUserItems ); if (! empty ( $redisArrayDiffItems )) { $redisArrayDiffIds = array_keys ( $redisArrayDiffItems ); // 销毁变量 unset( $redisArrayDiffItems ); if (RedisCmcConsoleUser::deleteAllByIds( $cmcApiGroupId , $redisArrayDiffIds ) === false) { continue ; } } // 遍历框架服务控制台的用户模型(Http),判断在 Redis 中是否存在,如果不存在,则插入,如果存在且更新时间不相等,则更新 foreach ( $httpCmcConsoleUserItems as $httpCmcConsoleUserItemValue ) { $redisCmcConsoleUserItem = RedisCmcConsoleUser::find()->where([ 'id' => $httpCmcConsoleUserItemValue [ 'id' ]])->one(); if (!isset( $redisCmcConsoleUserItem )) { $redisCmcConsoleUser = new RedisCmcConsoleUser(); $attributes = $this ->getAttributes( $getUserListData [ 'group_info' ][ 'group_id' ], $httpCmcConsoleUserItemValue ); $redisCmcConsoleUser ->attributes = $attributes ; $redisCmcConsoleUser ->insert(); } else { if ( $httpCmcConsoleUserItemValue [ 'update_time' ] != $redisCmcConsoleUserItem [ 'update_time' ]) { $attributes = $this ->getAttributes( $getUserListData [ 'group_info' ][ 'group_id' ], $httpCmcConsoleUserItemValue ); $redisCmcConsoleUserItem ->attributes = $attributes ; $redisCmcConsoleUserItem ->save(); } } } } // 延缓执行 60 秒 // sleep(Yii::$app->params['cmcConsoleUser']['isEmptyNoSleepTime']); // 将同步标识存放到缓存供下次使用,将数据在缓存中保留 60 秒 $redisCache ->set( $redisCacheIdentityKey , $redisCacheIdentityKey , Yii:: $app ->params[ 'cmcConsoleUser' ][ 'isEmptyNoSleepTime' ]); // file_put_contents('E:/wwwroot/pcs-api/console/runtime/logs/cmc-console-user-sync-memory-get-usage-' . time() . '.txt', memory_get_usage() / 1024 / 1024 . 'MB'); // file_put_contents('E:/wwwroot/pcs-api/console/runtime/logs/cmc-console-user-sync-memory-get-peak-usage-' . time() . '.txt', memory_get_peak_usage() / 1024 / 1024 . 'MB'); return ExitCode::OK; } else { return ExitCode::OK; } } /** * 获取属性列表 * @param string $groupId 租户ID * @param array $httpCmcConsoleUser 框架服务控制台的用户模型(Http) * * @return array */ private function getAttributes( $groupId , $httpCmcConsoleUser ) { return [ 'id' => $httpCmcConsoleUser [ 'id' ], 'group_id' => $groupId , 'login_name' => $httpCmcConsoleUser [ 'login_name' ], 'user_token' => $httpCmcConsoleUser [ 'user_token' ], 'user_nick' => $httpCmcConsoleUser [ 'user_nick' ], 'user_pic' => $httpCmcConsoleUser [ 'user_pic' ], 'user_mobile' => $httpCmcConsoleUser [ 'user_mobile' ] ? $httpCmcConsoleUser [ 'user_mobile' ] : '' , 'user_email' => $httpCmcConsoleUser [ 'user_email' ] ? $httpCmcConsoleUser [ 'user_email' ] : '' , 'user_sex' => $httpCmcConsoleUser [ 'user_sex' ], 'user_type' => $httpCmcConsoleUser [ 'user_type' ], 'user_birthday' => $httpCmcConsoleUser [ 'user_birthday' ], 'user_chat_id' => $httpCmcConsoleUser [ 'user_chat_id' ] ? $httpCmcConsoleUser [ 'user_chat_id' ] : '' , 'is_open' => $httpCmcConsoleUser [ 'is_open' ], 'add_time' => $httpCmcConsoleUser [ 'add_time' ], 'update_time' => $httpCmcConsoleUser [ 'update_time' ], ]; } } |
1 2 3 4 | 4.6499099731445MB //优化前 4.6722030639648MB //优化后(有 HTTP 请求,有同步) 2.8900680541992MB //优化后(无 HTTP 请求,无同步) 4.6064605712891MB //优化后(无 HTTP 请求,有同步) |
1 2 3 4 | 10.250221252441MB //优化前 10.41194152832MB //优化后(有 HTTP 请求,有同步) 2.9416198730469MB //优化后(无 HTTP 请求,无同步) 10.346199035645MB //优化后(无 HTTP 请求,有同步) |
34、分析优化第 2 个命令行脚本,\console\controllers\ConfigColumnUserController.php,设置同步标识的缓存,如果不存在,则同步,将数据在缓存中保留 60 秒。如果存在,则不同步(注:在同步成功后的 60 秒内,内存占用会很小,见第 2 个优化后的内存占用情况)。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 | <?php /** * Created by PhpStorm. * User: Qiang Wang * Date: 2019/03/01 * Time: 13:17 */ namespace console\controllers; use Yii; use console\models\redis\cmc_console\User as RedisCmcConsoleUser; use console\models\ConfigColumnUser; use console\services\CmcApiGroupService; use console\services\CmcConsoleUserService; use yii\console\Controller; use yii\console\ExitCode; use yii\helpers\ArrayHelper; use yii\web\HttpException; use yii\web\ServerErrorHttpException; /** * 栏目人员配置 * * @author Qiang Wang <shuijingwanwq@163.com> * @since 1.0 */ class ConfigColumnUserController extends Controller { /** * 同步框架服务控制台的用户模型(Http)至栏目人员配置模型(MySQL) * * @throws ServerErrorHttpException * @throws HttpException 如果登录超时 */ public function actionSync() { // 设置同步标识、租户ID列表的缓存键 $redisCache = Yii:: $app ->redisCache; $redisCacheIdentityKey = 'config_column_user_sync' ; $redisCacheGroupIdsKey = 'cmc_api_group_ids' ; // 从缓存中取回同步标识、租户ID列表 $redisCacheIdentityData = $redisCache [ $redisCacheIdentityKey ]; $redisCacheGroupIdsData = $redisCache [ $redisCacheGroupIdsKey ]; if ( $redisCacheIdentityData === false) { if ( $redisCacheGroupIdsData === false) { // HTTP 请求,获取开通有效服务的租户ID列表 // $cmcApiGroupIds = CmcApiGroupService::httpGetGroupIds(); // 将租户ID列表存放到缓存供下次使用,将数据在缓存中保留 60 * 60 秒 // $redisCache->set($redisCacheGroupIdsKey, $cmcApiGroupIds, Yii::$app->params['cmcConsoleUser']['isEmptyYesSleepTime']); return ExitCode::OK; } else { $cmcApiGroupIds = $redisCacheGroupIdsData ; } /* 判断 $cmcApiGroupIds 是否为空,如果为空,则成功退出 */ if ( empty ( $cmcApiGroupIds )) { // 延缓执行 60 * 60 秒 sleep(Yii:: $app ->params[ 'configColumnUser' ][ 'isEmptyYesSleepTime' ]); return ExitCode::OK; } foreach ( $cmcApiGroupIds as $cmcApiGroupId ) { // HTTP请求,获取租户ID下的用户列表 $httpGetUserListData = [ 'groupId' => $cmcApiGroupId , 'loginId' => '' , 'loginTid' => '' , ]; $getUserListData = CmcConsoleUserService::httpGetUserList( $httpGetUserListData ); // 框架服务控制台的用户模型(Http),重建数组索引(以 ID 索引结果集) $httpCmcConsoleUserItems = ArrayHelper::index( $getUserListData [ 'list' ], 'id' ); // 销毁变量 unset( $getUserListData ); // 基于租户ID查询栏目人员配置模型(MySQL)(以 用户ID 索引结果集) $configColumnUserItems = ConfigColumnUser::find()->where([ 'group_id' => $cmcApiGroupId ])->isDeletedNo()->indexBy( 'user_id' )->asArray()->all(); // 使用键名比较计算数组的差集,如果不为空,则删除 (软删除) 出现在 栏目人员配置模型(MySQL) 中但是未出现在 框架服务控制台的用户模型(Http) 中的记录 $diffItems = array_diff_key ( $configColumnUserItems , $httpCmcConsoleUserItems ); // 销毁变量 unset( $httpCmcConsoleUserItems , $configColumnUserItems ); if (! empty ( $diffItems )) { // 基于租户ID、用户ID查询栏目人员配置模型(MySQL)(待删除) $toBeDeletedConfigColumnUserItems = ConfigColumnUser::find()->where([ 'and' , [ 'group_id' => $cmcApiGroupId ], [ 'in' , 'user_id' , $diffItems ], ])->isDeletedNo()->all(); foreach ( $toBeDeletedConfigColumnUserItems as $toBeDeletedConfigColumnUserItem ) { /* @var $toBeDeletedConfigColumnUserItem ConfigColumnUser */ $toBeDeletedConfigColumnUserItem ->softDelete(); } continue ; } } // 延缓执行 60 秒 // sleep(Yii::$app->params['configColumnUser']['isEmptyNoSleepTime']); // 将同步标识存放到缓存供下次使用,将数据在缓存中保留 60 秒 $redisCache ->set( $redisCacheIdentityKey , $redisCacheIdentityKey , Yii:: $app ->params[ 'cmcConsoleUser' ][ 'isEmptyNoSleepTime' ]); // file_put_contents('E:/wwwroot/pcs-api/console/runtime/logs/config-column-user-sync-memory-get-usage-' . time() . '.txt', memory_get_usage() / 1024 / 1024 . 'MB'); // file_put_contents('E:/wwwroot/pcs-api/console/runtime/logs/config-column-user-sync-memory-get-peak-usage-' . time() . '.txt', memory_get_peak_usage() / 1024 / 1024 . 'MB'); return ExitCode::OK; } else { return ExitCode::OK; } } } |
1 2 3 4 | 5.3451461791992MB //优化前 5.5944900512695MB //优化后(有 HTTP 请求,有同步) 2.8861389160156MB //优化后(无 HTTP 请求,无同步) 5.3590927124023MB //优化后(无 HTTP 请求,有同步) |
1 2 3 4 | 12.024124145508MB //优化前 11.753318786621MB //优化后(有 HTTP 请求,有同步) 2.9376907348633MB //优化后(无 HTTP 请求,无同步) 11.67618560791MB //优化后(无 HTTP 请求,有同步) |
35、升级至开发环境,Docker 容器的 CPU:16%,内存:486MB,CPU 增加了 (16% – 0.04%) = 15.96% 左右,内存增加了 (486MB – 341MB) = 140MB 左右,证明同步成功后的 60 秒内,避免再次同步。优化的方案是不可行的(与预期不相符)。如图23、图24
36、仔细对比与第 31 步骤上的差异,在于命令行每次执行快结束时,使用了 sleep(),决定在同步成功后的 60 秒内,使用 sleep(),在同步成功后的 60 秒内,仅会再执行一次命令行(且执行时间长度大于:60 秒),而不会执行多次。查看 supervisord 运行状态,发现命令行执行的时间很短、频率很高,尤其是第 2 个命令行。如图25
1 2 3 4 5 6 | if ( $redisCacheIdentityData === false) { } else { // 延缓执行 60 秒 sleep(Yii:: $app ->params[ 'cmcConsoleUser' ][ 'isEmptyNoSleepTime' ]); return ExitCode::OK; } |
1 2 3 4 5 6 | if ( $redisCacheIdentityData === false) { } else { // 延缓执行 60 秒 sleep(Yii:: $app ->params[ 'configColumnUser' ][ 'isEmptyNoSleepTime' ]); return ExitCode::OK; } |
37、升级至开发环境,Docker 容器的 CPU:0.04%,内存:283MB,CPU 无变化,内存减少了 (341MB – 283MB) = 60MB 左右。证明同步成功后的 60 秒内,避免再次同步。优化的方案是可行的。如图26、图27
38、决定再次降低同步的执行频率,同步成功后的 5 * 60 秒内,避免再次同步。查看 supervisord 运行状态,发现命令行执行的时间很长、频率很低。升级至开发环境,Docker 容器的监控数据无变化。证明当执行频率达到某个临界值之后,再次降低,优化的意义不大了的,顶多 CPU 的平均值更低一些(理论上)。如图28
39、避免在生产环境中,当租户数量过多时,一次同步所有租户下的用户,进而导致内存占用过大、运行时间过长,决定一次仅同步一个租户下的用户。
40、分析优化第 1 个命令行脚本,\console\controllers\CmcConsoleUserController.php,遍历租户ID列表时,break 结束当前 foreach 结构的执行,即命令行的每一次运行,仅同步成功一个租户下的用户列表。在 有 HTTP 请求,有同步 的情况下,实际使用的内存量增加 1.54 MB 左右,系统分配总的内存量减少 0.75 MB 左右
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 | <?php /** * Created by PhpStorm. * User: Qiang Wang * Date: 2019/01/09 * Time: 13:55 */ namespace console\controllers; use Yii; use console\models\redis\cmc_console\User as RedisCmcConsoleUser; use console\services\CmcApiGroupService; use console\services\CmcConsoleUserService; use yii\base\InvalidArgumentException; use yii\console\Controller; use yii\console\ExitCode; use yii\helpers\ArrayHelper; use yii\web\HttpException; use yii\web\ServerErrorHttpException; /** * 框架服务控制台的用户 * * @author Qiang Wang <shuijingwanwq@163.com> * @since 1.0 */ class CmcConsoleUserController extends Controller { /** * 同步框架服务控制台的用户模型(Http)至框架服务控制台的用户模型(Redis) * * @throws ServerErrorHttpException * @throws HttpException 如果登录超时 * @throws InvalidArgumentException if the $direction or $sortFlag parameters do not have */ public function actionSync() { // 设置同步标识的缓存键 $redisCache = Yii:: $app ->redisCache; $redisCacheIdentityKey = 'cmc_console_user_sync' ; // 从缓存中取回同步标识 $redisCacheIdentityData = $redisCache [ $redisCacheIdentityKey ]; if ( $redisCacheIdentityData === false) { // HTTP 请求,获取开通有效服务的租户ID列表 $httpCmcApiGroupIds = CmcApiGroupService::httpGetGroupIds(); /* 判断 $httpCmcApiGroupIds 是否为空,如果为空,则成功退出 */ if ( empty ( $httpCmcApiGroupIds )) { // 延缓执行 60 * 60 秒 sleep(Yii:: $app ->params[ 'cmcConsoleUser' ][ 'isEmptyYesSleepTime' ]); return ExitCode::OK; } // 设置租户ID列表的缓存键 $redisCacheGroupIdsKey = 'cmc_api_group_ids' ; // 从缓存中取回租户ID列表 $redisCacheGroupIdsData = $redisCache [ $redisCacheGroupIdsKey ]; // 是否设置租户ID列表的缓存,默认:否 $isSetRedisCacheGroupIds = false; if ( $redisCacheGroupIdsData === false) { $cmcApiGroupIds = []; foreach ( $httpCmcApiGroupIds as $httpCmcApiGroupId ) { $cmcApiGroupIds [] = [ 'group_id' => $httpCmcApiGroupId , 'cmc_console_user_last_synced_at' => 0, //上次同步时间 'config_column_user_last_synced_at' => 0, //上次同步时间 ]; } // 是否设置租户ID列表的缓存:是 $isSetRedisCacheGroupIds = true; } else { // 获取 group_id 值列表 $redisCacheGroupIds = ArrayHelper::getColumn( $redisCacheGroupIdsData , 'group_id' ); $cmcApiGroupIds = $redisCacheGroupIdsData ; foreach ( $httpCmcApiGroupIds as $httpCmcApiGroupId ) { if (!in_array( $httpCmcApiGroupId , $redisCacheGroupIds )) { $cmcApiGroupIds [] = [ 'group_id' => $httpCmcApiGroupId , 'cmc_console_user_last_synced_at' => 0, //上次同步时间 'config_column_user_last_synced_at' => 0, //上次同步时间 ]; // 是否设置租户ID列表的缓存:是 $isSetRedisCacheGroupIds = true; } } } // 判断是否设置租户ID列表的缓存 if ( $isSetRedisCacheGroupIds ) { // 将 $cmcApiGroupIds 存放到缓存供下次使用,将数据在缓存中永久保留 $redisCache ->set( $redisCacheGroupIdsKey , $cmcApiGroupIds ); } // 基于上次同步时间顺序排列,赋值给:$sortCmcApiGroupIds $sortCmcApiGroupIds = $cmcApiGroupIds ; ArrayHelper::multisort( $sortCmcApiGroupIds , 'cmc_console_user_last_synced_at' , SORT_ASC); foreach ( $sortCmcApiGroupIds as $sortCmcApiGroupId ) { $isLockExist = RedisCmcConsoleUser::isLockExist( $sortCmcApiGroupId [ 'group_id' ]); // 返回 true,表示锁定存在,即已经被其他客户端锁定 if ( $isLockExist === true) { continue ; } // HTTP请求,获取租户ID下的用户列表 $httpGetUserListData = [ 'groupId' => $sortCmcApiGroupId [ 'group_id' ], 'loginId' => '' , 'loginTid' => '' , ]; $getUserListData = CmcConsoleUserService::httpGetUserList( $httpGetUserListData ); // 框架服务控制台的用户模型(Http),重建数组索引(以 ID 索引结果集) $httpCmcConsoleUserItems = ArrayHelper::index( $getUserListData [ 'list' ], 'id' ); // 销毁变量 unset( $getUserListData [ 'list' ]); // 基于租户ID查询框架服务控制台的用户模型(Redis)(以 ID 索引结果集) $redisCmcConsoleUserItems = RedisCmcConsoleUser::find()->where([ 'group_id' => $sortCmcApiGroupId [ 'group_id' ]])->indexBy( 'id' )->asArray()->all(); // 使用键名比较计算数组的差集,如果不为空,则删除出现在 Redis 中但是未出现在 Http 中的记录 $redisArrayDiffItems = array_diff_key ( $redisCmcConsoleUserItems , $httpCmcConsoleUserItems ); // 销毁变量 unset( $redisCmcConsoleUserItems ); if (! empty ( $redisArrayDiffItems )) { $redisArrayDiffIds = array_keys ( $redisArrayDiffItems ); // 销毁变量 unset( $redisArrayDiffItems ); if (RedisCmcConsoleUser::deleteAllByIds( $sortCmcApiGroupId [ 'group_id' ], $redisArrayDiffIds ) === false) { continue ; } } // 遍历框架服务控制台的用户模型(Http),判断在 Redis 中是否存在,如果不存在,则插入,如果存在且更新时间不相等,则更新 foreach ( $httpCmcConsoleUserItems as $httpCmcConsoleUserItemValue ) { $redisCmcConsoleUserItem = RedisCmcConsoleUser::find()->where([ 'id' => $httpCmcConsoleUserItemValue [ 'id' ]])->one(); if (!isset( $redisCmcConsoleUserItem )) { $redisCmcConsoleUser = new RedisCmcConsoleUser(); $attributes = $this ->getAttributes( $getUserListData [ 'group_info' ][ 'group_id' ], $httpCmcConsoleUserItemValue ); $redisCmcConsoleUser ->attributes = $attributes ; $redisCmcConsoleUser ->insert(); } else { if ( $httpCmcConsoleUserItemValue [ 'update_time' ] != $redisCmcConsoleUserItem [ 'update_time' ]) { $attributes = $this ->getAttributes( $getUserListData [ 'group_info' ][ 'group_id' ], $httpCmcConsoleUserItemValue ); $redisCmcConsoleUserItem ->attributes = $attributes ; $redisCmcConsoleUserItem ->save(); } } } // 从缓存中取回租户ID列表 $cmcApiGroupIds = $redisCache [ $redisCacheGroupIdsKey ]; // 设置当前租户的上次同步时间 foreach ( $cmcApiGroupIds as $cmcApiGroupIdKey => $cmcApiGroupId ) { if ( $cmcApiGroupId [ 'group_id' ] == $sortCmcApiGroupId [ 'group_id' ]) { $cmcApiGroupIds [ $cmcApiGroupIdKey ][ 'cmc_console_user_last_synced_at' ] = time(); break ; } } // 将 $cmcApiGroupIds 存放到缓存供下次使用,将数据在缓存中永久保留 $redisCache ->set( $redisCacheGroupIdsKey , $cmcApiGroupIds ); // break 结束当前 foreach 结构的执行,即命令行的每一次运行,仅同步成功一个租户下的用户列表 break ; } // 延缓执行 60 秒 // sleep(Yii::$app->params['cmcConsoleUser']['isEmptyNoSleepTime']); // 将同步标识存放到缓存供下次使用,将数据在缓存中保留 60 秒 $redisCache ->set( $redisCacheIdentityKey , $redisCacheIdentityKey , Yii:: $app ->params[ 'cmcConsoleUser' ][ 'isEmptyNoSleepTime' ]); // file_put_contents('E:/wwwroot/pcs-api/console/runtime/logs/cmc-console-user-sync-memory-get-usage-' . time() . '.txt', memory_get_usage() / 1024 / 1024 . 'MB'); // file_put_contents('E:/wwwroot/pcs-api/console/runtime/logs/cmc-console-user-sync-memory-get-peak-usage-' . time() . '.txt', memory_get_peak_usage() / 1024 / 1024 . 'MB'); return ExitCode::OK; } else { // 延缓执行 60 秒 sleep(Yii:: $app ->params[ 'cmcConsoleUser' ][ 'isEmptyNoSleepTime' ]); return ExitCode::OK; } } /** * 获取属性列表 * @param string $groupId 租户ID * @param array $httpCmcConsoleUser 框架服务控制台的用户模型(Http) * * @return array */ private function getAttributes( $groupId , $httpCmcConsoleUser ) { return [ 'id' => $httpCmcConsoleUser [ 'id' ], 'group_id' => $groupId , 'login_name' => $httpCmcConsoleUser [ 'login_name' ], 'user_token' => $httpCmcConsoleUser [ 'user_token' ], 'user_nick' => $httpCmcConsoleUser [ 'user_nick' ], 'user_pic' => $httpCmcConsoleUser [ 'user_pic' ], 'user_mobile' => $httpCmcConsoleUser [ 'user_mobile' ] ? $httpCmcConsoleUser [ 'user_mobile' ] : '' , 'user_email' => $httpCmcConsoleUser [ 'user_email' ] ? $httpCmcConsoleUser [ 'user_email' ] : '' , 'user_sex' => $httpCmcConsoleUser [ 'user_sex' ], 'user_type' => $httpCmcConsoleUser [ 'user_type' ], 'user_birthday' => $httpCmcConsoleUser [ 'user_birthday' ], 'user_chat_id' => $httpCmcConsoleUser [ 'user_chat_id' ] ? $httpCmcConsoleUser [ 'user_chat_id' ] : '' , 'is_open' => $httpCmcConsoleUser [ 'is_open' ], 'add_time' => $httpCmcConsoleUser [ 'add_time' ], 'update_time' => $httpCmcConsoleUser [ 'update_time' ], ]; } } |
1 2 3 4 5 | 4.6722030639648MB //优化前(有 HTTP 请求,有同步) 2.8900680541992MB //优化前(无 HTTP 请求,无同步) 4.6064605712891MB //优化前(无 HTTP 请求,有同步) 6.2168731689453MB //优化后(有 HTTP 请求,有同步) 2.8925552368164MB //优化后(无 HTTP 请求,无同步) |
1 2 3 4 5 | 10.41194152832MB //优化前(有 HTTP 请求,有同步) 2.9416198730469MB //优化前(无 HTTP 请求,无同步) 10.346199035645MB //优化前(无 HTTP 请求,有同步) 9.6655578613281MB //优化后(有 HTTP 请求,有同步) 2.9470977783203MB //优化后(无 HTTP 请求,无同步) |
41、分析优化第 2 个命令行脚本,\console\controllers\ConfigColumnUserController.php,遍历租户ID列表时,break 结束当前 foreach 结构的执行,即命令行的每一次运行,仅同步成功一个租户下的用户列表。在 无 HTTP 请求,有同步 的情况下,实际使用的内存量减少 0.45 MB 左右,系统分配总的内存量无变化
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 | <?php /** * Created by PhpStorm. * User: Qiang Wang * Date: 2019/03/01 * Time: 13:17 */ namespace console\controllers; use Yii; use console\models\ConfigColumnUser; use console\services\CmcConsoleUserService; use yii\console\Controller; use yii\console\ExitCode; use yii\helpers\ArrayHelper; use yii\web\HttpException; use yii\web\ServerErrorHttpException; /** * 栏目人员配置 * * @author Qiang Wang <shuijingwanwq@163.com> * @since 1.0 */ class ConfigColumnUserController extends Controller { /** * 同步框架服务控制台的用户模型(Http)至栏目人员配置模型(MySQL) * * @throws ServerErrorHttpException * @throws HttpException 如果登录超时 */ public function actionSync() { // 设置同步标识的缓存键 $redisCache = Yii:: $app ->redisCache; $redisCacheIdentityKey = 'config_column_user_sync' ; // 从缓存中取回同步标识 $redisCacheIdentityData = $redisCache [ $redisCacheIdentityKey ]; if ( $redisCacheIdentityData === false) { // 设置租户ID列表的缓存键 $redisCacheGroupIdsKey = 'cmc_api_group_ids' ; // 从缓存中取回租户ID列表 $redisCacheGroupIdsData = $redisCache [ $redisCacheGroupIdsKey ]; if ( $redisCacheGroupIdsData === false) { return ExitCode::OK; } else { $cmcApiGroupIds = $redisCacheGroupIdsData ; } /* 判断 $cmcApiGroupIds 是否为空,如果为空,则成功退出 */ if ( empty ( $cmcApiGroupIds )) { // 延缓执行 60 * 60 秒 sleep(Yii:: $app ->params[ 'configColumnUser' ][ 'isEmptyYesSleepTime' ]); return ExitCode::OK; } // 基于上次同步时间顺序排列,赋值给:$sortCmcApiGroupIds $sortCmcApiGroupIds = $cmcApiGroupIds ; ArrayHelper::multisort( $sortCmcApiGroupIds , 'config_column_user_last_synced_at' , SORT_ASC); foreach ( $sortCmcApiGroupIds as $sortCmcApiGroupId ) { // HTTP请求,获取租户ID下的用户列表 $httpGetUserListData = [ 'groupId' => $sortCmcApiGroupId [ 'group_id' ], 'loginId' => '' , 'loginTid' => '' , ]; $getUserListData = CmcConsoleUserService::httpGetUserList( $httpGetUserListData ); // 框架服务控制台的用户模型(Http),重建数组索引(以 ID 索引结果集) $httpCmcConsoleUserItems = ArrayHelper::index( $getUserListData [ 'list' ], 'id' ); // 销毁变量 unset( $getUserListData ); // 基于租户ID查询栏目人员配置模型(MySQL)(以 用户ID 索引结果集) $configColumnUserItems = ConfigColumnUser::find()->where([ 'group_id' => $sortCmcApiGroupId [ 'group_id' ]])->isDeletedNo()->indexBy( 'user_id' )->asArray()->all(); // 使用键名比较计算数组的差集,如果不为空,则删除 (软删除) 出现在 栏目人员配置模型(MySQL) 中但是未出现在 框架服务控制台的用户模型(Http) 中的记录 $diffItems = array_diff_key ( $configColumnUserItems , $httpCmcConsoleUserItems ); // 销毁变量 unset( $httpCmcConsoleUserItems , $configColumnUserItems ); if (! empty ( $diffItems )) { // 基于租户ID、用户ID查询栏目人员配置模型(MySQL)(待删除) $toBeDeletedConfigColumnUserItems = ConfigColumnUser::find()->where([ 'and' , [ 'group_id' => $sortCmcApiGroupId [ 'group_id' ]], [ 'in' , 'user_id' , $diffItems ], ])->isDeletedNo()->all(); foreach ( $toBeDeletedConfigColumnUserItems as $toBeDeletedConfigColumnUserItem ) { /* @var $toBeDeletedConfigColumnUserItem ConfigColumnUser */ $toBeDeletedConfigColumnUserItem ->softDelete(); } } // 从缓存中取回租户ID列表 $cmcApiGroupIds = $redisCache [ $redisCacheGroupIdsKey ]; // 设置当前租户的上次同步时间 foreach ( $cmcApiGroupIds as $cmcApiGroupIdKey => $cmcApiGroupId ) { if ( $cmcApiGroupId [ 'group_id' ] == $sortCmcApiGroupId [ 'group_id' ]) { $cmcApiGroupIds [ $cmcApiGroupIdKey ][ 'config_column_user_last_synced_at' ] = time(); break ; } } // 将 $cmcApiGroupIds 存放到缓存供下次使用,将数据在缓存中永久保留 $redisCache ->set( $redisCacheGroupIdsKey , $cmcApiGroupIds ); // break 结束当前 foreach 结构的执行,即命令行的每一次运行,仅同步成功一个租户下的用户列表 break ; } // 延缓执行 60 秒 // sleep(Yii::$app->params['configColumnUser']['isEmptyNoSleepTime']); // 将同步标识存放到缓存供下次使用,将数据在缓存中保留 60 秒 $redisCache ->set( $redisCacheIdentityKey , $redisCacheIdentityKey , Yii:: $app ->params[ 'cmcConsoleUser' ][ 'isEmptyNoSleepTime' ]); // file_put_contents('E:/wwwroot/pcs-api/console/runtime/logs/config-column-user-sync-memory-get-usage-' . time() . '.txt', memory_get_usage() / 1024 / 1024 . 'MB'); // file_put_contents('E:/wwwroot/pcs-api/console/runtime/logs/config-column-user-sync-memory-get-peak-usage-' . time() . '.txt', memory_get_peak_usage() / 1024 / 1024 . 'MB'); return ExitCode::OK; } else { // 延缓执行 60 秒 sleep(Yii:: $app ->params[ 'configColumnUser' ][ 'isEmptyNoSleepTime' ]); return ExitCode::OK; } } } |
1 2 3 4 5 | 5.5944900512695MB //优化前(有 HTTP 请求,有同步) 2.8861389160156MB //优化前(无 HTTP 请求,无同步) 5.3590927124023MB //优化前(无 HTTP 请求,有同步) 4.9042816162109MB //优化后(无 HTTP 请求,有同步) 2.8837127685547MB //优化后(无 HTTP 请求,无同步) |
1 2 3 4 5 | 11.753318786621MB //优化前(有 HTTP 请求,有同步) 2.9376907348633MB //优化前(无 HTTP 请求,无同步) 11.67618560791MB //优化前(无 HTTP 请求,有同步) 10.693077087402MB //优化后(无 HTTP 请求,有同步) 2.9382553100586MB //优化后(无 HTTP 请求,无同步) |
42、升级至开发环境,Docker 容器的 CPU:0.07%,内存:283MB,CPU 增加了 (0.07% – 0.04%) = 0.03% 左右,内存减少了 (283MB – 276MB) = 7MB 左右。证明遍历租户ID列表时,break 结束当前 foreach 结构的执行,即命令行的每一次运行,仅同步成功一个租户下的用户列表。优化的方案是不可行的,意义不大。如图29、图30
43、查看 MySQL 的实例监控情况,内存占用:6166MB,参考价值不大,因为是与其他产品共享同一个数据库,如图31
44、查看 Redis 的实例监控情况,已使用容量:75MB,如图32
45、占用内存的减少实现方案,总结如下:
(1)数据量的增加,会导致内存的持续增加,后期当数据量达致一定的量级时,需要支持集群部署
(2)Docker 容器的 CPU:5.4%,内存:371MB,内存减少了 (541MB – 371MB) = 170MB 左右,证明基于 unset() 实现的方案是可行的
(3)Docker 容器的 CPU:5.4%,内存:280MB,内存减少了 (371MB – 371MB) = 0MB 左右,证明减少命令行的执行时间,对于内存的使用优化无意义。
(4)Docker 容器的 CPU:0.04%,内存:283MB,内存减少了 (371MB – 283MB) = 90MB 左右,证明变量仅在使用时才定义,以数组形式获取数据,在查询方法前调用 asArray() 方法,来获取 PHP 数组形式的结果。优化的方案是可行的。
(5)Docker 容器的 CPU:92%,内存:435MB,CPU 增加了 (92% – 0.04%) = 91.96% 左右,内存增加了 (435MB – 283MB) = 152MB 左右。HTTP 请求响应 400 时,命令行抛出异常时,CPU 与内存皆增加得很厉害
(6)解决了 响应 400 的 Bug 之后,升级至开发环境,Docker 容器的 CPU:0.04%,内存:341MB,CPU无变化,内存增加了 (341MB – 283MB) = 60MB 左右。删除 RedisCmcConsoleUser::find(),以另外一种形式(HTTP 请求)来获取租户ID列表,实际使用的内存量增加 0.37 MB 左右,系统分配总的内存量减少 19.39 MB 左右。证明实际使用的内存量增加,系统分配总的内存量减少,Docker 的内存占用是在增加的(主要受到实际使用的内存量的影响)
(7)Docker 容器的 CPU:16%,内存:486MB,CPU 增加了 (16% – 0.04%) = 15.96% 左右,内存增加了 (486MB – 341MB) = 140MB 左右,查看 supervisord 运行状态,发现命令行执行的时间很短、频率很高时,CPU 与内存皆增加得一般厉害
(8)Docker 容器的 CPU:0.04%,内存:283MB,CPU 无变化,内存减少了 (341MB – 283MB) = 60MB 左右。证明同步成功后的 60 秒内,避免再次同步。优化的方案是可行的
(9)决定再次降低同步的执行频率,同步成功后的 5 * 60 秒内,避免再次同步。当执行频率达到某个临界值之后,再次降低,优化的意义不大了的,顶多 CPU 的平均值更低一些(理论上)
(10)遍历租户ID列表时,break 结束当前 foreach 结构的执行,即命令行的每一次运行,仅同步成功一个租户下的用户列表。Docker 容器升级后,性能指标未变化。优化的意义不大了的,虽然命令行同步一次的运行时间大幅度减少(原因可能在于开发环境的租户数量不多,总的数据量不大,优化的意义未体现出来。当租户数量更多时,内存占用的优化意义才能够体现出来)
近期评论