写点什么

微服务化解决文库下载业务问题实践

作者:百度Geek说
  • 2022 年 7 月 26 日
  • 本文字数:6075 字

    阅读完需:约 20 分钟


作者 | 文库主站研发


导读:百度文库是百度发布的供网友在线分享文档的平台,文档下载服务是文库的一个基础服务。经过多年发展,当前文库的下载服务在业务方面和技术方面都面临着众多困扰。因此我们把传统的面向接口开发的下载服务改造成一个独立的微服务系统,重点做了两件事情:第一件是定义微服务调用的协议和规则,并且将下载系统独立部署独立上线,让微服务垂直划分,这是微服务化的基础。第二件事是基于我们的下载业务,开发一个满足业务要求,适合业务发展的下载技术框架。从而化繁为简,解决了复杂的业务问题。

全文 6212 字,预计阅读时间 16 分钟。

01 引子

百度文库是百度发布的供网友在线分享文档的平台,用户可以在百度文库下载各类文档。百度文库平台自 2009 年 11 月上线以来,经过十余年发展,文档数量和每日下载次数都是一个非常巨大的数字。内容是文库的核心资产,文档下载服务是文库的一个基础服务,用户来到文库的主要目的之一就是获取内容,所以下载服务的体验是重中之重,同时下载功能也是各种黑产们薅羊毛的重要目标。



当前文库是一个大的分布式单体架构,文库所有的代码都作为系统整体一起部署,尽管文库拥有多个应用多个实例以实现分布式扩展,从根本上讲,所有的代码都打包在一个程序中。文档下载功能是这个巨大单体架构的一部分,在文库十余年的发展历史中,这个巨大的单体架构,和下载服务都在各自持续地生长扩展。

02 面临的问题现象

在业务方面和技术方面,当前文库的下载服务都面临着众多困扰:


  • 体验差客诉多:


每天很多用户反馈文档下载失败,或者提示成功但是找不到文档,或者下载了但是内容不全等等。为此文库值班客服和研发人员不得不每天花费大量精力处理下载的客诉问题,疲于奔命。


场景


文库用户甲:我刚买了个文档,下载提示成功,但是文档没有下载下来。


文库客服乙:非常抱歉给你带来不好的体验……


文库研发:因为这个文档作者设置了『不可下载』,这个是历史遗留问题,我修一下。


文库用户甲:这个文档我下载下来只有个摘要,内容不全。快给我退款!


文库客服乙:好的收到,我马上联系研发处理……


文库研发:因为这个是第三方文档,文库调用第三方服务失败,只能用我们这边保存的摘要兜底。


  • 下载方式众多,开发维护成本高:


文库经过多年发展,下载方式也变得多种多样。PC、WAP、小程序、APP 端各自有各自的下载接口,还有一些跟第三方合作相关的下载服务。如用户可以在一些高校的内网下载百度文库的文档,也可以在文库下载来自第三方平台的文档内容。还有其它变种形式的下载服务,比如文档转存网盘,文档发送邮件附件等。这些下载服务大同小异,基本逻辑差不多,也有各自的特殊逻辑。


场景


文库产品:我们今天谈了一家新的内容合作方,把我们文档在他们 app 展示,要提供下载功能。


文库研发:好的,我开发一个下载接口。


文库产品:我们跟 XX 高校合作,部分文档可以免费给他们使用。


文库研发:那我单独给他们提供个下载服务吧。


  • 反黑产反作弊成本高:


因为下载方式多种多样,文库历史上开发的各种反作弊逻辑也是多种多样,没有做到策略统一。想增加一个新的反作弊策略非常困难。网上出现过一些转卖或者下载文档的工具,咱们很难定位他们是通过什么方式获取到文库的内容。


场景


文库产品:最近又发现一个文库下载工具,能下载我们的文档。我们的下载服务需要接入 XX 反作弊平台


文库研发:好的,我可能需要改十几个接口……


针对这些下载 case,尽管文库研发人员每天都有整理跟进,并归类,但实际效果并不明显,问题没有得到彻底解决。因为各种下载 case 纷繁复杂,难以尽述。随着新项目上线,也不断会有新问题出现,难以收敛。


原因


经过分析,问题有以下几方面原因:


  • 【业务层面】


