在 Yii 2.0 中,控制台命令的集群实现,Redis模型的锁定实现(以保证同一时间段内,即使多台服务器皆在运行命令行,但是每台服务器运行的任务是不重复的,以提升命令行的总体处理性能)

1、Docker 部署,基于 Supervisor 的 crontab (bash sleep) 的实现,以降低内存占用,参考:https://www.shuijingwanwq.com/2019/10/12/3555/ 。/console/controllers/CmcConsoleUserController.php

<?php
/**
 * Created by PhpStorm.
 * User: Qiang Wang
 * Date: 2019/01/09
 * Time: 13:55
 */
namespace console\controllers;

use Yii;
use console\models\ConfigColumnUser;
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)、栏目人员配置模型(MySQL)
     *
     * @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, //上次同步时间
                    ];
                }
                // 是否设置租户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, //上次同步时间
                        ];
                        // 是否设置租户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;
                    }
                }

                // 基于租户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($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();
                    }
                }

                // 遍历框架服务控制台的用户模型(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'],
        ];
    }
}

2、break 结束当前 foreach 结构的执行,即命令行的每一次运行,仅同步成功一个租户下的用户列表。当租户数量过多的时候,假设 100 个租户,平均 1 个租户的同步时间为 10 秒钟 + 下一个租户的同步的间隔时间 60 秒(包含了 bash sleep 5 秒),那么可能某个租户下的用户同步,其最大间隔时间就有可能为 7000 秒钟了。因此,如果支持了集群的部署,假设为 10 个容器,那么某个租户下的用户同步,其最大间隔时间就有可能为 700 秒钟了,同步的即时性提升了 10 倍。/mcloud/yii-cmc-console-user-sync.ini

[program:yii-cmc-console-user-sync]
command = bash -c 'sleep 5 && exec php /mcloud/www/pcs-api/yii cmc-console-user/sync'
autorestart = true
startsecs = 5
stopwaitsecs = 10
stderr_logfile = /data/logs/yii-cmc-console-user-sync-stderr.log
stdout_logfile = /data/logs/yii-cmc-console-user-sync-stdout.log

3、如果要支持集群的部署,现阶段存在的问题:同一个租户下的用户同步,可能在某一时间段,多个容器皆在执行,导致了同步的冗余执行(未达到最大化利用集群部署,进而满足同步的即时性提升至 10 倍的理想值)。因此,应该保证同一个租户下的用户同步,在某一时间段,仅在 1 个容器中执行。

4、将同步标识存放到缓存供下次使用,将数据在缓存中保留 60 秒。之前实现的初衷是同步成功一个租户下的用户后,间隔 60 秒再同步下一个租户下的用户,以降低资源消耗。现在如果要支持集群,就需要取消。否则会导致某个容器同步成功一个租户下的用户后,在 60 秒的间隔时间段内,其他容器也无法同步其他租户下的用户。/console/controllers/CmcConsoleUserController.php

<?php
/**
 * Created by PhpStorm.
 * User: Qiang Wang
 * Date: 2019/01/09
 * Time: 13:55
 */
namespace console\controllers;

use Yii;
use console\models\ConfigColumnUser;
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)、栏目人员配置模型(MySQL)
     *
     * @throws ServerErrorHttpException
     * @throws HttpException 如果登录超时
     * @throws InvalidArgumentException if the $direction or $sortFlag parameters do not have
     */    public function actionSync()
    {
        // 设置同步标识的缓存键
        $redisCache = Yii::$app->redisCache;

        // 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, //上次同步时间
                ];
            }
            // 是否设置租户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, //上次同步时间
                    ];
                    // 是否设置租户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;
                }
            }

            // 基于租户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($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();
                }
            }

            // 遍历框架服务控制台的用户模型(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']);

        // 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;
    }

}

5、此时,当租户数量过多的时候,假设 100 个租户,平均 1 个租户的同步时间为 10 秒钟 + 下一个租户的同步的间隔时间 60 秒(包含了 bash sleep 60 秒),那么可能某个租户下的用户同步,其最大间隔时间就有可能为 7000 秒钟了。因此,如果支持了集群的部署,假设为 10 个容器,那么某个租户下的用户同步,其最大间隔时间就有可能为 700 秒钟了,同步的即时性提升了 10 倍。/mcloud/yii-cmc-console-user-sync.ini

[program:yii-cmc-console-user-sync]
command = bash -c 'sleep 60 && exec php /mcloud/www/pcs-api/yii cmc-console-user/sync'
autorestart = true
startsecs = 5
stopwaitsecs = 10
stderr_logfile = /data/logs/yii-cmc-console-user-sync-stderr.log
stdout_logfile = /data/logs/yii-cmc-console-user-sync-stdout.log

6、编辑 /common/logics/redis/Lock.php,Redis模型的锁定实现

<?php

namespace common\logics\redis;

use Yii;

/**
 * This is the model class for table "{{%lock}}".
 *
 * @author Qiang Wang <shuijingwanwq@163.com>
 * @since 1.0
 */class Lock extends \yii\redis\ActiveRecord
{

    /**
     * Redis模型的锁定实现
     * @param string $lockKeyName 锁定键名
     * 格式如下:
     *
     * 'game_category' //锁定键名,如比赛分类
     *
     * @return bool 成功返回真/失败返回假
     * 格式如下:
     *
     * true //状态,获取锁定成功,可继续执行
     * 
     * 或者
     * 
     * false //状态,获取锁定失败,不可继续执行
     *
     */    public function lock($lockKeyName)
    {
        // 设置锁定的过期时间,获取相关锁定参数
        $time = time();
        $lockKey = Yii::$app->params['redisLock']['keyPrefix'] . $lockKeyName;
        $lockExpire = $time + Yii::$app->params['redisLock']['timeOut'];
        // 获取 Redis 连接,以执行相关命令
        $redis = Yii::$app->redis;
        // 获取锁定
        $executeCommandResult = $redis->setnx($lockKey, $lockExpire);
        // 返回0,表示已经被其他客户端锁定
        if ($executeCommandResult == 0) {
            // 防止死锁,获取当前锁的过期时间
            $lockCurrentExpire = $redis->get($lockKey);
            // 判断锁是否过期,如果已经过期
            if ($time > $lockCurrentExpire) {
                // 防止并发锁定,检查存储在 key 的旧值是否仍然是过期的时间戳,如果是,则获取锁定,否则返回假
                $executeCommandResult = $redis->getset($lockKey, $lockExpire);
                if ($lockCurrentExpire != $executeCommandResult) {
                    return false;
                }
            }

            // 返回0,表示已经被其他客户端锁定,且不存在死锁,返回假
            if ($executeCommandResult == 0) {
                return false;
            }

        }
        
        return true;
    }
    
    /**
     * 判断Redis模型的锁定是否存在
     * @param string $lockKeyName 锁定键名
     * 格式如下:
     *
     * 'game_category' //锁定键名,如比赛分类
     *
     * @return bool 锁定是否存在
     * 格式如下:
     *
     * true //状态:已存在
     * 
     * 或者
     * 
     * false //状态:不存在
     *
     */    public function isLockExist($lockKeyName)
    {
        // 获取相关锁定参数
        $time = time();
        $lockKey = Yii::$app->params['redisLock']['keyPrefix'] . $lockKeyName;
        // 获取 Redis 连接,以执行相关命令
        $redis = Yii::$app->redis;
        // 获取锁定
        $executeCommandResult = $redis->get($lockKey);

        // 返回NULL,表示不存在锁定,否则表示存在
        if ($executeCommandResult === null) {
            return false;
        } else {
            // 如果存在锁定,判断锁是否过期,如果已经过期,则仍然认定为不存在锁定
            if ($time > $executeCommandResult) {
                // 如果已经过期,则释放锁定
                $redis->del($lockKey);
                return false;
            }
            
        }

        return true;
    }

    /**
     * Redis模型的释放锁定实现
     * @param string $lockKeyName 锁定键名
     * 格式如下:
     *
     * 'game_category' //锁定键名,如比赛分类
     *
     * @return integer 被删除的keys的数量
     * 格式如下:
     *
     * 1 //被删除的keys的数量
     * 
     * 或者
     * 
     * 0 //被删除的keys的数量
     *
     */    public function unlock($lockKeyName)
    {
        // 获取相关锁定参数
        $lockKey = Yii::$app->params['redisLock']['keyPrefix'] . $lockKeyName;
        // 获取 Redis 连接,以执行相关命令
        $redis = Yii::$app->redis;
        // 释放锁定
        return $redis->del($lockKey);
    }
}

7、编辑 /console/controllers/CmcConsoleUserController.php,Redis模型的锁定实现。在开始一个租户下的用户同步之前,判断Redis模型的锁定是否存在,如果存在,则跳出当前循环,继续下一个租户的同步。在开始一个租户下的用户同步之前,Redis模型的获取锁定实现,如果获取锁定失败,则跳出当前循环,继续下一个租户的同步。一个租户下的用户同步成功之后,释放锁定。结束循环。

<?php
/**
 * Created by PhpStorm.
 * User: Qiang Wang
 * Date: 2019/01/09
 * Time: 13:55
 */
namespace console\controllers;

