途牛原创|无线中心运营研发Redis酷实践

522 查看

Redis-简介

Redis是一个开源的使用ANSI C语言编写、支持网络、可基于内存亦可持久化的日志型、Key-Value数据库,并提供多种语言的API。从2010年3月15日起,Redis的开发工作由VMware主持。从2013年5月开始,Redis的开发由Pivotal(Redis作者目前就职)赞助。

数据类型

  • String(字符串)

  • List(列表)

  • Set(集合)

  • Sort Set(有序集合)

  • Hash(哈希)

Redis-应用场景

Redis作者antirez描述了Redis比较适合的一些应用场景,NoSQLFan简单列举在这里,供大家一览:

  • 取最新N个数据的操作

  • 排行榜应用,取TOP N操作

  • 需要精准设定过期时间的应用

  • 计数器应用

  • Uniq操作,获取某段时间所有数据排重值

  • 实时系统,反垃圾系统

  • Pub/Sub构建实时消息系统

  • 构建队列系统

  • 缓存

运营研发-场景

无线运营研发部,作为无线运营侧的兵工厂,成功打造了CMS,位置管理,权限中心,RBZ等运营支撑工具。

武器一览

  • CMS:无线运营播种机

  • RBZ:EAV模型(动态表单+属性中心+标签系统)

  • 权限中心:RBAC3模型

  • 位置管理:一切皆位置

^^^^^^^ 回到主题,下面就为大家详细介绍下,我们如何玩耍Redis。

场景包括CMS页面缓存、API限速器、页面性能分析、API状态统计、CMS智能提醒-异常线路。尤其页面性能分析、API状态统计、CMS智能提醒等应用简直X爆了,将页面和接口性能看板化、智能化,技术应用一目了然、一览无余。

场景字段的一些说明

  • 应用场景:属于哪一类应用范畴

  • 数据类型:使用的数据类型

  • 代码说明:PHP,扩展phpredis

CMS页面缓存

基于Redis的字符串数据类型,用来存储CMS静态页面数据,提高CMS相关页面访问速度,缓冲mysql的压力。

  • 数据类型:String

  • 应用场景:缓存

  • 代码:


$staticHtml = Yii::app()->redis->get($cmsCacheKey);

if (! $staticHtml || $this->clearcache) {
   $staticHtml = CmsTools::getStaticHtml($pageId, $cityCode);
   Yii::app()->redis->setex($cmsCacheKey, 3600, $staticHtml);
}

API限速器

基于Redis的字符串数据类型,用来控制API访问频率,一段时间内某一个IP针对某一个请求的访问控制官方用例

  • 数据类型:String

  • 应用场景:计数器

  • 代码:


public static function rateLimit($apiKey = null)
{
    //Redis键值
    $apiRunCountKey = Yii::app()->request->userHostAddress . '-' . $apiKey;
    //初始化接口访问频次
    if (Yii::app()->redis->get($apiRunCountKey) === false) {
        Yii::app()->redis->setex(
            $apiRunCountKey,
            self::$RateLimitTime,
            self::$RateLimitCount
        );
    }
    //获取当前可执行的频次
    $currentApiCount = Yii::app()->redis->decr($apiRunCountKey);

    if ($currentApiCount < 0) {
        Yii::log($apiRunCountKey, 'info', 'webadmin.cms.api.rate');
        return false;
    }
    return true;
}

//CMS页面-重置频率控制
return PowerApiService::rateLimit('cms-refresh-page-' . $pageId)
    && CmsTool::refreshStaticPage($pageId);

性能分析

基于Redis的有序集合数据类型,分析页面执行性能

  • 数据类型:Sort Set

  • 应用场景:排行榜

  • 代码:


//基于城市,记录PC首页生成时间
Yii::app()->redis->zAdd(
    'homepage-cache-profile',
    round($endTime - $startTime, 2),
    $this->letter
);

//汇总PC首页性能数据
Yii::app()->redis->zRange('homepage-cache-profile', 0, -1, true);

//基于页面,记录CMS页面重置时间
Yii::app()->redis->zAdd(
    'cms-refresh-page-profile',
    round($pageEndTime - $pageStartTime, 2),
    $pageId
);

//获取CMS,0-30s性能的页面
Yii::app()->redis->zRangeByScore('cms-refresh-page-profile', 0, 30);

//获取CMS,>30s性能的页面
Yii::app()->redis->zRangeByScore('cms-refresh-page-profile', 30, 900);