文库的下载服务属于基础服务,业务场景比较固定。所有的下载服务和下载方式,其逻辑和步骤都大同小异。下载服务都有安全检查,业务逻辑检查,获取下载地址,后置处理(扣特权,计数)等流程,可以求出最大公约数,抽象通用步骤。而当前是面向接口开发,应对新需求时,很多情况下优先保证短平快上线,没有选择长期方案。


  • 【架构层面】


当前下载服务和文库整体耦合在一起,互相影响。且下载的代码比较分散,基本都是面向接口编程,没有统一的基础模块和通用规范,也缺乏统一的异常处理机制和错误码信息。


  • 【监控 &运营层面】


因为文库的下载接口多且分散,没有整体的日志收集机制,并且缺乏统一的监控、数据报表、运营工具等。导致不能及时发现问题进行预警,出问题往往要等到用户反馈才发现。即使用户反馈了问题,因为缺乏有效的运营工具,客服人员无法自行定位问题,必须依赖研发的协助。


  • 【用户体验方面】


下载行为的提示文案不准确,有些下载错误缺乏友好的提示文案,下载流程体验不佳。


归根结底是因为当前的技术设计,难以满足业务的快速发展变化的需要。最开始文档下载只是一个简单的接口或者功能,随着业务变迁变得越来越复杂。实际上已经成了一套复杂的系统,而且是在不断扩展不断升级的系统。但是技术架构上多年未改进,已经落后于业务发展。


所以要彻底解决它们,不能着眼于当前的问题,需要重新思考我们的业务场景,建立适合当前业务发展的技术体系,用技术来系统性的解决当前问题,我们文库研发开出的药方是"微服务化"。


03 解决问题之道


微服务是围绕业务领域建模的、可独立部署的服务。微服务利用网络相互通信,把耦合在一起的整体架构拆分开,微服务是一种面向服务的架构,基于多个微服务间的协作,为解决复杂系统问题供了一个不错的选择。


在定义下载服务的边界时,如何保证耦合和内聚之间的平衡非常重要。耦合和内聚这两个概念相互关联,如果一个架构具备高内聚、低耦合的特性,则该架构是稳定的。单体结构的问题在于,单体对内聚和耦合而言是相反的(低内聚,高耦合)。单体架构不是倾向于内聚,而是趋向于把变化的代码组织在一起,衍生出各种不相关的代码逻辑并将其拼贴在一起。


回到我们具体的下载业务上来,如果下载服务内部各子模块功能越单一,内聚性就越高,其独立性就越强。同样的一个服务应该做好自己业务范围内的功能就可以了,不需要面面俱到,不然难以维护。同时,下载服务跟其它服务之间联系越少,耦合性就越低,各服务之间的相对独立性就越强。这就是我们进行下载微服务改造的基本指导思想。基于这个思想,我们重新思考了文库业务,让这个新的微服务架构能实现我们下载业务需要的功能和特性。我们设计实现了一个下载的微服务系统,这个系统独立部署独立上线,跟其它业务隔离开来。同时把文库的下载接口全部纳入到这个框架里来,这个框架重点做了以下几方面的事情:


  1. 抽象下载过程,定义通用的下载步骤。使研发人员以后开发下载服务时都按照此框架的规范来开发。

  2. 抽象下载行为,对每一次下载过程,都记录详细的数据,包括下载现场数据,下载过程和下载结果。为监控和运营工具提供基础数据。

  3. 开发组件化的功能模块,方便复用和统一修改升级。

  4. 制定统一的异常处理机制,规范错误码,自动收集下载日志,优化提示文案。

  5. 产出配套的运营分析工具,方便客服人员定位问题,同时增加下载数据实时监控。



综上,文库研发,PM,QA 联合启动了一个下载服务优化项目,来解决困扰我们已久的下载问题。

04 方案实现

要把传统的面向接口开发的系统改造成一个独立的微服务系统,我们重点做了两件事情。第一件是定义微服务调用的协议和规则,并且将下载系统独立部署独立上线,让微服务垂直划分,这是微服务化的基础。第二件事是基于我们的下载业务,开发一个满足业务要求,适合业务发展的下载技术框架。


4.1 微服务基础