use Yii;
use console\models\ConfigColumnUser;
use console\models\redis\cmc_console\User as RedisCmcConsoleUser;
use console\models\redis\Lock as RedisLock;
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)、栏目人员配置模型(MySQL)
     *
     * @throws ServerErrorHttpException
     * @throws HttpException 如果登录超时
     * @throws InvalidArgumentException if the $direction or $sortFlag parameters do not have
     */    public function actionSync()
    {
        // 设置同步标识的缓存键
        $redisCache = Yii::$app->redisCache;

        // 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, //上次同步时间
                ];
            }
            // 是否设置租户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, //上次同步时间
                    ];
                    // 是否设置租户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;
            }

            /* 判断Redis模型的锁定是否存在 */            $redisLockKeyName = 'cmc_console_user:sync:' . implode(':', ['group_id' => $sortCmcApiGroupId['group_id']]);
            $redisLock = new RedisLock();
            $redisLockResult = $redisLock->isLockExist($redisLockKeyName);
            // 返回 true,表示锁定存在,即已经被其他客户端锁定
            if ($redisLockResult === true) {
                continue;
            }

            /* Redis模型的锁定实现 */            $redisLockResult = $redisLock->lock($redisLockKeyName);
            // 返回 false,表示已经被其他客户端锁定
            if ($redisLockResult === false) {
                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;
                }
            }

            // 基于租户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($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();
                }
            }

            // 遍历框架服务控制台的用户模型(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);

            // 释放锁定
            $redisLock->unlock($redisLockKeyName);

            // break 结束当前 foreach 结构的执行,即命令行的每一次运行,仅同步成功一个租户下的用户列表
            break;
        }

        // 延缓执行 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;
    }

}

8、当一个租户下的用户同步时间长度小于等于 Redis锁定超时时间 的值 10秒时(sleep(8),实际长度超过了 8 秒),此时,此租户已经处于锁定状态,进而导致部署为集群时,可以保证同一个租户下的用户同步,在某一时间段,仅在 1 个容器中执行。在本地环境中,模拟了 3 个容器,3 个容器开始同步时间:1582784312、1582784315、1582784318,分别间隔 3 秒、3秒,皆小于 10 秒。第 1 个容器同步了租户:c10e87f39873512a16727e17f57456a5,第 2 个容器同步了租户:015ce30b116ce86058fa6ab4fea4ac63,第 3 个容器同步了租户:4fd58ceba1fbc537b5402302702131eb。3 个容器的并行执行,分别同步了 3 个租户,符合预期。文件生成顺序体现出了执行执行流程。如图1

图1

    'redisLock' => [
        'keyPrefix' => 'pa:lock:', //Redis锁定 key 前缀
        'timeOut' => 10, //Redis锁定超时时间,单位为秒
    ],
            file_put_contents('E:/wwwroot/pcs-api/console/runtime/logs/cmc-console-user-sync-' . $sortCmcApiGroupId['group_id'] . '-' . time() . '.txt', $sortCmcApiGroupId['group_id']);

            /* 判断Redis模型的锁定是否存在 */            $redisLockKeyName = 'cmc_console_user:sync:' . implode(':', ['group_id' => $sortCmcApiGroupId['group_id']]);
            $redisLock = new RedisLock();
            $isLockExist = $redisLock->isLockExist($redisLockKeyName);
            // 返回 true,表示锁定存在,即已经被其他客户端锁定
            if ($isLockExist === true) {
                file_put_contents('E:/wwwroot/pcs-api/console/runtime/logs/cmc-console-user-sync-is-lock-exist-' . $sortCmcApiGroupId['group_id'] . '-' . time() . '.txt', $sortCmcApiGroupId['group_id']);
                continue;
            }

            /* Redis模型的锁定实现 */            $lock = $redisLock->lock($redisLockKeyName);
            // 返回 false,表示已经被其他客户端锁定
            if ($lock === false) {
                file_put_contents('E:/wwwroot/pcs-api/console/runtime/logs/cmc-console-user-sync-lock-' . $sortCmcApiGroupId['group_id'] . '-' . time() . '.txt', $sortCmcApiGroupId['group_id']);
                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;
                }
            }

            // 基于租户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($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();
                }
            }

            // 遍历框架服务控制台的用户模型(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 存放到缓存供下次使用,将数据在缓存中永久保留
            sleep(8);
            $redisCache->set($redisCacheGroupIdsKey, $cmcApiGroupIds);

            // 释放锁定
            $redisLock->unlock($redisLockKeyName);

9、打印出从缓存中取回租户ID列表,已经成功同步了 3 个租户:c10e87f39873512a16727e17f57456a5、015ce30b116ce86058fa6ab4fea4ac63、4fd58ceba1fbc537b5402302702131eb,但是,仅有租户 4fd58ceba1fbc537b5402302702131eb 的同步时间得到了更新。虽然在实际的生产环境中,不存在 sleep(8),此为在本地环境模拟并发请求,而添加的。但是,理论上来说,从缓存中取回租户ID列表,到 将 $cmcApiGroupIds 存放到缓存供下次使用,将数据在缓存中永久保留 的间隔时间段(假设租户数量为 10000 个,则遍历 10000 次,时间间隔为 30000 微秒,即 0.03 秒)内,如果存在 多个容器 在运行的话,便会出现相互覆盖的情况。暂时不做处理,即时出现了相互覆盖的情况,其后果是可以接受的。租户:c10e87f39873512a16727e17f57456a5、015ce30b116ce86058fa6ab4fea4ac63 会相继立即重复同步一次。如图2

图2

Array
(
    [0] => Array
        (
            [group_id] => c10e87f39873512a16727e17f57456a5
            [cmc_console_user_last_synced_at] => 0
        )

    [1] => Array
        (
            [group_id] => 015ce30b116ce86058fa6ab4fea4ac63
            [cmc_console_user_last_synced_at] => 0
        )

    [2] => Array
        (
            [group_id] => 4fd58ceba1fbc537b5402302702131eb
            [cmc_console_user_last_synced_at] => 1582784318
        )

    [3] => Array
        (
            [group_id] => f53df1a8d46108afc8ae9eeb3f0e1f0e
            [cmc_console_user_last_synced_at] => 0
        )

)

10、当一个租户下的用户同步时间长度超过 Redis锁定超时时间 的值 10秒后(sleep(60),实际长度超过了 60 秒),此时,此租户已经被自动解除锁定,进而导致部署为集群时,无法保证同一个租户下的用户同步,在某一时间段,仅在 1 个容器中执行。在本地环境中,模拟了 3 个容器,3 个容器开始同步时间:1582773882、1582773895、1582773917,分别间隔 13 秒、22秒,皆大于 10 秒。第 3 个容器开始同步时,第 1 个容器仍然在同步中,并未结束(释放锁定)。因此,租户:c10e87f39873512a16727e17f57456a5 被 3 个容器同时同步。如图3

图3


            // 将 $cmcApiGroupIds 存放到缓存供下次使用,将数据在缓存中永久保留
            sleep(60);
            $redisCache->set($redisCacheGroupIdsKey, $cmcApiGroupIds);

            // 释放锁定
            $redisLock->unlock($redisLockKeyName);

11、编辑 /common/logics/redis/Lock.php。public function lock($lockKeyName),添加一个参数,支持自定义Redis锁定超时时间。以基于场景灵活设置。

    /**
     * Redis模型的锁定实现
     * @param string $lockKeyName 锁定键名
     * 格式如下:
     *
     * 'game_category' //锁定键名,如比赛分类
     *
     * @param int $timeOut Redis锁定超时时间,单位为秒
     * 格式如下:3
     *
     * @return bool 成功返回真/失败返回假
     * 格式如下:
     *
     * true //状态,获取锁定成功,可继续执行
     * 
     * 或者
     * 
     * false //状态,获取锁定失败,不可继续执行
     *
     */    public function lock($lockKeyName, $timeOut = 3)
    {
        // 设置锁定的过期时间,获取相关锁定参数
        $time = time();
        $lockKey = Yii::$app->params['redisLock']['keyPrefix'] . $lockKeyName;
        $lockExpire = $time + $timeOut;
        // 获取 Redis 连接,以执行相关命令
        $redis = Yii::$app->redis;
        // 获取锁定
        $executeCommandResult = $redis->setnx($lockKey, $lockExpire);
        // 返回0,表示已经被其他客户端锁定
        if ($executeCommandResult == 0) {
            // 防止死锁,获取当前锁的过期时间
            $lockCurrentExpire = $redis->get($lockKey);
            // 判断锁是否过期,如果已经过期
            if ($time > $lockCurrentExpire) {
                // 防止并发锁定,检查存储在 key 的旧值是否仍然是过期的时间戳,如果是,则获取锁定,否则返回假
                $executeCommandResult = $redis->getset($lockKey, $lockExpire);
                if ($lockCurrentExpire != $executeCommandResult) {
                    return false;
                }
            }

            // 返回0,表示已经被其他客户端锁定,且不存在死锁,返回假
            if ($executeCommandResult == 0) {
                return false;
            }

        }
        
        return true;
    }

12、Redis模型的锁定实现,Redis锁定超时时间,单位为秒(自定义 600)。一个租户下的用户同步,耗费的最长时间只要不超过 10 分钟,当部署为集群时,就可以保证同一个租户下的用户同步,在某一时间段,仅在 1 个容器中执行。

            /* Redis模型的锁定实现 */            $lock = $redisLock->lock($redisLockKeyName, 600);

13、在开发环境,部署了 3 个容器。以便于测试集群部署时,并发锁定的情况。在 bash sleep 60 秒 的情况下,很难出现并发锁定的情况。不过,当部署为集群时,已经可以保证同一个租户下的用户同步,在某一时间段,仅在 1 个容器中执行。当出现租户下的用户同步及时性不够理想的情况,可以通过添加容器的方案提升同步的及时性。在 3 个容器中,总计同步了 30 + 30 + 33 = 93 次。其中 租户 f53df1a8d46108afc8ae9eeb3f0e1f0e、c10e87f39873512a16727e17f57456a5、4fd58ceba1fbc537b5402302702131eb、015ce30b116ce86058fa6ab4fea4ac63 分别同步了:23、24、22、24 次。3 个容器的情况下,在 15:43 至 16:16 的时间段内(32 分钟左右),一个租户的同步次数平均为:23 次。实际测试结果,一个租户的同步时间间隔为:32 * 60 / 24 = 80 秒。理论上的计算公式,一个租户的同步时间间隔为:租户数量 / 容器数量 * 60,结果单位为秒。同步性能符合设计预期。如图4

