• 新闻客户端信息流重构系统设计——从零到一


    项目背景

    接口调研及排期规划

    新闻客户端旧版imcp接口相对较多,重构时采用先开发核心接口(访问量大的接口),再逐步迁移非核心接口。

    接口优先级排序

    根据线上一段时间的access日志进行统计,按访问量排序结果如下:

    awk '{print $7}' access.log|sort | uniq -c |sort -nk 1 -r|head -30

      10354 HEAD /httpchk.php HTTP/1.1
       6518 GET /client_base_config
       5213 GET /ifengNewsRedidentPush
       3713 GET /userBehaviorRecord
       3081 GET /ifengNewsKeepLiveConfig
       2944 GET /commentEmojiConfig HTTP/1.1
       2064 POST /nlist
       1939 GET /thirdoutput
       1680 POST /getNewsRecList HTTP/1.1
       1190 POST /NewRelatedDocs
       1001 GET /api_phoenixtv_details
        944 GET /weishare_token_api
        826 POST /getNewsDocs
        734 GET /NewRelativeVideoList
        658 GET /its HTTP/1.1
        634 GET /client_search_hotword
        619 GET /android_api
        568 GET /totalProfile
        513 GET /api_vampire_article_detail
        481 HEAD /runaccess/android
        385 GET /phoneweather
        361 GET /ipadtestdoc
        327 GET /userInterestTag
        324 GET /interest_select
        321 GET /ClientNews
        313 GET /share_zmt_home
        302 GET /api_wemedia_index
        263 GET /client_share_bottom_number
        238 GET /burroughsnews
        209 GET /shareNews
        176 GET /nlist
        167 POST /irecommendList
        165 POST /NewRelativeVideoList
        142 GET /ipLocation
        128 GET /favicon.ico HTTP/1.1
        126 GET /newH5Weather
        118 GET /awakeIfengNews HTTP/1.1
        115 GET /client_share_news_recommend_test
        111 POST /ClientNews
        110 POST /ipadtestdoc HTTP/1.1
    

    根据业务分析,决定一期先重构如下几个接口

    信息流列表:nlist 
    客户端正文:getNewsDocs/ipadtestdoc
    文章相关:NewRelatedDocs
    视频正文:api_phoenixtv_details
    频道配置:totalProfile
    视频相关:NewRelativeVideoList
    客户端配置:client_base_config
    专题正文:TopicApiForCmpp
    

    人员安排,小组分工

    人员安排

    架构师 1人

    业务开发组 2人
    调优监控组 1人
    Swoole服务组 3人

    配置组 1人

    业务组分工

    接口范围

    列表1个 正文3个

    人员分工

    列表:王爵, 正文:沐杉

    排期细化

    (共20天,排除其他工作任务的情况下):
    1.流程梳理期(共同参与) 列表3天+正文3天 共6天
    1.1细化接口流程
    1.2梳理与主业务无关的逻辑
    1.3梳理核实旧版兼容逻辑
    2.数据源协议对接,确认一次请求和二次请求 2天
    3.模块拆分设计,版本兼容设计 ,广告模块置入 4天
    4.输入输出参数优化,返回字段优化 1天
    5.接口开发,框架优化,测试联调 7天
    6.相关文档整理-待定

    日常开发制度

    为保证开发进度和开发质量,不出现重大问题,在重构期间制定了相关制定

    编码规范

    新加入的团队成员初次合作,为保证编码风格统一,统一制定了php编码规范以及IDE的代码格式化规范,以统一成员间的代码风格

    周例会

    为了跟踪和梳理遇到的问题,每周固定时间(大概半小时)统一组织周例会,会上把大家遇到的问题和需要讨论的技术方案进行陈述,大家共同分析或者由架构师给出方案建议。同时制定出下一周(下一阶段)的重点工作。

    周例会中要强调“里程碑”,即一段时间后的重要节点,需要实现哪些重要的功能模块,给大家信心。

    code review

    定期组织代码审查会,把大家的代码进行审查和讨论,确定优化方案

    问题梳理

    在重构初期,由于之前的业务逻辑复杂,因此自己花了很长时间去理解之前的逻辑,并画了流程图。

    同时,在开发新接口前,还需要向以前负责接口的同事咨询一些业务细节,如下是当时询问的问题列表:

    本次开发基于全新版本,不用考虑历史逻辑,但要考虑后续的版本差异
    
    整理头条和正文、其他信息流哪些是公用的模块
    
    梳理流程中无关的(不做的)流程(如行为上报,push弹窗等)  梳理后去掉
    
    梳理哪些逻辑是废弃逻辑,本次重构不再考虑的  列表,找相关人核实,去掉
    
    整理接口输入参数,返回参数中已废弃的参数,优化输入输出结构   整理 优化
    
    广告模块后期置入,基于原类微调改造 可暂不考虑优化
    
    新数据源的提供形式,分类,数据结构(旧版mongo中的数据被什么所取代?)  直接对接数据层
    
    确认哪些流程和数据是数据源直接可以提供的,保证一次请求可能拿到全部数据  直接对接数据层,或者二次发起请求
    
    ======信息流其它疑问及建议======
    程序设计建议:
    5.推荐接口调用前,编辑数据固定条数 待定
    3个引擎,1个头条,1个上下拉,1个非上下拉 ok
    nlist page和action模式 二选一 ok
    chconfig从rule中抽离,在配置中心单独成立配置项 ok
    rule里的下拉不展示(downNoShow)独立成频道规则 ok
    cmpppod 和 oldpod获取数据源后的格式化操作(统一字段名) 整合成一套 ok
    aqudata中,2320中ids中有list的和没有的  统一成一个 ok
    小列表递归,返回值list ok
    专题正文会调信息流里的 getTopicTreeNews 专题套专题 ok
    接口鉴权保持目前一致 ok
    参数预处理处理频道号 ok
    财经头条接口下线,自媒体商品信息不需要考虑 ok
    cmpp数据全部获取过来后,解析实例化的性能开销,实际只用1条数据
    
    重构设计疑问(需要确认的):
    列表频道id参数多个变1个,由配置中心统一处理,(旧版id顺序会影响返回结果)
    上划每次都返回置顶top数据,为了兼容客户端展示用,改版后建议客户端修改
    列表整体结构是否缓存(内容组织层)
    评论数的获取能否在数据源一次返回,不做二次请求
    推荐接口的focus内容 能否单独给,不混在流数据中
    rule里推荐位的多个数据源 是否能整合成1个? 
    列表是否支持其他平台的调用?(非客户端,其他平台可能传其他参数,单独返回定制化数据)
    推荐接口返回的specialParam字段能否给成json对象
    推荐返回的other字段中,包含很多信息,如缩略图,给过来是半成品,能否推荐直接对应到相应的字段中去,或者能否推荐只给id,其他信息我们自己查
    焦点图先从推荐给的数据中分离,再合并到顶部,再分离出列表?其他频道?
    视频信息调接口,二次请求,能否一次获得
    财经频道数据源接口特殊接入 后续能否不支持列表外的数据源
    横排内的子元素都要走外面的大流程 循环执行 性能低(取自媒体号)
    正文文章id位数能否统一
    正文的数据源能否统一
    为方便开发和测试,需要一份包含目前会用到的全部模块组成的一个信息流,全流程覆盖的,可以新旧版本对比的一套数据源(推荐位和上下拉模式各一种)。
    
    实现机制理解:
    rule里的index为第一页的pageSize,other为非首页的pageSize
    推荐数据与编辑数据的混合机制,类似广告
    二级导航rule,otherrule代表模块样式
    
    问题汇总:
    正文:
    正文内容属性整合,和广告信息,相关信息并列,格式见wiki:http://know.mnews.ifeng.com/pages/viewpage.action?pageId=15508510
    
    
    推荐:
    推荐给的docid都统一成ucms id?  x
    确认推荐给的docType在用的都有哪些列表? ok
    头条的前2屏取default数据,为何需要将pullup强转为default  ok去掉逻辑
    推荐接口的focus内容 能否单独给,不混在流数据中 可以提供
    推荐接口返回的specialParam字段能否给成json对象			保持
    推荐返回的other字段中,包含很多信息,如缩略图,给过来是半成品,能否推荐直接对应到相应的字段中去,或者能否推荐只给id,其他信息我们自己查			
    lastdoc 上报机制确认
    
    内容方面的疑问:
    1.评论数的获取能否在数据源一次返回,不做二次请求
    2.推荐位的多个数据源 是否能整合成1个
    3.列表的视频信息调接口,二次请求,能否一次获得
    4.财经频道数据源接口特殊接入 后续能否不支持列表外的数据源
    5.获取自媒体信息随列表一并给出(包括各种号信息)
    6.正文的数据源能否统一(如统一到ucms)
    7.文章的阅读数和评论数从基础数据直接给到
    8.数据接口支持批量获取
    
    关于测试需求:
    
    特殊用户群体,特殊地域情况,头条频道特殊业务逻辑验证,
    新旧接口降级切换流程 ,业务模块降级流程 业务方面暂时补充这些
    

    技术方案

    demo讨论

    由于重构项目从0开始,框架的使用和核心流程的设计需要确定一套方案。当时采用的是3个团队成员分别根据自己对项目的理解,写一套demo,之后开会介绍自己demo实现的思路和想法。

    最终通过讨论,自己设计的这套开发框架得到了认可和执行,最终的方案以此demo为基准。

    框架调研

    最初打算用php的 Laravel框架来进行开发,后来考虑到该框架较重,不适合前端API的性能,因此从Laravel框架中抽离了几个核心组件(路由,自动加载)进行了自定义框架的配置

    正式开发

    业务组和服务组基于调研后的框架进行开发

    开发模式采用 敏捷开发+螺旋模式+里程碑 的混合模式

    php7 与 swoole

    框架方案确定后,整体的业务开发方案也基本确定。模式为:

    web -> swoole -> 第三方API

    之所以在web和第三方api间加入一个swoole层,目的是:

    1. 逻辑清晰,将请求数据部分单独成立一个项目,便于管理
    2. 重复利用swoole的并发协程机制,提高请求效率
    3. 更好更专注的将监控,熔断,降级等策略应用到请求第三方接口上

    重构项目使用了php7+swoole+docker的开发模式,与之前php+fpm的模式不同

    性能调优

    随着重构功能开发的进行,性能的问题也在逐步显现出来,因此对如下几个方面进行了优化

    空跑框架

    新项目由于使用了自研框架(路由部分及自动加载部分取自了laravel的相关组件),框架本身会有一些性能损耗,因此进行了空框架的性能测试,测试输出hello world

    压力测试 ab

    对业务相对复杂的头条接口进行了ab压力测试

    第三方服务化(tars)

    针对之前的http接口,获取内容接口进行了服务化改造,新的接口使用tcp服务,效率提升了很高,响应时间控制在了几十毫秒

    批量接口

    由于web端的“批量预请求结果缓存机制”,需要将批量的请求单独组合成数组的方式发给Swoole(而不能用手动拼接逗号分隔的形式),需要Swoole端进行参数转换,转换后发送一次请求(之前的处理方式是发送多次协程来请求,效率较低)

    xhprof性能查看

    在测试环境部署了一套xhprof,可以查看调用的耗时,便于排查问题

    调试debug入口

    在url中增加debug参数,可以打印出本次请求Swoole的耗时,参数,返回值以及真实调用第三方的url,便于排查问题。

    同时,debug模式会使缓存失效,更方便跟踪过程

    客户端对接,历史遗留问题解决

    历史遗留问题主要在数据源推荐处,当时约定的一些字段有的已经废弃,有的功能重复等

    头条推荐接口优化
    1.焦点图分离,考虑与documentList同级新增字段返回
    2.others 里面的 url=http://api.iclient.ifeng.com/ipadtestdoc?aid=ucms_7oF3QfFDYTA,拆分出aid字段
    3.specialParam和ext->specialParam统一,值为json字符串
    4.编辑固定位置条数先获取,再计算size。编辑固定数据能否推荐统一出,包括列表和焦点图。
    5.确认头条推荐接口外层的字段都有 flagMap,documentList 字段
    6.if($jsonData['flagMap']['flag_jpType'] == 'jpFilter') => 丢弃编辑固定位数据,确认该逻辑是否还存在,和size参数的逻辑冲突
    7.前2次上拉,pullup改为default,推荐接口已实现?
    8.头条接口本次设计的字段调整,在其他频道推荐接口同步更新
    9.提供各模块测试数据,之前只提供了一两个测试的
    
    客户端:
    列表,正文url会变,是否有影响?ok
    新输入返回参数格式见wiki:http://know.mnews.ifeng.com/pages/viewpage.action?pageId=15508171
    列表:
    移除push弹框,登录弹框,绑定手机弹框,事件上报相关功能ok
    
    输入参数删除:ok
    installTime //安装时间 时间戳
    openNum //打开次数
    pushStatus //push 打开状态 1/0
    closeWinType //关闭的窗口类型
    closeWinTime //关闭的窗口时间
    closeWinCount //关闭的窗口次数
    
    返回参数确认:
    syRetainOldNew		下拉后清除历史数据,保留条数
    downHideNews		下拉后自动向上隐藏新闻条数
    downHideFocusNav	下拉是否需要隐藏置顶(1:是,0:否)
    这3个参数功能是否还需要? 如果需要,能否放到chConfig中返回?ok
    
    其他逻辑:
    列表上划每次都返回置顶top数据,为了兼容客户端展示用,改版后建议客户端修改 待定??
    财经频道指数模块特殊处理 ok
    

    第三方对接

    统一接口,参数格式化

    客户端信息流列表需要对接推荐部门的接口,目前的现状是 推荐的头条接口,频道接口,其他小列表接口和打底接口给出的数据格式均不完全一致,因此为了保证对接的统一性,多次和推荐沟通,将全部接口统一成同一种格式,方便对接和维护。

    在过渡期swool端临时进行了数据格式的中转,目的是让web端的逻辑保持统一。

    后期推荐将只返回id,其他详细信息将通过后续的业务接口(ucms)进行获取。

    配合测试部门梳理历史业务

    有些历史业务开发同学也不是特别清楚了,需要测试部门的资深同学进行讲解和梳理,服务端进行及时的整理和细化,将废弃的逻辑大胆删除,简化。

    大家好才是真的好

    对于很多历史遗留问题,有的不光是一个部门进行优化就能起作用的,需要多个部门配合才能完成,因此需要大家开会讨论,将重构的利弊说清楚,只有大家充分理解重构的优势,才会一起配合,向着最终的方向共同前进。此时需要一定的沟通技巧,将各个业务线的负责人都统一战线,才能取得最终的胜利。

    重构的验证

    服务端API的重构,相对于客户端调用方来说,是透明的,因此,客户端只需要配合服务端做一些工作即可。

    而真正的挑战在于重构后结果的正确性。因为之前的业务逻辑相对复杂,因此最终生成的结果无法简单的进行验证,所以在开发过程中使用了各种方法尽量将最终的数据保持正确。

    数据正确性验证

    对比小模块生成的json结果

    小模块生成的json数据短小,且具有可对比性,修改起来排查也时分方便,借助于网上web端的对比工具也十分方便

    JSON对比工具
    https://www.sojson.com/jsondiff.html
    

    P.S. BeyondCompare高版本的工具中也内置了json比较的功能,但相对不是很智能且BeyondCompare工具本身对正版的验证较严格,最终放弃。

    开发json比对工具

    上面提到的web对比工具,在对比长json文档的时候会导致浏览器卡死,无法进行。因此整个信息流最终的输出结果无法直接对比,考虑到这一点,想到手工开发一个比对工具

    此工具的实现思路是,将一段json数据的每一个key按字母表顺序排序后,重新进行输出,每一个层级均按此规则实现,于是便有了下面这个递归算法:

    /**
     * json数组按key名排序
     * @param $jsonArray
     * @return array
     */
    function ksortArray($jsonArray) {
        if (!is_array($jsonArray)) {
            return $jsonArray;
        }
        foreach ($jsonArray as $key=> $value) {
            if (!is_array($value)) {
                continue;
            }
            ksort($value);
            $jsonArray[$key]=ksortArray($value);
        }
        return $jsonArray;
    }
    

    原始日志记录

    由于列表中的数据多数来自推荐,导致每一次刷新每个用户的内容是不同的,因此很难再次复现同一条数据。因此为了保证回溯问题,我在接口层面添加了一系列白名单用户,这些用户的请求原始接口的信息会记录详细的返回结果日志,方便后续的比对和验证

    由于访问量大,无法全量记录日志排查,仅能针对部分用户做此功能

    手机模拟

    在抓包工具中进行请求的中转,在实际手机中查看新接口返回的数据显示是否有缺失,测试人员也配合进行多次验证

    新旧项目同步开发

    人员沟通,代码同步

    在重构项目和原有imcp项目并行开发的阶段,由于版本迭代,新加的功能需要在两个项目中同步开发,负责开发的同学也不太一样,实现的方式也略有不同,因此需要及时沟通,同步两个项目的修改

    代码更新通知机制

    由于部分小需求及bug修改不在版本需求列表中,可能会随时修改上线,因此新项目中为保证不丢失这一部分的更改,在原有项目的上线更新列表中进行监控和通知,有文件变动及时通知到重构项目的开发,保持两端一致

    上线方案

    上线及回退方案

    重构列表上线方案

    上线时间安排:

    上线时间 上线的列表频道 量级
    2019-11-12 推荐列表,关注列表 全量
    2019-11-13 头条列表 1%

    上线方式:

    列表名称 上线方式 上线客户端版本 回退方案 回退生效时间
    推荐,关注 在cmpp后台频道管理中操作频道api配置,升version版本号 v6.7.21 cmpp后台操作回滚 用户下次启动app重新获取频道配置后
    头条 在现有头条接口(nlist)中 进行路由跳转,将原有请求转发到重构接口上 v6.7.21 去掉转发逻辑 立即生效,再次刷新头条列表即可

    逐步灰度放量

    列表逐步从1%,5%,10%到更多进行切换,方式为在原接口中做302跳转

    #region 新头条列表切换
    $whiteList=[
        '白名单用户id'
    ];
    
    $hideList=[
        '黑名单用户id'
    ];
    if ((version_compare($version, '6.7.21') >= 0 && $idstr == 'SYLB10,SYDT10')) {
        if (in_array($uid, $whiteList)||(crc32($uid) % 100 < 5&& !in_array($uid, $hideList))) {
             header("Location: https://nine.ifeng.com/headline?" . http_build_query($_REQUEST));
             exit;
        }
    }
    
    #endregion 新头条列表切换
    
    

    因为原接口中有部分post参数,之前考虑使用307跳转,但是安卓客户端不支持,因此只能使用302跳转,将post参数自动变为get参数提交到新接口中*

    列表放量历史

    时间 客户端类型 客户端名称 上线频道说明 备注
    2019-09-02 17:30 ios 专业版,探索版 “fun来了” 和 “暖新闻” 2个频道 推荐位类型频道
    2019-09-25 17:40 ios 专业版 推荐频道 推荐频道
    2019-10-11 15:56 ios 探索版 推荐频道 推荐频道
    2019-10-11 17:14 android 快头条 FUN来了 推荐位类型频道
    2019-10-16 16:38:25 ios 探索版,专业版 关注频道 推荐频道类型+特殊处理逻辑
    2019-10-16 17:16:28 ios 探索版,专业版 视频频道 推荐频道类型
    2019-10-17 17:51:39 ios 专业版 头条 头条
    2019-10-28 16:39:26 ios 探索版 头条 头条
    2019-11-12 16:42:29 ios 主线版 推荐频道,关注频道 推荐频道类型+特殊处理逻辑
    2019-11-12 16:56:34 android 主线版 推荐频道,关注频道 推荐频道类型+特殊处理逻辑
    2019-11-14 15:46:37 android/ios 主线版 头条 上线2%,v6721+,增加白名单
    2019-11-19 10:14:54 android/ios 主线版 头条 上线5%
    2019-11-22 15:29:12 android/ios 主线版 头条 上线10%
    2019-11-25 15:00 android/ios 主线版 新正文重新上线 全量
    2019-11-26 14:21:45 android/ios 主线版 头条 上线5%
    2019-11-27 09:43:49 android/ios 主线版 头条 上线15%
    2019-11-28 10:12:27 android/ios 主线版 头条 上线25%
    2019-12-02 10:29:13 android/ios 主线版 头条 上线50%
    2019-12-04 16:47:26 android 主线版 头条 安卓切换10%,ios 50%
    2019-12-06 09:17:18 ios 主线版 头条 ios 80%
    2019-12-11 11:34:18 android/ios 主线版 头条 安卓切换40%,ios 100%
    2019-12-12 16:48:46 android/ios 主线版 头条 android 40%,ios降到了 30% 版本支持6721-6741版本
    2019-12-16 14:06:24 android/ios 主线版 头条 全部60% 6721-6750
    2019-12-18 15:05:35 android/ios 主线版 头条 全部80%
    2019-12-20 10:26:01 android/ios 主线版 头条 90%
    2019-12-23 10:52:53 android/ios 主线版 头条 100% 6721-6750

    上线后问题处理

    2019-11-14 主线版第一次上线头条列表,上线1%,逐步上线后发生过一些问题

    redis依赖导致的问题

    由于最初使用redis对swool端的降级熔断进行计数等操作,在调用真正业务的api前,会依赖redis的结果进行打底验证,一旦redis发生异常则swool无法对外提供服务。

    当时排查到,熔断的逻辑可能有bug 导致进入了死循环,最终redis拒绝连接。

    回退新老接口对应

    新列表上线前,旧的头条列表中焦点图更多跳转的热点列表,全部使用了重构接口,因此一旦重构项目异常,则无法切换回备用方案。

    解决办法是在旧项目中,临时开发了一个数据格式相同的热点列表接口,一旦新项目异常,马上进行切换。

    499过多问题排查

    逐步放量后,统计监控平台发现日志中499的情况相对较多(安卓端的情况大量多于ios端),于是进行了相关排查。

    出现499的原因是由于“客户端”主动断开连接导致,但是实际情况是,服务端API面对的“客户端”不仅仅指真正的手机客户端,期间还好经历负载均衡服务器,代理服务器等的处理,因此需要协调运维的同事一起进行排查。

    最终运维给出的答复是:真正的客户端主动断开连接的可能性较大。

    于是,我们调研了客户端的相关行为,推测出如下结论:

    499的问题级可能是安卓客户端双击返回和crach相关。
    在安卓客户端的实现逻辑,在任何频道按一下back键执行下拉刷新,连续按两次back键,直接退出客户端。但是连续两次back的第一次执行下拉刷新,导致这次请求没有结束就关闭客户端了。
    另一个原因可能是crach:目前安卓客户端加载文章后存在内存存在不清除的情况,打开多篇文章,大图文章,复杂广告的文章就会引起操作系统级别的内存回收(一个启动的app安卓系统要求在200M以内运行,而我们的客户端启动后大概占140-160M),从而引发crach,这种情况多出现在正文回到列表的时候。

    有同事找了几个有499的用户验证了一下,绝大多数499为该用户的最后一条请求。并且 安卓头条499中 86.7%的为down操作,12.3%的为default

    最后调查发现,开机配置中,安卓有配置关于back键返回的动作,是导致客户端主动断开的根本原因。

    优化与完善

    重构头条上线后,基本功能正常,后续还需要进行性能的优化和完善

    目前的性能指标

    重构头条列表(nine)新版本切量100%后(6721-6750版本)

    私有云 QPS:最高270

    金山云 QPS:最高170

    总共:450 左右

    接口平均响应时间 400ms左右

    第三方接口优化

    列表依赖的主要第三方接口为推荐接口,推荐接口平均响应时间在200ms左右,因此急需优化该接口的响应时长

    需要优化的方面:

    1.删除服务端不需要的字段

    2.简化参数结构,移除字符串json格式的数据

    3.协同内容部门接口,改变推荐接口的数据结构,未来考虑只返回id,具体内容详情从内容接口获取

    公有云优化

    腾讯云的响应延迟还比较大,后续还应继续优化跟进

  • 相关阅读:
    L2-1 功夫传人 (25分)
    7-11 家庭房产(25 分)
    7-11 玩转二叉树 (25分)
    7-10 排座位 (25分)
    7-12 最长对称子串 (25分)
    7-10 树的遍历 (25分)
    STL
    Max Gcd
    水果
    Rails
  • 原文地址:https://www.cnblogs.com/dannywang/p/12092225.html
Copyright © 2020-2023  润新知