在单体式应用中,各个模块之间的调用是通过本机的编程语言的方法或者函数来实现的。但是一个基于微服务的分布式应用是运行在多台机器上的,分布式的部署,服务之间通过网络通信来互相调用。文库下载服务借助百度公司内部通用的 BNS 来实现流量调度和负载均衡。BNS 服务全称:Baidu Naming Service,即百度名字服务,用于满足服务间交互中常见的的资源定位、模块间 ip 白名单 授权验证、负载均衡以及其他任何依赖于这些信息的开发和运维需求。我们给下载服务的所有线上机器加入到我们创建的 BNS。客户端或者其它内部业务要访问下载服务,都通过这个 BNS 来访问,并且支持就近访问对应的机房,避免远程网络通信带来的额外开销。



远程访问和重试机制依赖百度内部的 RAL 客户端。RAL 支持多种交互协议和数据打包格式,具有高性能和低成本的特点,并且可以与 BNS 完美配合。调用方使用 RAL 与下载服务进行交互,而不需要关注数据格式处理与协议交互的过程,是一种简单可依赖的方式。


部署采用百度云平台,也是非常成熟可靠的解决方案。实现跟文库主业务隔离,开发和上线互不影响。


4.2 业务框架


基于对文库业务的思考,在新的文库下载微服务系统中,我们开发了一个适合文库业务场景,满足长期发展变化的下载服务框架。这个框架做了以下五部分的工作:


  • 抽象下载过程,下载流程规范化


首先我们对下载过程做了抽象。做抽象是为了简化业务,对下载行为进⾏总结和过滤,去除掉我们不需要的步骤或者多余的信息,分析其本质。经过分析,文库的下载业务逻辑通常比较固定,可以把下载过程分为下载行为初始化,安全检查,权限检查,主业务操作,后置操作几部分。



其中,下载行为初始化操作会预先收集本次下载行为的基础数据,即不包含隐私数据的用户信息,以及当前下载场景信息,以及被下载的对象信息等,后面各个环节,以及写下载日志都会用到这些信息。第二步为安全检查,本步骤会做频率限制,防止某些 IP 或者账号短时间内进行大量下载,以及会有一些封禁检查,反作弊检查等。第三步为权限检查。一般下载服务都不是免费的,需要用户有一定的积分,或者已经购买过内容,或者用户身份具有下载权限等。第四步为下载主业务逻辑,本步骤根据被下载的内容 id,获取到内容保存的地址,把内容返回给用户的设备。最后一步为后置操作,一般用于扣减用户积分或者金钱消耗,以及记录用户数据等。


最终我们的框架里定义了以上几个下载步骤,各个下载的具体业务实现里,按照预先定义好的步骤进行开发,保证了下载流程规范化,也为整个下载系统的日志收集,监控等打下了基础。


示意代码:



  • 描述下载行为


一次下载行为实际上是一个网络请求,我们需要收集这类网络请求的一些附加信息,结合当前的应用场景,再加上这个行为的结果,就能很好的描述出这一次下载过程。下载行为通常是由终端用户发起,在一个特定的场景下,下载特定的内容对象。用户通常具有账号 id,终端 IP 地址,所用浏览器或者设备信息,以及 cookie 信息等特征(不包含用户隐私信息)。而场景包括用户在哪个产品线发起下载行为,这个下载行为的来源,下载时间,下载过程结果等。内容对象包括用户所下载的内容的类型,以及这个内容的 id,内容标题等一些能描述此内容的信息。



收集这些数据能大大提升我们快速定位问题的能力,以及用于监控下载服务的稳定性和业务数据变化。


  • 提供组件化的基础模块


软件架构设计本身就是一个复杂的事情,但其实业界已经演化出了行之有效的应对办法,那就是“通过组件化完成关注点的分离从而降低局部复杂度”。其实现在我们用的无论是容器、中间件、消息、数据库等,在某种意义上都是组件化的产物,这样的好处是在不同的系统里可以复用,正是这样的复用才成就了今天的互联网级架构。


对应到我们具体的下载服务,反作弊,频率限制,权限校验,身份检查等都可以开发成通用的、组件化的功能模块,以提升开发效率,降低多次迭代引入的风险。