图4

[root@1d03b809a523 logs]# ls -ltr
total 120
-rw-r--r-- 1 root root 32 Feb 28 15:43 cmc-console-user-sync-f53df1a8d46108afc8ae9eeb3f0e1f0e-1582875818.txt
-rw-r--r-- 1 root root 32 Feb 28 15:44 cmc-console-user-sync-c10e87f39873512a16727e17f57456a5-1582875879.txt
-rw-r--r-- 1 root root 32 Feb 28 15:45 cmc-console-user-sync-4fd58ceba1fbc537b5402302702131eb-1582875941.txt
-rw-r--r-- 1 root root 32 Feb 28 15:49 cmc-console-user-sync-4fd58ceba1fbc537b5402302702131eb-1582876192.txt
-rw-r--r-- 1 root root 32 Feb 28 15:50 cmc-console-user-sync-015ce30b116ce86058fa6ab4fea4ac63-1582876254.txt
-rw-r--r-- 1 root root 32 Feb 28 15:51 cmc-console-user-sync-c10e87f39873512a16727e17f57456a5-1582876315.txt
-rw-r--r-- 1 root root 32 Feb 28 15:52 cmc-console-user-sync-f53df1a8d46108afc8ae9eeb3f0e1f0e-1582876377.txt
-rw-r--r-- 1 root root 32 Feb 28 15:53 cmc-console-user-sync-4fd58ceba1fbc537b5402302702131eb-1582876438.txt
-rw-r--r-- 1 root root 32 Feb 28 15:55 cmc-console-user-sync-015ce30b116ce86058fa6ab4fea4ac63-1582876500.txt
-rw-r--r-- 1 root root 32 Feb 28 15:56 cmc-console-user-sync-c10e87f39873512a16727e17f57456a5-1582876561.txt
-rw-r--r-- 1 root root 32 Feb 28 15:57 cmc-console-user-sync-f53df1a8d46108afc8ae9eeb3f0e1f0e-1582876623.txt
-rw-r--r-- 1 root root 32 Feb 28 15:58 cmc-console-user-sync-4fd58ceba1fbc537b5402302702131eb-1582876684.txt
-rw-r--r-- 1 root root 32 Feb 28 15:59 cmc-console-user-sync-015ce30b116ce86058fa6ab4fea4ac63-1582876746.txt
-rw-r--r-- 1 root root 32 Feb 28 16:00 cmc-console-user-sync-c10e87f39873512a16727e17f57456a5-1582876808.txt
-rw-r--r-- 1 root root 32 Feb 28 16:01 cmc-console-user-sync-f53df1a8d46108afc8ae9eeb3f0e1f0e-1582876869.txt
-rw-r--r-- 1 root root 32 Feb 28 16:02 cmc-console-user-sync-4fd58ceba1fbc537b5402302702131eb-1582876931.txt
-rw-r--r-- 1 root root 32 Feb 28 16:03 cmc-console-user-sync-015ce30b116ce86058fa6ab4fea4ac63-1582876992.txt
-rw-r--r-- 1 root root 32 Feb 28 16:04 cmc-console-user-sync-c10e87f39873512a16727e17f57456a5-1582877054.txt
-rw-r--r-- 1 root root 32 Feb 28 16:05 cmc-console-user-sync-f53df1a8d46108afc8ae9eeb3f0e1f0e-1582877115.txt
-rw-r--r-- 1 root root 32 Feb 28 16:06 cmc-console-user-sync-4fd58ceba1fbc537b5402302702131eb-1582877177.txt
-rw-r--r-- 1 root root 32 Feb 28 16:07 cmc-console-user-sync-015ce30b116ce86058fa6ab4fea4ac63-1582877238.txt
-rw-r--r-- 1 root root 32 Feb 28 16:08 cmc-console-user-sync-c10e87f39873512a16727e17f57456a5-1582877300.txt
-rw-r--r-- 1 root root 32 Feb 28 16:09 cmc-console-user-sync-f53df1a8d46108afc8ae9eeb3f0e1f0e-1582877362.txt
-rw-r--r-- 1 root root 32 Feb 28 16:10 cmc-console-user-sync-4fd58ceba1fbc537b5402302702131eb-1582877423.txt
-rw-r--r-- 1 root root 32 Feb 28 16:11 cmc-console-user-sync-015ce30b116ce86058fa6ab4fea4ac63-1582877485.txt
-rw-r--r-- 1 root root 32 Feb 28 16:12 cmc-console-user-sync-c10e87f39873512a16727e17f57456a5-1582877546.txt
-rw-r--r-- 1 root root 32 Feb 28 16:13 cmc-console-user-sync-f53df1a8d46108afc8ae9eeb3f0e1f0e-1582877608.txt
-rw-r--r-- 1 root root 32 Feb 28 16:14 cmc-console-user-sync-4fd58ceba1fbc537b5402302702131eb-1582877669.txt
-rw-r--r-- 1 root root 32 Feb 28 16:15 cmc-console-user-sync-015ce30b116ce86058fa6ab4fea4ac63-1582877731.txt
-rw-r--r-- 1 root root 32 Feb 28 16:16 cmc-console-user-sync-c10e87f39873512a16727e17f57456a5-1582877792.txt

[root@7e1ea0bc777c logs]# ls -ltr
total 120
-rw-r--r-- 1 root root 32 Feb 28 15:43 cmc-console-user-sync-c10e87f39873512a16727e17f57456a5-1582875803.txt
-rw-r--r-- 1 root root 32 Feb 28 15:44 cmc-console-user-sync-4fd58ceba1fbc537b5402302702131eb-1582875864.txt
-rw-r--r-- 1 root root 32 Feb 28 15:45 cmc-console-user-sync-015ce30b116ce86058fa6ab4fea4ac63-1582875926.txt
-rw-r--r-- 1 root root 32 Feb 28 15:49 cmc-console-user-sync-015ce30b116ce86058fa6ab4fea4ac63-1582876189.txt
-rw-r--r-- 1 root root 32 Feb 28 15:50 cmc-console-user-sync-c10e87f39873512a16727e17f57456a5-1582876251.txt
-rw-r--r-- 1 root root 32 Feb 28 15:51 cmc-console-user-sync-f53df1a8d46108afc8ae9eeb3f0e1f0e-1582876313.txt
-rw-r--r-- 1 root root 32 Feb 28 15:52 cmc-console-user-sync-4fd58ceba1fbc537b5402302702131eb-1582876374.txt
-rw-r--r-- 1 root root 32 Feb 28 15:53 cmc-console-user-sync-015ce30b116ce86058fa6ab4fea4ac63-1582876436.txt
-rw-r--r-- 1 root root 32 Feb 28 15:54 cmc-console-user-sync-c10e87f39873512a16727e17f57456a5-1582876497.txt
-rw-r--r-- 1 root root 32 Feb 28 15:55 cmc-console-user-sync-f53df1a8d46108afc8ae9eeb3f0e1f0e-1582876559.txt
-rw-r--r-- 1 root root 32 Feb 28 15:57 cmc-console-user-sync-4fd58ceba1fbc537b5402302702131eb-1582876621.txt
-rw-r--r-- 1 root root 32 Feb 28 15:58 cmc-console-user-sync-015ce30b116ce86058fa6ab4fea4ac63-1582876682.txt
-rw-r--r-- 1 root root 32 Feb 28 15:59 cmc-console-user-sync-c10e87f39873512a16727e17f57456a5-1582876744.txt
-rw-r--r-- 1 root root 32 Feb 28 16:00 cmc-console-user-sync-f53df1a8d46108afc8ae9eeb3f0e1f0e-1582876805.txt
-rw-r--r-- 1 root root 32 Feb 28 16:01 cmc-console-user-sync-4fd58ceba1fbc537b5402302702131eb-1582876867.txt
-rw-r--r-- 1 root root 32 Feb 28 16:02 cmc-console-user-sync-015ce30b116ce86058fa6ab4fea4ac63-1582876929.txt
-rw-r--r-- 1 root root 32 Feb 28 16:03 cmc-console-user-sync-c10e87f39873512a16727e17f57456a5-1582876990.txt
-rw-r--r-- 1 root root 32 Feb 28 16:04 cmc-console-user-sync-f53df1a8d46108afc8ae9eeb3f0e1f0e-1582877052.txt
-rw-r--r-- 1 root root 32 Feb 28 16:05 cmc-console-user-sync-4fd58ceba1fbc537b5402302702131eb-1582877113.txt
-rw-r--r-- 1 root root 32 Feb 28 16:06 cmc-console-user-sync-015ce30b116ce86058fa6ab4fea4ac63-1582877175.txt
-rw-r--r-- 1 root root 32 Feb 28 16:07 cmc-console-user-sync-c10e87f39873512a16727e17f57456a5-1582877237.txt
-rw-r--r-- 1 root root 32 Feb 28 16:08 cmc-console-user-sync-f53df1a8d46108afc8ae9eeb3f0e1f0e-1582877298.txt
-rw-r--r-- 1 root root 32 Feb 28 16:09 cmc-console-user-sync-4fd58ceba1fbc537b5402302702131eb-1582877360.txt
-rw-r--r-- 1 root root 32 Feb 28 16:10 cmc-console-user-sync-015ce30b116ce86058fa6ab4fea4ac63-1582877422.txt
-rw-r--r-- 1 root root 32 Feb 28 16:11 cmc-console-user-sync-c10e87f39873512a16727e17f57456a5-1582877483.txt
-rw-r--r-- 1 root root 32 Feb 28 16:12 cmc-console-user-sync-f53df1a8d46108afc8ae9eeb3f0e1f0e-1582877545.txt
-rw-r--r-- 1 root root 32 Feb 28 16:13 cmc-console-user-sync-4fd58ceba1fbc537b5402302702131eb-1582877606.txt
-rw-r--r-- 1 root root 32 Feb 28 16:14 cmc-console-user-sync-015ce30b116ce86058fa6ab4fea4ac63-1582877668.txt
-rw-r--r-- 1 root root 32 Feb 28 16:15 cmc-console-user-sync-c10e87f39873512a16727e17f57456a5-1582877730.txt
-rw-r--r-- 1 root root 32 Feb 28 16:16 cmc-console-user-sync-f53df1a8d46108afc8ae9eeb3f0e1f0e-1582877791.txt