API状态统计

综合运用Redis数据类型,汇总API的调用,监控API的实时请求,分析超时请求

  • 数据类型:String,List,Sort Set

  • 应用场景:计数器,排行榜

  • 代码(有点长):


/**
 * Webadmin-API-Status
 */

public static $webApiList200       = 'web:api:list:200';

public static $webApiList500       = 'web:api:list:500';

public static $webApiListTimeOut   = 'web:api:list:timeout';

public static $webApiListCache     = 'web:api:list:cache';

public static $webApiListLatest    = 'web:api:List:latest';

public static function collectApiStatus(ApiStatus $apiStatus)
{
    $apiAction = array(
        'n' => $apiStatus->name, //接口名
        'p' => $apiStatus->params, //接口参数
        'c' => $apiStatus->client, //客户端
        'e' => $apiStatus->elapsed(), //响应时长
        't' => time() //时间戳
    );

    //最新请求-数据录入
    Yii::app()->redis->lPush(self::$webApiListLatest, json_encode($apiAction));
    Yii::app()->redis->ltrim(self::$webApiListLatest, 0, 29);

    //最新请求-前台渲染
    //$apiLatest = Yii::app()->redis->lGetRange(self::$webApiListLatest, 0, 29);

    //收集缓存
    if ($apiStatus->cache) {
        self::collectApiResponseCache($apiStatus->name); //zIncrBy
    }

    //收集状态
    if ($apiStatus->status == 200) {
        self::collectApiResponse200($apiStatus->name); //zIncrBy
    } else if ($apiStatus->status == 500) {
        self::collectApiResponse500($apiStatus->name); //zIncrBy
    } else {
      //
    }

    //收集超时
    if ($apiStatus->elapsed() > 2000) {
        self::collectApiResponseTimeOut($apiAction); //zIncrBy
    }
}
  • API-Status:

CMS智能提醒-异常线路

综合运用Redis数据类型,准实时汇总CMS所有楼层的线路呈现情况,精确的定位异常线路楼层,易于运营人员更好的开展工作。

  • 数据类型:String,List,Sort Set

  • 应用场景:队列,排行榜,缓存

  • 代码(有点绕):


//Redis键值
$cmsCheckPrdKey = "cms:{$pageId}:{$cityCode}"; //CMS-页面ID-预定城市

//推送CMS楼层线路信息
PowerApiService::push(
    $cmsCheckPrdKey,
    array(
        'pid'  => $pageId, //页面ID
        'code' => $cityCode, //城市Code
        'mid'  => $moduleId, //产品模块ID
        'i'    => $preRouteIds, //运营配置线路
        't'    => time() //时间戳
    )
);

/**
 * Worker-计算CMS中的异常产品
 */
public static function cmsCheckPrd($item = array())
{
    $pid  = $item['pid']; //页面ID
    $code = $item['code']; //城市Code
    $mid  = $item['mid']; //产品模块ID
    $i    = $item['i']; //待计算的线路
    $t    = $item['t']; //请求时间戳

    //时间戳,用于判断队列的实效性,此处代码省略

    //通过搜索接口,判断线路有效性  
    $solr = new PowerSolrService();
    $recommend = new ror_service_recommend();
    $recommend->ids = $i;
    $recommend->queryFields = array("productId");
    $o = $solr->recommendQueryOrigin($recommend); //有效的线路

    //数据差集=异常线路
    $diff = array_diff($i, $o);

    if ($diff) {
        //通过有序集合的特性,模块的异常线路=score
        Yii::app()->redis->zAdd($cmsCheckPrdKey, $mid, $mid . ':' . implode(',', $diff));
        //数据有效性=1day
        Yii::app()->redis->expire($cmsCheckPrdKey, 86400);
    }

    //获取CMS页面模块信息
    //Yii::app()->redis->zRange($cmsCheckPrdKey, 0, -1);

    //切割数据,获取模块对应的异常线路
    //list($moduleID, $modulePrd) = explode(':', $checkString);
}
  • 图示1 (楼层提醒):

  • CMS异常楼层统计(实时计算):

页面ID 城市Code 异常楼层
1992 上海 4
1992 广州 7
1992 成都 6
1949 北京 6

!显然,Redis的应用场景远甚于此。=)

学习指南

Redis固然很赞,切记当你手上有一把锤子的时候,看所有的东西都是钉子,理解他,用好他。