文章图片
作者 | 文库主站研发
导读:百度文库是百度发布的供网友在线分享文档的平台,文档下载服务是文库的一个基础服务。经过多年发展,当前文库的下载服务在业务方面和技术方面都面临着众多困扰。因此我们把传统的面向接口开发的下载服务改造成一个独立的微服务系统,重点做了两件事情:第一件是定义微服务调用的协议和规则,并且将下载系统独立部署独立上线,让微服务垂直划分,这是微服务化的基础。第二件事是基于我们的下载业务,开发一个满足业务要求,适合业务发展的下载技术框架。从而化繁为简,解决了复杂的业务问题。01 引子 百度文库是百度发布的供网友在线分享文档的平台,用户可以在百度文库下载各类文档。百度文库平台自2009年11月上线以来,经过十余年发展,文档数量和每日下载次数都是一个非常巨大的数字。内容是文库的核心资产,文档下载服务是文库的一个基础服务,用户来到文库的主要目的之一就是获取内容,所以下载服务的体验是重中之重,同时下载功能也是各种黑产们薅羊毛的重要目标。
全文6212字,预计阅读时间16分钟。
文章图片
当前文库是一个大的分布式单体架构,文库所有的代码都作为系统整体一起部署,尽管文库拥有多个应用多个实例以实现分布式扩展,从根本上讲,所有的代码都打包在一个程序中。文档下载功能是这个巨大单体架构的一部分,在文库十余年的发展历史中,这个巨大的单体架构,和下载服务都在各自持续地生长扩展。
02 面临的问题现象 在业务方面和技术方面,当前文库的下载服务都面临着众多困扰:
- 体验差客诉多:
场景
文库用户甲:我刚买了个文档,下载提示成功,但是文档没有下载下来。
文库客服乙:非常抱歉给你带来不好的体验……
文库研发:因为这个文档作者设置了『不可下载』,这个是历史遗留问题,我修一下。
文库用户甲:这个文档我下载下来只有个摘要,内容不全。快给我退款!
文库客服乙:好的收到,我马上联系研发处理……
文库研发:因为这个是第三方文档,文库调用第三方服务失败,只能用我们这边保存的摘要兜底。
- 下载方式众多,开发维护成本高:
场景
文库产品:我们今天谈了一家新的内容合作方,把我们文档在他们app展示,要提供下载功能。
文库研发:好的,我开发一个下载接口。
文库产品:我们跟XX高校合作,部分文档可以免费给他们使用。
文库研发:那我单独给他们提供个下载服务吧。
- 反黑产反作弊成本高:
场景
文库产品:最近又发现一个文库下载工具,能下载我们的文档。我们的下载服务需要接入XX反作弊平台
文库研发:好的,我可能需要改十几个接口……
针对这些下载case,尽管文库研发人员每天都有整理跟进,并归类,但实际效果并不明显,问题没有得到彻底解决。因为各种下载case纷繁复杂,难以尽述。随着新项目上线,也不断会有新问题出现,难以收敛。
原因
经过分析,问题有以下几方面原因:
- 【业务层面】
- 【架构层面】
- 【监控&运营层面】
- 【用户体验方面】
归根结底是因为当前的技术设计,难以满足业务的快速发展变化的需要。最开始文档下载只是一个简单的接口或者功能,随着业务变迁变得越来越复杂。实际上已经成了一套复杂的系统,而且是在不断扩展不断升级的系统。但是技术架构上多年未改进,已经落后于业务发展。
所以要彻底解决它们,不能着眼于当前的问题,需要重新思考我们的业务场景,建立适合当前业务发展的技术体系,用技术来系统性的解决当前问题,我们文库研发开出的药方是"微服务化"。
03 解决问题之道
微服务是围绕业务领域建模的、可独立部署的服务。微服务利用网络相互通信,把耦合在一起的整体架构拆分开,微服务是一种面向服务的架构,基于多个微服务间的协作,为解决复杂系统问题供了一个不错的选择。
在定义下载服务的边界时,如何保证耦合和内聚之间的平衡非常重要。耦合和内聚这两个概念相互关联,如果一个架构具备高内聚、低耦合的特性,则该架构是稳定的。单体结构的问题在于,单体对内聚和耦合而言是相反的(低内聚,高耦合)。单体架构不是倾向于内聚,而是趋向于把变化的代码组织在一起,衍生出各种不相关的代码逻辑并将其拼贴在一起。
回到我们具体的下载业务上来,如果下载服务内部各子模块功能越单一,内聚性就越高,其独立性就越强。同样的一个服务应该做好自己业务范围内的功能就可以了,不需要面面俱到,不然难以维护。同时,下载服务跟其它服务之间联系越少,耦合性就越低,各服务之间的相对独立性就越强。这就是我们进行下载微服务改造的基本指导思想。基于这个思想,我们重新思考了文库业务,让这个新的微服务架构能实现我们下载业务需要的功能和特性。我们设计实现了一个下载的微服务系统,这个系统独立部署独立上线,跟其它业务隔离开来。同时把文库的下载接口全部纳入到这个框架里来,这个框架重点做了以下几方面的事情:
- 抽象下载过程,定义通用的下载步骤。使研发人员以后开发下载服务时都按照此框架的规范来开发。
- 抽象下载行为,对每一次下载过程,都记录详细的数据,包括下载现场数据,下载过程和下载结果。为监控和运营工具提供基础数据。
- 开发组件化的功能模块,方便复用和统一修改升级。
- 制定统一的异常处理机制,规范错误码,自动收集下载日志,优化提示文案。
- 产出配套的运营分析工具,方便客服人员定位问题,同时增加下载数据实时监控。
文章图片
综上,文库研发,PM,QA联合启动了一个下载服务优化项目,来解决困扰我们已久的下载问题。
04 方案实现 要把传统的面向接口开发的系统改造成一个独立的微服务系统,我们重点做了两件事情。第一件是定义微服务调用的协议和规则,并且将下载系统独立部署独立上线,让微服务垂直划分,这是微服务化的基础。第二件事是基于我们的下载业务,开发一个满足业务要求,适合业务发展的下载技术框架。
4.1 微服务基础
在单体式应用中,各个模块之间的调用是通过本机的编程语言的方法或者函数来实现的。但是一个基于微服务的分布式应用是运行在多台机器上的,分布式的部署,服务之间通过网络通信来互相调用。文库下载服务借助百度公司内部通用的BNS来实现流量调度和负载均衡。BNS服务全称:Baidu Naming Service,即百度名字服务,用于满足服务间交互中常见的的资源定位、模块间ip白名单 授权验证、负载均衡以及其他任何依赖于这些信息的开发和运维需求。我们给下载服务的所有线上机器加入到我们创建的BNS。客户端或者其它内部业务要访问下载服务,都通过这个BNS来访问,并且支持就近访问对应的机房,避免远程网络通信带来的额外开销。
文章图片
远程访问和重试机制依赖百度内部的RAL客户端。RAL支持多种交互协议和数据打包格式,具有高性能和低成本的特点,并且可以与BNS完美配合。调用方使用 RAL与下载服务进行交互,而不需要关注数据格式处理与协议交互的过程,是一种简单可依赖的方式。
部署采用百度云平台,也是非常成熟可靠的解决方案。实现跟文库主业务隔离,开发和上线互不影响。
4.2 业务框架
基于对文库业务的思考,在新的文库下载微服务系统中,我们开发了一个适合文库业务场景,满足长期发展变化的下载服务框架。这个框架做了以下五部分的工作:
- 抽象下载过程,下载流程规范化
文章图片
其中,下载行为初始化操作会预先收集本次下载行为的基础数据,即不包含隐私数据的用户信息,以及当前下载场景信息,以及被下载的对象信息等,后面各个环节,以及写下载日志都会用到这些信息。第二步为安全检查,本步骤会做频率限制,防止某些IP或者账号短时间内进行大量下载,以及会有一些封禁检查,反作弊检查等。第三步为权限检查。一般下载服务都不是免费的,需要用户有一定的积分,或者已经购买过内容,或者用户身份具有下载权限等。第四步为下载主业务逻辑,本步骤根据被下载的内容id,获取到内容保存的地址,把内容返回给用户的设备。最后一步为后置操作,一般用于扣减用户积分或者金钱消耗,以及记录用户数据等。
最终我们的框架里定义了以上几个下载步骤,各个下载的具体业务实现里,按照预先定义好的步骤进行开发,保证了下载流程规范化,也为整个下载系统的日志收集,监控等打下了基础。
示意代码:
文章图片
- 描述下载行为
文章图片
收集这些数据能大大提升我们快速定位问题的能力,以及用于监控下载服务的稳定性和业务数据变化。
- 提供组件化的基础模块
对应到我们具体的下载服务,反作弊,频率限制,权限校验,身份检查等都可以开发成通用的、组件化的功能模块,以提升开发效率,降低多次迭代引入的风险。
当前代码中下载和转存分别覆盖pc, wap, 小程序三端以及其它合作接口,各接口代码中部分逻辑是各自为战, 代码冗余度高, 逻辑不一致。为解决这种问题, 将部分基础服务抽象剥离,业务解耦,开发成独立的,可复用的基础模块,就能做到一改全改。对于一些有特殊要求的逻辑,例如PC限制一天最多下载M次,WAP限制一天最多下载N次,通常用传参或者配置文件来控制,保留灵活性。
文章图片
举个例子,我们对不同类型的文档,检查权益也不一样。单篇付费类文档,下载过程中需要检查用户是否已购买这篇文档,而VIP免费文档只需检查用户是否VIP身份就行,对于VIP专享文档,除了要检查VIP身份,还要检查用户是否有足够的下载特权。这些权益规则也不是固定不变的,随着业务发展会有新的权益规则。将权益控制封装成一个独立的模块后,以后新增和修改权益都会变得很容易。
文章图片
- 规范异常处理机制,自动收集日志
以前下载过程抛异常很混乱,没有规范的错误提示。框架重新定义错误码,规范异常处理。增加异常收集机制。如果框架检测到任一步骤返回错误码,则自动记录下载日志,如果下载过程最终成功,同样会记录下载日志。最后格式化返回错误信息以及数据。
文章图片
在这个下载微服务框架下,业务开发只需要实现各自下载服务的Downloader就行,其它的步骤,比如流程定义,行为收集,日志落库工作都是框架自动完成。主框架会监测执行过程,执行结束时,获取下载行为数据,并且自动写入日志。
日志的存储上,因为下载量巨大,不方便用单表存储,采取按月分表的方式,因为这个数据只是用于运营定位问题,以及一些数据分析,不作为在线业务使用,所以这种分表方式完全满足我们的使用场景。
- 产出配套的运营分析工具和监控
=
04 效果和总结 经过如上改造后,再遇到类似的问题,应对方式已经完全不一样了:
场景
文库用户甲:我刚买了个文档,下载提示失败请重试,是什么原因?
文库客服乙:您好,经查询你下载的文档属于第三方问题,下载当时对方服务稳定性有问题,我们已经联系合作伙伴解决,现在好了。
文库产品:我们发现XX黑产工具再用非法手段下载我们的内容,需要在所有下载入口针对此工具紧急上线一个封禁策略
文库研发:好的,一改全改。
我们通过微服务化改造,从巨大单体式应用分离出独立的下载业务架构,从而化繁为简,解决了复杂性问题。新的微服务架构也不局限于原来的技术栈,开发者可以自由选择新的开发技术,提供API服务。新的下载服务只关注于自己的业务功能,并且独立开发部署,解决了耦合问题。实际上,进行微服务化改造之后,下载业务的迭代无需依赖其它服务,迭代速度也快了很多,最终业务问题也得到了收敛。
——————END——————
推荐阅读:
百度APP Android包体积优化实践(二)Dex行号优化
百度APP Android包体积优化实践(一)总览
百度APP iOS端内存优化实践-大块内存监控方案
百家号基于AE的视频渲染技术探索
百度工程师教你玩转设计模式(观察者模式)
【微服务化解决文库下载业务问题实践】Linux透明大页机制在云上大规模集群实践介绍