[root@16fa59fcd4e0 logs]# ls -ltr
total 144
-rw-r--r-- 1 root root 8845 Feb 28 15:43 app.log
-rw-r--r-- 1 root root   32 Feb 28 15:44 cmc-console-user-sync-015ce30b116ce86058fa6ab4fea4ac63-1582875846.txt
-rw-r--r-- 1 root root   32 Feb 28 15:45 cmc-console-user-sync-f53df1a8d46108afc8ae9eeb3f0e1f0e-1582875908.txt
-rw-r--r-- 1 root root   32 Feb 28 15:46 cmc-console-user-sync-c10e87f39873512a16727e17f57456a5-1582875969.txt
-rw-r--r-- 1 root root   32 Feb 28 15:47 cmc-console-user-sync-f53df1a8d46108afc8ae9eeb3f0e1f0e-1582876031.txt
-rw-r--r-- 1 root root   32 Feb 28 15:48 cmc-console-user-sync-015ce30b116ce86058fa6ab4fea4ac63-1582876093.txt
-rw-r--r-- 1 root root   32 Feb 28 15:49 cmc-console-user-sync-c10e87f39873512a16727e17f57456a5-1582876155.txt
-rw-r--r-- 1 root root   32 Feb 28 15:50 cmc-console-user-sync-f53df1a8d46108afc8ae9eeb3f0e1f0e-1582876218.txt
-rw-r--r-- 1 root root   32 Feb 28 15:51 cmc-console-user-sync-4fd58ceba1fbc537b5402302702131eb-1582876279.txt
-rw-r--r-- 1 root root   32 Feb 28 15:52 cmc-console-user-sync-015ce30b116ce86058fa6ab4fea4ac63-1582876341.txt
-rw-r--r-- 1 root root   32 Feb 28 15:53 cmc-console-user-sync-c10e87f39873512a16727e17f57456a5-1582876403.txt
-rw-r--r-- 1 root root   32 Feb 28 15:54 cmc-console-user-sync-f53df1a8d46108afc8ae9eeb3f0e1f0e-1582876465.txt
-rw-r--r-- 1 root root   32 Feb 28 15:55 cmc-console-user-sync-4fd58ceba1fbc537b5402302702131eb-1582876526.txt
-rw-r--r-- 1 root root   32 Feb 28 15:56 cmc-console-user-sync-015ce30b116ce86058fa6ab4fea4ac63-1582876588.txt
-rw-r--r-- 1 root root   32 Feb 28 15:57 cmc-console-user-sync-c10e87f39873512a16727e17f57456a5-1582876650.txt
-rw-r--r-- 1 root root   32 Feb 28 15:58 cmc-console-user-sync-f53df1a8d46108afc8ae9eeb3f0e1f0e-1582876712.txt
-rw-r--r-- 1 root root   32 Feb 28 15:59 cmc-console-user-sync-4fd58ceba1fbc537b5402302702131eb-1582876773.txt
-rw-r--r-- 1 root root   32 Feb 28 16:00 cmc-console-user-sync-015ce30b116ce86058fa6ab4fea4ac63-1582876835.txt
-rw-r--r-- 1 root root   32 Feb 28 16:01 cmc-console-user-sync-c10e87f39873512a16727e17f57456a5-1582876897.txt
-rw-r--r-- 1 root root   32 Feb 28 16:02 cmc-console-user-sync-f53df1a8d46108afc8ae9eeb3f0e1f0e-1582876959.txt
-rw-r--r-- 1 root root   32 Feb 28 16:03 cmc-console-user-sync-4fd58ceba1fbc537b5402302702131eb-1582877020.txt
-rw-r--r-- 1 root root   32 Feb 28 16:04 cmc-console-user-sync-015ce30b116ce86058fa6ab4fea4ac63-1582877082.txt
-rw-r--r-- 1 root root   32 Feb 28 16:05 cmc-console-user-sync-c10e87f39873512a16727e17f57456a5-1582877144.txt
-rw-r--r-- 1 root root   32 Feb 28 16:06 cmc-console-user-sync-f53df1a8d46108afc8ae9eeb3f0e1f0e-1582877206.txt
-rw-r--r-- 1 root root   32 Feb 28 16:07 cmc-console-user-sync-4fd58ceba1fbc537b5402302702131eb-1582877268.txt
-rw-r--r-- 1 root root   32 Feb 28 16:08 cmc-console-user-sync-015ce30b116ce86058fa6ab4fea4ac63-1582877330.txt
-rw-r--r-- 1 root root   32 Feb 28 16:09 cmc-console-user-sync-c10e87f39873512a16727e17f57456a5-1582877392.txt
-rw-r--r-- 1 root root   32 Feb 28 16:10 cmc-console-user-sync-f53df1a8d46108afc8ae9eeb3f0e1f0e-1582877454.txt
-rw-r--r-- 1 root root   32 Feb 28 16:11 cmc-console-user-sync-4fd58ceba1fbc537b5402302702131eb-1582877515.txt
-rw-r--r-- 1 root root   32 Feb 28 16:12 cmc-console-user-sync-015ce30b116ce86058fa6ab4fea4ac63-1582877577.txt
-rw-r--r-- 1 root root   32 Feb 28 16:13 cmc-console-user-sync-c10e87f39873512a16727e17f57456a5-1582877639.txt
-rw-r--r-- 1 root root   32 Feb 28 16:15 cmc-console-user-sync-f53df1a8d46108afc8ae9eeb3f0e1f0e-1582877701.txt
-rw-r--r-- 1 root root   32 Feb 28 16:16 cmc-console-user-sync-4fd58ceba1fbc537b5402302702131eb-1582877763.txt
-rw-r--r-- 1 root root   32 Feb 28 16:17 cmc-console-user-sync-015ce30b116ce86058fa6ab4fea4ac63-1582877825.txt

14、在 bash sleep 60 秒 的情况下,很难出现并发锁定的情况。或者增加更多的容器,或者提升执行命令行的频率。设置 bash sleep 10 秒。已经出现并发锁定(防止同一个租户下的用户同步同时在多个容器中执行)的情况。当部署为集群时,已经可以保证同一个租户下的用户同步,在某一时间段,仅在 1 个容器中执行。理论上的计算公式,一个租户的同步时间间隔为:4 / 3 * 10 = 13,结果单位为秒。符合设计预期。总结:部署的容器数量不要超过租户的数量,以防止并发锁定的情况过于频繁。如图5

图5