当前代码中下载和转存分别覆盖 pc, wap, 小程序三端以及其它合作接口,  各接口代码中部分逻辑是各自为战, 代码冗余度高, 逻辑不一致。为解决这种问题, 将部分基础服务抽象剥离,  业务解耦,开发成独立的,可复用的基础模块,就能做到一改全改。对于一些有特殊要求的逻辑,例如 PC 限制一天最多下载 M 次,WAP 限制一天最多下载 N 次,通常用传参或者配置文件来控制,保留灵活性。



举个例子,我们对不同类型的文档,检查权益也不一样。单篇付费类文档,下载过程中需要检查用户是否已购买这篇文档,而 VIP 免费文档只需检查用户是否 VIP 身份就行,对于 VIP 专享文档,除了要检查 VIP 身份,还要检查用户是否有足够的下载特权。这些权益规则也不是固定不变的,随着业务发展会有新的权益规则。将权益控制封装成一个独立的模块后,以后新增和修改权益都会变得很容易。



  • 规范异常处理机制,自动收集日志


任何服务都需要考虑到异常处理机制,这里说的异常,既包括程序逻辑上的异常,也包括业务逻辑上的异常。程序异常诸如指针错误,变量类型错误之类,一般程序员都会考虑到这些。实际业务执行过程中也会有异常,比如非 VIP 身份却去下载 VIP 专属文档,一天下载超限了还在继续下载文档。很多开发者通常会不太在意这种业务异常,但是对于我们下载服务来说非常重要,我们需要记录这种异常来帮助我们分析异常 case。


以前下载过程抛异常很混乱,没有规范的错误提示。框架重新定义错误码,规范异常处理。增加异常收集机制。如果框架检测到任一步骤返回错误码,则自动记录下载日志,如果下载过程最终成功,同样会记录下载日志。最后格式化返回错误信息以及数据。



在这个下载微服务框架下,业务开发只需要实现各自下载服务的 Downloader 就行,其它的步骤,比如流程定义,行为收集,日志落库工作都是框架自动完成。主框架会监测执行过程,执行结束时,获取下载行为数据,并且自动写入日志。


日志的存储上,因为下载量巨大,不方便用单表存储,采取按月分表的方式,因为这个数据只是用于运营定位问题,以及一些数据分析,不作为在线业务使用,所以这种分表方式完全满足我们的使用场景。


  • 产出配套的运营分析工具和监控


新的下载服务框架收集数据的同时,也产出了相应的运营工具,报表,监控等。如果有客诉,客服人员可以自行查询下载记录,看是否用户下载是否成功,以及相应的日志。并且增加实时监控,如果是十分钟内下载成功数/失败数有剧烈波动,就会向研发人员发送报警信息。


04 效果和总结

经过如上改造后,再遇到类似的问题,应对方式已经完全不一样了:


场景


文库用户甲:我刚买了个文档,下载提示失败请重试,是什么原因?


文库客服乙:您好,经查询你下载的文档属于第三方问题,下载当时对方服务稳定性有问题,我们已经联系合作伙伴解决,现在好了。


文库产品:我们发现 XX 黑产工具再用非法手段下载我们的内容,需要在所有下载入口针对此工具紧急上线一个封禁策略


文库研发:好的,一改全改。


我们通过微服务化改造,从巨大单体式应用分离出独立的下载业务架构,从而化繁为简,解决了复杂性问题。新的微服务架构也不局限于原来的技术栈,开发者可以自由选择新的开发技术,提供 API 服务。新的下载服务只关注于自己的业务功能,并且独立开发部署,解决了耦合问题。实际上,进行微服务化改造之后,下载业务的迭代无需依赖其它服务,迭代速度也快了很多,最终业务问题也得到了收敛。


——————END——————


推荐阅读:


百度APP Android包体积优化实践(二)Dex行号优化


百度APP Android包体积优化实践(一)总览


百度APP iOS端内存优化实践-大块内存监控方案


百家号基于AE的视频渲染技术探索


百度工程师教你玩转设计模式(观察者模式)


Linux透明大页机制在云上大规模集群实践介绍

发布于: 1 小时前阅读数: 12
用户头像

百度Geek说

关注

百度官方技术账号 2021.01.22 加入

关注我们,带你了解更多百度技术干货。

评论

发布
暂无评论
微服务化解决文库下载业务问题实践_Java_百度Geek说_InfoQ写作社区