[root@0f5a081a481a logs]# ls -ltr
total 320
-rw-r--r-- 1 root root 8845 Mar  2 09:50 app.log
-rw-r--r-- 1 root root   32 Mar  2 09:50 cmc-console-user-sync-4fd58ceba1fbc537b5402302702131eb-1583113813.txt
-rw-r--r-- 1 root root   32 Mar  2 09:50 cmc-console-user-sync-f53df1a8d46108afc8ae9eeb3f0e1f0e-1583113825.txt
-rw-r--r-- 1 root root   32 Mar  2 09:50 cmc-console-user-sync-015ce30b116ce86058fa6ab4fea4ac63-1583113836.txt
-rw-r--r-- 1 root root   32 Mar  2 09:50 cmc-console-user-sync-4fd58ceba1fbc537b5402302702131eb-1583113848.txt
-rw-r--r-- 1 root root   32 Mar  2 09:51 cmc-console-user-sync-c10e87f39873512a16727e17f57456a5-1583113860.txt
-rw-r--r-- 1 root root   32 Mar  2 09:51 cmc-console-user-sync-f53df1a8d46108afc8ae9eeb3f0e1f0e-1583113871.txt
-rw-r--r-- 1 root root   32 Mar  2 09:51 cmc-console-user-sync-015ce30b116ce86058fa6ab4fea4ac63-1583113883.txt
-rw-r--r-- 1 root root   32 Mar  2 09:51 cmc-console-user-sync-4fd58ceba1fbc537b5402302702131eb-1583113894.txt
-rw-r--r-- 1 root root   32 Mar  2 09:51 cmc-console-user-sync-c10e87f39873512a16727e17f57456a5-1583113906.txt
-rw-r--r-- 1 root root   32 Mar  2 09:51 cmc-console-user-sync-f53df1a8d46108afc8ae9eeb3f0e1f0e-1583113917.txt
-rw-r--r-- 1 root root   32 Mar  2 09:52 cmc-console-user-sync-015ce30b116ce86058fa6ab4fea4ac63-1583113929.txt
-rw-r--r-- 1 root root   32 Mar  2 09:52 cmc-console-user-sync-4fd58ceba1fbc537b5402302702131eb-1583113940.txt
-rw-r--r-- 1 root root   32 Mar  2 09:52 cmc-console-user-sync-c10e87f39873512a16727e17f57456a5-1583113952.txt
-rw-r--r-- 1 root root   32 Mar  2 09:52 cmc-console-user-sync-f53df1a8d46108afc8ae9eeb3f0e1f0e-1583113964.txt
-rw-r--r-- 1 root root   32 Mar  2 09:52 cmc-console-user-sync-015ce30b116ce86058fa6ab4fea4ac63-1583113975.txt
-rw-r--r-- 1 root root   32 Mar  2 09:53 cmc-console-user-sync-4fd58ceba1fbc537b5402302702131eb-1583113987.txt
-rw-r--r-- 1 root root   32 Mar  2 09:53 cmc-console-user-sync-c10e87f39873512a16727e17f57456a5-1583113998.txt
-rw-r--r-- 1 root root   32 Mar  2 09:53 cmc-console-user-sync-f53df1a8d46108afc8ae9eeb3f0e1f0e-1583114010.txt
-rw-r--r-- 1 root root   32 Mar  2 09:53 cmc-console-user-sync-015ce30b116ce86058fa6ab4fea4ac63-1583114021.txt
-rw-r--r-- 1 root root   32 Mar  2 09:53 cmc-console-user-sync-4fd58ceba1fbc537b5402302702131eb-1583114033.txt
-rw-r--r-- 1 root root   32 Mar  2 09:54 cmc-console-user-sync-c10e87f39873512a16727e17f57456a5-1583114044.txt
-rw-r--r-- 1 root root   32 Mar  2 09:54 cmc-console-user-sync-f53df1a8d46108afc8ae9eeb3f0e1f0e-1583114056.txt
-rw-r--r-- 1 root root   32 Mar  2 09:54 cmc-console-user-sync-015ce30b116ce86058fa6ab4fea4ac63-1583114067.txt
-rw-r--r-- 1 root root   32 Mar  2 09:54 cmc-console-user-sync-4fd58ceba1fbc537b5402302702131eb-1583114079.txt
-rw-r--r-- 1 root root   32 Mar  2 09:54 cmc-console-user-sync-c10e87f39873512a16727e17f57456a5-1583114091.txt
-rw-r--r-- 1 root root   32 Mar  2 09:55 cmc-console-user-sync-f53df1a8d46108afc8ae9eeb3f0e1f0e-1583114102.txt
-rw-r--r-- 1 root root   32 Mar  2 09:55 cmc-console-user-sync-015ce30b116ce86058fa6ab4fea4ac63-1583114114.txt
-rw-r--r-- 1 root root   32 Mar  2 09:55 cmc-console-user-sync-4fd58ceba1fbc537b5402302702131eb-1583114125.txt
-rw-r--r-- 1 root root   32 Mar  2 09:55 cmc-console-user-sync-c10e87f39873512a16727e17f57456a5-1583114137.txt
-rw-r--r-- 1 root root   32 Mar  2 09:55 cmc-console-user-sync-f53df1a8d46108afc8ae9eeb3f0e1f0e-1583114148.txt
-rw-r--r-- 1 root root   32 Mar  2 09:56 cmc-console-user-sync-015ce30b116ce86058fa6ab4fea4ac63-1583114160.txt
-rw-r--r-- 1 root root   32 Mar  2 09:56 cmc-console-user-sync-c10e87f39873512a16727e17f57456a5-1583114171.txt
-rw-r--r-- 1 root root   32 Mar  2 09:56 cmc-console-user-sync-is-lock-exist-c10e87f39873512a16727e17f57456a5-1583114171.txt
-rw-r--r-- 1 root root   32 Mar  2 09:56 cmc-console-user-sync-015ce30b116ce86058fa6ab4fea4ac63-1583114171.txt
-rw-r--r-- 1 root root   32 Mar  2 09:56 cmc-console-user-sync-f53df1a8d46108afc8ae9eeb3f0e1f0e-1583114183.txt
-rw-r--r-- 1 root root   32 Mar  2 09:56 cmc-console-user-sync-is-lock-exist-f53df1a8d46108afc8ae9eeb3f0e1f0e-1583114183.txt
-rw-r--r-- 1 root root   32 Mar  2 09:56 cmc-console-user-sync-c10e87f39873512a16727e17f57456a5-1583114183.txt
-rw-r--r-- 1 root root   32 Mar  2 09:56 cmc-console-user-sync-4fd58ceba1fbc537b5402302702131eb-1583114194.txt
-rw-r--r-- 1 root root   32 Mar  2 09:56 cmc-console-user-sync-015ce30b116ce86058fa6ab4fea4ac63-1583114206.txt
-rw-r--r-- 1 root root   32 Mar  2 09:56 cmc-console-user-sync-f53df1a8d46108afc8ae9eeb3f0e1f0e-1583114218.txt
-rw-r--r-- 1 root root   32 Mar  2 09:57 cmc-console-user-sync-4fd58ceba1fbc537b5402302702131eb-1583114228.txt
-rw-r--r-- 1 root root   32 Mar  2 09:57 cmc-console-user-sync-c10e87f39873512a16727e17f57456a5-1583114240.txt
-rw-r--r-- 1 root root   32 Mar  2 09:57 cmc-console-user-sync-f53df1a8d46108afc8ae9eeb3f0e1f0e-1583114252.txt
-rw-r--r-- 1 root root   32 Mar  2 09:57 cmc-console-user-sync-015ce30b116ce86058fa6ab4fea4ac63-1583114263.txt
-rw-r--r-- 1 root root   32 Mar  2 09:57 cmc-console-user-sync-4fd58ceba1fbc537b5402302702131eb-1583114275.txt
-rw-r--r-- 1 root root   32 Mar  2 09:58 cmc-console-user-sync-c10e87f39873512a16727e17f57456a5-1583114286.txt
-rw-r--r-- 1 root root   32 Mar  2 09:58 cmc-console-user-sync-f53df1a8d46108afc8ae9eeb3f0e1f0e-1583114298.txt
-rw-r--r-- 1 root root   32 Mar  2 09:58 cmc-console-user-sync-015ce30b116ce86058fa6ab4fea4ac63-1583114309.txt
-rw-r--r-- 1 root root   32 Mar  2 09:58 cmc-console-user-sync-4fd58ceba1fbc537b5402302702131eb-1583114321.txt
-rw-r--r-- 1 root root   32 Mar  2 09:58 cmc-console-user-sync-c10e87f39873512a16727e17f57456a5-1583114332.txt
-rw-r--r-- 1 root root   32 Mar  2 09:59 cmc-console-user-sync-f53df1a8d46108afc8ae9eeb3f0e1f0e-1583114344.txt
-rw-r--r-- 1 root root   32 Mar  2 09:59 cmc-console-user-sync-4fd58ceba1fbc537b5402302702131eb-1583114355.txt
-rw-r--r-- 1 root root   32 Mar  2 09:59 cmc-console-user-sync-is-lock-exist-4fd58ceba1fbc537b5402302702131eb-1583114355.txt
-rw-r--r-- 1 root root   32 Mar  2 09:59 cmc-console-user-sync-015ce30b116ce86058fa6ab4fea4ac63-1583114355.txt
-rw-r--r-- 1 root root   32 Mar  2 09:59 cmc-console-user-sync-c10e87f39873512a16727e17f57456a5-1583114367.txt
-rw-r--r-- 1 root root   32 Mar  2 09:59 cmc-console-user-sync-is-lock-exist-c10e87f39873512a16727e17f57456a5-1583114367.txt
-rw-r--r-- 1 root root   32 Mar  2 09:59 cmc-console-user-sync-4fd58ceba1fbc537b5402302702131eb-1583114367.txt
-rw-r--r-- 1 root root   32 Mar  2 09:59 cmc-console-user-sync-f53df1a8d46108afc8ae9eeb3f0e1f0e-1583114379.txt
-rw-r--r-- 1 root root   32 Mar  2 09:59 cmc-console-user-sync-015ce30b116ce86058fa6ab4fea4ac63-1583114390.txt
-rw-r--r-- 1 root root   32 Mar  2 10:00 cmc-console-user-sync-4fd58ceba1fbc537b5402302702131eb-1583114402.txt
-rw-r--r-- 1 root root   32 Mar  2 10:00 cmc-console-user-sync-f53df1a8d46108afc8ae9eeb3f0e1f0e-1583114413.txt
-rw-r--r-- 1 root root   32 Mar  2 10:00 cmc-console-user-sync-c10e87f39873512a16727e17f57456a5-1583114425.txt
-rw-r--r-- 1 root root   32 Mar  2 10:00 cmc-console-user-sync-015ce30b116ce86058fa6ab4fea4ac63-1583114436.txt
-rw-r--r-- 1 root root   32 Mar  2 10:00 cmc-console-user-sync-4fd58ceba1fbc537b5402302702131eb-1583114448.txt
-rw-r--r-- 1 root root   32 Mar  2 10:01 cmc-console-user-sync-f53df1a8d46108afc8ae9eeb3f0e1f0e-1583114460.txt
-rw-r--r-- 1 root root   32 Mar  2 10:01 cmc-console-user-sync-015ce30b116ce86058fa6ab4fea4ac63-1583114471.txt
-rw-r--r-- 1 root root   32 Mar  2 10:01 cmc-console-user-sync-4fd58ceba1fbc537b5402302702131eb-1583114483.txt
-rw-r--r-- 1 root root   32 Mar  2 10:01 cmc-console-user-sync-f53df1a8d46108afc8ae9eeb3f0e1f0e-1583114494.txt
-rw-r--r-- 1 root root   32 Mar  2 10:01 cmc-console-user-sync-c10e87f39873512a16727e17f57456a5-1583114506.txt
-rw-r--r-- 1 root root   32 Mar  2 10:01 cmc-console-user-sync-4fd58ceba1fbc537b5402302702131eb-1583114517.txt
-rw-r--r-- 1 root root   32 Mar  2 10:02 cmc-console-user-sync-f53df1a8d46108afc8ae9eeb3f0e1f0e-1583114529.txt
-rw-r--r-- 1 root root   32 Mar  2 10:02 cmc-console-user-sync-015ce30b116ce86058fa6ab4fea4ac63-1583114540.txt
-rw-r--r-- 1 root root   32 Mar  2 10:02 cmc-console-user-sync-c10e87f39873512a16727e17f57456a5-1583114552.txt
-rw-r--r-- 1 root root   32 Mar  2 10:02 cmc-console-user-sync-f53df1a8d46108afc8ae9eeb3f0e1f0e-1583114563.txt
-rw-r--r-- 1 root root   32 Mar  2 10:02 cmc-console-user-sync-4fd58ceba1fbc537b5402302702131eb-1583114575.txt
-rw-r--r-- 1 root root   32 Mar  2 10:03 cmc-console-user-sync-015ce30b116ce86058fa6ab4fea4ac63-1583114587.txt
-rw-r--r-- 1 root root   32 Mar  2 10:03 cmc-console-user-sync-c10e87f39873512a16727e17f57456a5-1583114598.txt

[root@88f433b87315 logs]# ls -ltr
total 344
-rw-r--r-- 1 root root 9092 Mar  2 09:49 app.log
-rw-r--r-- 1 root root   32 Mar  2 09:49 cmc-console-user-sync-4fd58ceba1fbc537b5402302702131eb-1583113764.txt
-rw-r--r-- 1 root root   32 Mar  2 09:49 cmc-console-user-sync-c10e87f39873512a16727e17f57456a5-1583113776.txt
-rw-r--r-- 1 root root   32 Mar  2 09:49 cmc-console-user-sync-f53df1a8d46108afc8ae9eeb3f0e1f0e-1583113788.txt
-rw-r--r-- 1 root root   32 Mar  2 09:49 cmc-console-user-sync-c10e87f39873512a16727e17f57456a5-1583113799.txt
-rw-r--r-- 1 root root   32 Mar  2 09:50 cmc-console-user-sync-f53df1a8d46108afc8ae9eeb3f0e1f0e-1583113811.txt
-rw-r--r-- 1 root root   32 Mar  2 09:50 cmc-console-user-sync-015ce30b116ce86058fa6ab4fea4ac63-1583113823.txt
-rw-r--r-- 1 root root   32 Mar  2 09:50 cmc-console-user-sync-4fd58ceba1fbc537b5402302702131eb-1583113834.txt
-rw-r--r-- 1 root root   32 Mar  2 09:50 cmc-console-user-sync-c10e87f39873512a16727e17f57456a5-1583113846.txt
-rw-r--r-- 1 root root   32 Mar  2 09:50 cmc-console-user-sync-f53df1a8d46108afc8ae9eeb3f0e1f0e-1583113857.txt
-rw-r--r-- 1 root root   32 Mar  2 09:51 cmc-console-user-sync-015ce30b116ce86058fa6ab4fea4ac63-1583113869.txt
-rw-r--r-- 1 root root   32 Mar  2 09:51 cmc-console-user-sync-4fd58ceba1fbc537b5402302702131eb-1583113881.txt
-rw-r--r-- 1 root root   32 Mar  2 09:51 cmc-console-user-sync-c10e87f39873512a16727e17f57456a5-1583113892.txt
-rw-r--r-- 1 root root   32 Mar  2 09:51 cmc-console-user-sync-f53df1a8d46108afc8ae9eeb3f0e1f0e-1583113904.txt
-rw-r--r-- 1 root root   32 Mar  2 09:51 cmc-console-user-sync-015ce30b116ce86058fa6ab4fea4ac63-1583113915.txt
-rw-r--r-- 1 root root   32 Mar  2 09:52 cmc-console-user-sync-4fd58ceba1fbc537b5402302702131eb-1583113927.txt
-rw-r--r-- 1 root root   32 Mar  2 09:52 cmc-console-user-sync-c10e87f39873512a16727e17f57456a5-1583113939.txt
-rw-r--r-- 1 root root   32 Mar  2 09:52 cmc-console-user-sync-f53df1a8d46108afc8ae9eeb3f0e1f0e-1583113950.txt
-rw-r--r-- 1 root root   32 Mar  2 09:52 cmc-console-user-sync-015ce30b116ce86058fa6ab4fea4ac63-1583113962.txt
-rw-r--r-- 1 root root   32 Mar  2 09:52 cmc-console-user-sync-4fd58ceba1fbc537b5402302702131eb-1583113973.txt
-rw-r--r-- 1 root root   32 Mar  2 09:53 cmc-console-user-sync-c10e87f39873512a16727e17f57456a5-1583113985.txt
-rw-r--r-- 1 root root   32 Mar  2 09:53 cmc-console-user-sync-f53df1a8d46108afc8ae9eeb3f0e1f0e-1583113997.txt
-rw-r--r-- 1 root root   32 Mar  2 09:53 cmc-console-user-sync-015ce30b116ce86058fa6ab4fea4ac63-1583114008.txt
-rw-r--r-- 1 root root   32 Mar  2 09:53 cmc-console-user-sync-4fd58ceba1fbc537b5402302702131eb-1583114020.txt
-rw-r--r-- 1 root root   32 Mar  2 09:53 cmc-console-user-sync-c10e87f39873512a16727e17f57456a5-1583114032.txt
-rw-r--r-- 1 root root   32 Mar  2 09:54 cmc-console-user-sync-f53df1a8d46108afc8ae9eeb3f0e1f0e-1583114043.txt
-rw-r--r-- 1 root root   32 Mar  2 09:54 cmc-console-user-sync-015ce30b116ce86058fa6ab4fea4ac63-1583114055.txt
-rw-r--r-- 1 root root   32 Mar  2 09:54 cmc-console-user-sync-4fd58ceba1fbc537b5402302702131eb-1583114066.txt
-rw-r--r-- 1 root root   32 Mar  2 09:54 cmc-console-user-sync-c10e87f39873512a16727e17f57456a5-1583114078.txt
-rw-r--r-- 1 root root   32 Mar  2 09:54 cmc-console-user-sync-f53df1a8d46108afc8ae9eeb3f0e1f0e-1583114090.txt
-rw-r--r-- 1 root root   32 Mar  2 09:55 cmc-console-user-sync-015ce30b116ce86058fa6ab4fea4ac63-1583114101.txt
-rw-r--r-- 1 root root   32 Mar  2 09:55 cmc-console-user-sync-4fd58ceba1fbc537b5402302702131eb-1583114113.txt
-rw-r--r-- 1 root root   32 Mar  2 09:55 cmc-console-user-sync-c10e87f39873512a16727e17f57456a5-1583114125.txt
-rw-r--r-- 1 root root   32 Mar  2 09:55 cmc-console-user-sync-f53df1a8d46108afc8ae9eeb3f0e1f0e-1583114136.txt
-rw-r--r-- 1 root root   32 Mar  2 09:55 cmc-console-user-sync-015ce30b116ce86058fa6ab4fea4ac63-1583114148.txt
-rw-r--r-- 1 root root   32 Mar  2 09:55 cmc-console-user-sync-4fd58ceba1fbc537b5402302702131eb-1583114159.txt
-rw-r--r-- 1 root root   32 Mar  2 09:56 cmc-console-user-sync-c10e87f39873512a16727e17f57456a5-1583114171.txt
-rw-r--r-- 1 root root   32 Mar  2 09:56 cmc-console-user-sync-f53df1a8d46108afc8ae9eeb3f0e1f0e-1583114183.txt
-rw-r--r-- 1 root root   32 Mar  2 09:56 cmc-console-user-sync-4fd58ceba1fbc537b5402302702131eb-1583114195.txt
-rw-r--r-- 1 root root   32 Mar  2 09:56 cmc-console-user-sync-is-lock-exist-4fd58ceba1fbc537b5402302702131eb-1583114195.txt
-rw-r--r-- 1 root root   32 Mar  2 09:56 cmc-console-user-sync-c10e87f39873512a16727e17f57456a5-1583114195.txt
-rw-r--r-- 1 root root   32 Mar  2 09:56 cmc-console-user-sync-015ce30b116ce86058fa6ab4fea4ac63-1583114206.txt
-rw-r--r-- 1 root root   32 Mar  2 09:56 cmc-console-user-sync-is-lock-exist-015ce30b116ce86058fa6ab4fea4ac63-1583114206.txt
-rw-r--r-- 1 root root   32 Mar  2 09:56 cmc-console-user-sync-c10e87f39873512a16727e17f57456a5-1583114206.txt
-rw-r--r-- 1 root root   32 Mar  2 09:56 cmc-console-user-sync-015ce30b116ce86058fa6ab4fea4ac63-1583114218.txt
-rw-r--r-- 1 root root   32 Mar  2 09:57 cmc-console-user-sync-015ce30b116ce86058fa6ab4fea4ac63-1583114229.txt
-rw-r--r-- 1 root root   32 Mar  2 09:57 cmc-console-user-sync-4fd58ceba1fbc537b5402302702131eb-1583114241.txt
-rw-r--r-- 1 root root   32 Mar  2 09:57 cmc-console-user-sync-c10e87f39873512a16727e17f57456a5-1583114253.txt
-rw-r--r-- 1 root root   32 Mar  2 09:57 cmc-console-user-sync-f53df1a8d46108afc8ae9eeb3f0e1f0e-1583114264.txt
-rw-r--r-- 1 root root   32 Mar  2 09:57 cmc-console-user-sync-015ce30b116ce86058fa6ab4fea4ac63-1583114276.txt
-rw-r--r-- 1 root root   32 Mar  2 09:58 cmc-console-user-sync-4fd58ceba1fbc537b5402302702131eb-1583114288.txt
-rw-r--r-- 1 root root   32 Mar  2 09:58 cmc-console-user-sync-c10e87f39873512a16727e17f57456a5-1583114299.txt
-rw-r--r-- 1 root root   32 Mar  2 09:58 cmc-console-user-sync-f53df1a8d46108afc8ae9eeb3f0e1f0e-1583114311.txt
-rw-r--r-- 1 root root   32 Mar  2 09:58 cmc-console-user-sync-015ce30b116ce86058fa6ab4fea4ac63-1583114322.txt
-rw-r--r-- 1 root root   32 Mar  2 09:58 cmc-console-user-sync-4fd58ceba1fbc537b5402302702131eb-1583114334.txt
-rw-r--r-- 1 root root   32 Mar  2 09:59 cmc-console-user-sync-c10e87f39873512a16727e17f57456a5-1583114346.txt
-rw-r--r-- 1 root root   32 Mar  2 09:59 cmc-console-user-sync-f53df1a8d46108afc8ae9eeb3f0e1f0e-1583114357.txt
-rw-r--r-- 1 root root   32 Mar  2 09:59 cmc-console-user-sync-015ce30b116ce86058fa6ab4fea4ac63-1583114369.txt
-rw-r--r-- 1 root root   32 Mar  2 09:59 cmc-console-user-sync-4fd58ceba1fbc537b5402302702131eb-1583114380.txt
-rw-r--r-- 1 root root   32 Mar  2 09:59 cmc-console-user-sync-f53df1a8d46108afc8ae9eeb3f0e1f0e-1583114392.txt
-rw-r--r-- 1 root root   32 Mar  2 10:00 cmc-console-user-sync-c10e87f39873512a16727e17f57456a5-1583114404.txt
-rw-r--r-- 1 root root   32 Mar  2 10:00 cmc-console-user-sync-015ce30b116ce86058fa6ab4fea4ac63-1583114415.txt
-rw-r--r-- 1 root root   32 Mar  2 10:00 cmc-console-user-sync-4fd58ceba1fbc537b5402302702131eb-1583114427.txt
-rw-r--r-- 1 root root   32 Mar  2 10:00 cmc-console-user-sync-f53df1a8d46108afc8ae9eeb3f0e1f0e-1583114439.txt
-rw-r--r-- 1 root root   32 Mar  2 10:00 cmc-console-user-sync-c10e87f39873512a16727e17f57456a5-1583114450.txt
-rw-r--r-- 1 root root   32 Mar  2 10:01 cmc-console-user-sync-c10e87f39873512a16727e17f57456a5-1583114462.txt
-rw-r--r-- 1 root root   32 Mar  2 10:01 cmc-console-user-sync-f53df1a8d46108afc8ae9eeb3f0e1f0e-1583114474.txt
-rw-r--r-- 1 root root   32 Mar  2 10:01 cmc-console-user-sync-is-lock-exist-f53df1a8d46108afc8ae9eeb3f0e1f0e-1583114474.txt
-rw-r--r-- 1 root root   32 Mar  2 10:01 cmc-console-user-sync-c10e87f39873512a16727e17f57456a5-1583114474.txt
-rw-r--r-- 1 root root   32 Mar  2 10:01 cmc-console-user-sync-015ce30b116ce86058fa6ab4fea4ac63-1583114485.txt
-rw-r--r-- 1 root root   32 Mar  2 10:01 cmc-console-user-sync-is-lock-exist-015ce30b116ce86058fa6ab4fea4ac63-1583114485.txt
-rw-r--r-- 1 root root   32 Mar  2 10:01 cmc-console-user-sync-c10e87f39873512a16727e17f57456a5-1583114485.txt
-rw-r--r-- 1 root root   32 Mar  2 10:01 cmc-console-user-sync-4fd58ceba1fbc537b5402302702131eb-1583114497.txt
-rw-r--r-- 1 root root   32 Mar  2 10:01 cmc-console-user-sync-is-lock-exist-4fd58ceba1fbc537b5402302702131eb-1583114497.txt
-rw-r--r-- 1 root root   32 Mar  2 10:01 cmc-console-user-sync-015ce30b116ce86058fa6ab4fea4ac63-1583114497.txt
-rw-r--r-- 1 root root   32 Mar  2 10:01 cmc-console-user-sync-f53df1a8d46108afc8ae9eeb3f0e1f0e-1583114509.txt
-rw-r--r-- 1 root root   32 Mar  2 10:02 cmc-console-user-sync-c10e87f39873512a16727e17f57456a5-1583114520.txt
-rw-r--r-- 1 root root   32 Mar  2 10:02 cmc-console-user-sync-4fd58ceba1fbc537b5402302702131eb-1583114532.txt
-rw-r--r-- 1 root root   32 Mar  2 10:02 cmc-console-user-sync-f53df1a8d46108afc8ae9eeb3f0e1f0e-1583114543.txt
-rw-r--r-- 1 root root   32 Mar  2 10:02 cmc-console-user-sync-015ce30b116ce86058fa6ab4fea4ac63-1583114555.txt
-rw-r--r-- 1 root root   32 Mar  2 10:02 cmc-console-user-sync-c10e87f39873512a16727e17f57456a5-1583114567.txt
-rw-r--r-- 1 root root   32 Mar  2 10:02 cmc-console-user-sync-f53df1a8d46108afc8ae9eeb3f0e1f0e-1583114578.txt
-rw-r--r-- 1 root root   32 Mar  2 10:03 cmc-console-user-sync-4fd58ceba1fbc537b5402302702131eb-1583114590.txt
-rw-r--r-- 1 root root   32 Mar  2 10:03 cmc-console-user-sync-015ce30b116ce86058fa6ab4fea4ac63-1583114601.txt

[root@c2e084f1424c logs]# ls -ltr
total 308
-rw-r--r-- 1 root root 8845 Mar  2 09:49 app.log
-rw-r--r-- 1 root root   32 Mar  2 09:49 cmc-console-user-sync-015ce30b116ce86058fa6ab4fea4ac63-1583113778.txt
-rw-r--r-- 1 root root   32 Mar  2 09:49 cmc-console-user-sync-4fd58ceba1fbc537b5402302702131eb-1583113790.txt
-rw-r--r-- 1 root root   32 Mar  2 09:50 cmc-console-user-sync-015ce30b116ce86058fa6ab4fea4ac63-1583113802.txt
-rw-r--r-- 1 root root   32 Mar  2 09:50 cmc-console-user-sync-c10e87f39873512a16727e17f57456a5-1583113814.txt
-rw-r--r-- 1 root root   32 Mar  2 09:50 cmc-console-user-sync-c10e87f39873512a16727e17f57456a5-1583113825.txt
-rw-r--r-- 1 root root   32 Mar  2 09:50 cmc-console-user-sync-f53df1a8d46108afc8ae9eeb3f0e1f0e-1583113837.txt
-rw-r--r-- 1 root root   32 Mar  2 09:50 cmc-console-user-sync-015ce30b116ce86058fa6ab4fea4ac63-1583113849.txt
-rw-r--r-- 1 root root   32 Mar  2 09:51 cmc-console-user-sync-4fd58ceba1fbc537b5402302702131eb-1583113861.txt
-rw-r--r-- 1 root root   32 Mar  2 09:51 cmc-console-user-sync-c10e87f39873512a16727e17f57456a5-1583113873.txt
-rw-r--r-- 1 root root   32 Mar  2 09:51 cmc-console-user-sync-f53df1a8d46108afc8ae9eeb3f0e1f0e-1583113884.txt
-rw-r--r-- 1 root root   32 Mar  2 09:51 cmc-console-user-sync-015ce30b116ce86058fa6ab4fea4ac63-1583113896.txt
-rw-r--r-- 1 root root   32 Mar  2 09:51 cmc-console-user-sync-4fd58ceba1fbc537b5402302702131eb-1583113908.txt
-rw-r--r-- 1 root root   32 Mar  2 09:52 cmc-console-user-sync-c10e87f39873512a16727e17f57456a5-1583113920.txt
-rw-r--r-- 1 root root   32 Mar  2 09:52 cmc-console-user-sync-f53df1a8d46108afc8ae9eeb3f0e1f0e-1583113932.txt
-rw-r--r-- 1 root root   32 Mar  2 09:52 cmc-console-user-sync-015ce30b116ce86058fa6ab4fea4ac63-1583113943.txt
-rw-r--r-- 1 root root   32 Mar  2 09:52 cmc-console-user-sync-4fd58ceba1fbc537b5402302702131eb-1583113955.txt
-rw-r--r-- 1 root root   32 Mar  2 09:52 cmc-console-user-sync-c10e87f39873512a16727e17f57456a5-1583113967.txt
-rw-r--r-- 1 root root   32 Mar  2 09:52 cmc-console-user-sync-f53df1a8d46108afc8ae9eeb3f0e1f0e-1583113979.txt
-rw-r--r-- 1 root root   32 Mar  2 09:53 cmc-console-user-sync-015ce30b116ce86058fa6ab4fea4ac63-1583113990.txt
-rw-r--r-- 1 root root   32 Mar  2 09:53 cmc-console-user-sync-4fd58ceba1fbc537b5402302702131eb-1583114002.txt
-rw-r--r-- 1 root root   32 Mar  2 09:53 cmc-console-user-sync-c10e87f39873512a16727e17f57456a5-1583114014.txt
-rw-r--r-- 1 root root   32 Mar  2 09:53 cmc-console-user-sync-f53df1a8d46108afc8ae9eeb3f0e1f0e-1583114026.txt
-rw-r--r-- 1 root root   32 Mar  2 09:53 cmc-console-user-sync-015ce30b116ce86058fa6ab4fea4ac63-1583114037.txt
-rw-r--r-- 1 root root   32 Mar  2 09:54 cmc-console-user-sync-4fd58ceba1fbc537b5402302702131eb-1583114049.txt
-rw-r--r-- 1 root root   32 Mar  2 09:54 cmc-console-user-sync-c10e87f39873512a16727e17f57456a5-1583114061.txt
-rw-r--r-- 1 root root   32 Mar  2 09:54 cmc-console-user-sync-f53df1a8d46108afc8ae9eeb3f0e1f0e-1583114073.txt
-rw-r--r-- 1 root root   32 Mar  2 09:54 cmc-console-user-sync-015ce30b116ce86058fa6ab4fea4ac63-1583114085.txt
-rw-r--r-- 1 root root   32 Mar  2 09:54 cmc-console-user-sync-4fd58ceba1fbc537b5402302702131eb-1583114096.txt
-rw-r--r-- 1 root root   32 Mar  2 09:55 cmc-console-user-sync-c10e87f39873512a16727e17f57456a5-1583114108.txt
-rw-r--r-- 1 root root   32 Mar  2 09:55 cmc-console-user-sync-f53df1a8d46108afc8ae9eeb3f0e1f0e-1583114120.txt
-rw-r--r-- 1 root root   32 Mar  2 09:55 cmc-console-user-sync-015ce30b116ce86058fa6ab4fea4ac63-1583114132.txt
-rw-r--r-- 1 root root   32 Mar  2 09:55 cmc-console-user-sync-4fd58ceba1fbc537b5402302702131eb-1583114143.txt
-rw-r--r-- 1 root root   32 Mar  2 09:55 cmc-console-user-sync-c10e87f39873512a16727e17f57456a5-1583114155.txt
-rw-r--r-- 1 root root   32 Mar  2 09:56 cmc-console-user-sync-f53df1a8d46108afc8ae9eeb3f0e1f0e-1583114167.txt
-rw-r--r-- 1 root root   32 Mar  2 09:56 cmc-console-user-sync-4fd58ceba1fbc537b5402302702131eb-1583114179.txt
-rw-r--r-- 1 root root   32 Mar  2 09:56 cmc-console-user-sync-015ce30b116ce86058fa6ab4fea4ac63-1583114191.txt
-rw-r--r-- 1 root root   32 Mar  2 09:56 cmc-console-user-sync-f53df1a8d46108afc8ae9eeb3f0e1f0e-1583114202.txt
-rw-r--r-- 1 root root   32 Mar  2 09:56 cmc-console-user-sync-4fd58ceba1fbc537b5402302702131eb-1583114214.txt
-rw-r--r-- 1 root root   32 Mar  2 09:57 cmc-console-user-sync-c10e87f39873512a16727e17f57456a5-1583114226.txt
-rw-r--r-- 1 root root   32 Mar  2 09:57 cmc-console-user-sync-f53df1a8d46108afc8ae9eeb3f0e1f0e-1583114238.txt
-rw-r--r-- 1 root root   32 Mar  2 09:57 cmc-console-user-sync-015ce30b116ce86058fa6ab4fea4ac63-1583114249.txt
-rw-r--r-- 1 root root   32 Mar  2 09:57 cmc-console-user-sync-4fd58ceba1fbc537b5402302702131eb-1583114261.txt
-rw-r--r-- 1 root root   32 Mar  2 09:57 cmc-console-user-sync-c10e87f39873512a16727e17f57456a5-1583114273.txt
-rw-r--r-- 1 root root   32 Mar  2 09:58 cmc-console-user-sync-f53df1a8d46108afc8ae9eeb3f0e1f0e-1583114285.txt
-rw-r--r-- 1 root root   32 Mar  2 09:58 cmc-console-user-sync-015ce30b116ce86058fa6ab4fea4ac63-1583114296.txt
-rw-r--r-- 1 root root   32 Mar  2 09:58 cmc-console-user-sync-4fd58ceba1fbc537b5402302702131eb-1583114308.txt
-rw-r--r-- 1 root root   32 Mar  2 09:58 cmc-console-user-sync-c10e87f39873512a16727e17f57456a5-1583114320.txt
-rw-r--r-- 1 root root   32 Mar  2 09:58 cmc-console-user-sync-f53df1a8d46108afc8ae9eeb3f0e1f0e-1583114332.txt
-rw-r--r-- 1 root root   32 Mar  2 09:59 cmc-console-user-sync-015ce30b116ce86058fa6ab4fea4ac63-1583114343.txt
-rw-r--r-- 1 root root   32 Mar  2 09:59 cmc-console-user-sync-4fd58ceba1fbc537b5402302702131eb-1583114355.txt
-rw-r--r-- 1 root root   32 Mar  2 09:59 cmc-console-user-sync-c10e87f39873512a16727e17f57456a5-1583114367.txt
-rw-r--r-- 1 root root   32 Mar  2 09:59 cmc-console-user-sync-c10e87f39873512a16727e17f57456a5-1583114379.txt
-rw-r--r-- 1 root root   32 Mar  2 09:59 cmc-console-user-sync-c10e87f39873512a16727e17f57456a5-1583114391.txt
-rw-r--r-- 1 root root   32 Mar  2 10:00 cmc-console-user-sync-015ce30b116ce86058fa6ab4fea4ac63-1583114403.txt
-rw-r--r-- 1 root root   32 Mar  2 10:00 cmc-console-user-sync-4fd58ceba1fbc537b5402302702131eb-1583114414.txt
-rw-r--r-- 1 root root   32 Mar  2 10:00 cmc-console-user-sync-f53df1a8d46108afc8ae9eeb3f0e1f0e-1583114426.txt
-rw-r--r-- 1 root root   32 Mar  2 10:00 cmc-console-user-sync-c10e87f39873512a16727e17f57456a5-1583114438.txt
-rw-r--r-- 1 root root   32 Mar  2 10:00 cmc-console-user-sync-015ce30b116ce86058fa6ab4fea4ac63-1583114450.txt
-rw-r--r-- 1 root root   32 Mar  2 10:01 cmc-console-user-sync-4fd58ceba1fbc537b5402302702131eb-1583114461.txt
-rw-r--r-- 1 root root   32 Mar  2 10:01 cmc-console-user-sync-f53df1a8d46108afc8ae9eeb3f0e1f0e-1583114473.txt
-rw-r--r-- 1 root root   32 Mar  2 10:01 cmc-console-user-sync-015ce30b116ce86058fa6ab4fea4ac63-1583114485.txt
-rw-r--r-- 1 root root   32 Mar  2 10:01 cmc-console-user-sync-4fd58ceba1fbc537b5402302702131eb-1583114497.txt
-rw-r--r-- 1 root root   32 Mar  2 10:01 cmc-console-user-sync-f53df1a8d46108afc8ae9eeb3f0e1f0e-1583114509.txt
-rw-r--r-- 1 root root   32 Mar  2 10:01 cmc-console-user-sync-is-lock-exist-f53df1a8d46108afc8ae9eeb3f0e1f0e-1583114509.txt
-rw-r--r-- 1 root root   32 Mar  2 10:01 cmc-console-user-sync-015ce30b116ce86058fa6ab4fea4ac63-1583114509.txt
-rw-r--r-- 1 root root   32 Mar  2 10:02 cmc-console-user-sync-015ce30b116ce86058fa6ab4fea4ac63-1583114520.txt
-rw-r--r-- 1 root root   32 Mar  2 10:02 cmc-console-user-sync-c10e87f39873512a16727e17f57456a5-1583114532.txt
-rw-r--r-- 1 root root   32 Mar  2 10:02 cmc-console-user-sync-4fd58ceba1fbc537b5402302702131eb-1583114544.txt
-rw-r--r-- 1 root root   32 Mar  2 10:02 cmc-console-user-sync-4fd58ceba1fbc537b5402302702131eb-1583114556.txt
-rw-r--r-- 1 root root   32 Mar  2 10:02 cmc-console-user-sync-015ce30b116ce86058fa6ab4fea4ac63-1583114568.txt
-rw-r--r-- 1 root root   32 Mar  2 10:03 cmc-console-user-sync-c10e87f39873512a16727e17f57456a5-1583114580.txt
-rw-r--r-- 1 root root   32 Mar  2 10:03 cmc-console-user-sync-f53df1a8d46108afc8ae9eeb3f0e1f0e-1583114592.txt
-rw-r--r-- 1 root root   32 Mar  2 10:03 cmc-console-user-sync-4fd58ceba1fbc537b5402302702131eb-1583114603.txt
-rw-r--r-- 1 root root   32 Mar  2 10:03 cmc-console-user-sync-015ce30b116ce86058fa6ab4fea4ac63-1583114615.txt

15、总结:
(1) 理论上的计算公式,一个租户的同步时间间隔为:租户数量 / 容器数量 * 60,结果单位为秒。
(2) 部署的容器数量不要超过租户的数量,以防止并发锁定的情况过于频繁,且必要性不大(当容器数量等于租户数量时,同步时间间隔为:60 秒)。

永夜