闲鱼服务端架构演进历程
1、闲鱼的业务特点
据巴滕介绍,闲鱼是一个典型的双边市场,买家和卖家规模相互影响,“如何同时服务好买家和卖家双方,这是我们一直努力的方向”。
对买家来说,要提升商品发现效率,帮助他们尽快地买到商品。
对卖家而言,要降低发布门槛,帮助他们尽快地把商品卖出去。
对于平台,要持续优化用户的使用体验,比如降低纠纷和欺诈问题,同时持续扩大市场规模。
2、闲鱼服务端最初的架构设计
众所周知,闲鱼的前身是 PC 时代的淘宝二手,它属于淘宝的一个小频道。当时,闲鱼整体业务规模和用户量都非常小。基本上,单一应用就支撑所有业务,并且整体架构和服务完全是面向 PC 设计的。
“当时,服务端同学需要时常编写 Velocity 模板代码,又由于部分前端代码也部署在业务服务器中,前端和服务端同学还要经常需要修改同一个应用。”巴滕说。
当时,闲鱼服务端的基本架构如下图所示:
据悉,webx 是阿里内部大量使用的一套基于 Java Servlet API 的通用 web 框架,可以类比为 Spring MVC,“在阿里 PC 站点上经过多年应用,不仅成熟可靠,而且具备极高的扩展性和开放性”。
巴滕称,“这套架构当时跟淘宝的 PC 架构是基本一致的。由于业务规模小,还未做服务化拆分,并且负责维护开发的技术同学经常调换,所以采用这种单一服务架构,维护成本相对较低又能支撑业务诉求。”
3、闲鱼服务端架构演进
刚创立时,闲鱼的定位是做移动端独立 App。这样,他们在做架构设计时就面临两大问题:
整体架构必须从 PC 全面切换为无线,这需要进行大量的 0 到 1 的技术基础设施的完善和引入; 业务形态复杂化,单一应用带来的耦合问题和开发效率问题,因此要进行服务端的服务化改造。
不过,手机淘宝当时已经积累了一些无线端的基础设施,例如统一无线网关、开关系统、hotpatch 等。
这样,第一波的改造更多的是接入和集成阿里集团的这些无线中间件。客户端(包括前端)和服务端的工作界面也以无线网关的接口为分割线,服务端主要负责基础的数据返回,客户端则完成数据到 MVC 模型的绑定和控制。
服务端则进行了一些服务化改造,从单一应用进行横向拆分(按业务拆分)和纵向拆分(对数据层的访问进行收敛,并根据服务能力让一些基础能力下沉形成公共服务)。业务层做薄,更轻量化的支撑业务快速迭代,拆分独立业务网关层来对接无线接入层网关,收口完成所有的客户端业务数据,最后拼装。
此时,闲鱼服务端的架构基本如下图所示:
在第一轮无线化和服务化改造后,闲鱼服务端初步能够应对 App 的正常迭代。但是业务发展速度太快,用户规模以极快的速度增长,业务团队的需求快速膨胀,这让技术又面临新的挑战:
线上服务性能不足 迭代速度不够快
用两个典型例子来说明。
案例 1:闲鱼商品量从数十万快速膨胀到数千万,而闲鱼原来用 Lucene+solar 搭建的搜索引擎出现问题:无论是服务能力,还是算法能力接入,以及运维成本,都已经不堪重负。闲鱼必须要升级整个搜索引擎。“当时,我们的选择是升级到集团的大规模搜索引擎 HA3 上,同时接入集团统一推荐平台 TPP 上“。
案例 2:随着业务需求的不断增多,大量的活动类型开发需要前端上线,但是每个活动对服务端数据又有略微的差异。如果全部都需要服务端人员每个接口定制开发,服务层网关开发以及发布等工作量和周期都较大。因此,团队在服务端上参考 Facebook 的 GraphQL 做了一套自己的 MBaaS 的解决方案。它被称之为 Card 模型,其核心思想是基于 MVVM 的概念,将一部分 Model 和 View Model 以及部分的 View Controller 前置到服务端下发,客户端做薄。
在巴滕看来,应对服务能力不足,他们更多的是在做一些基础能力的升级,比如更彻底的业务服务化拆分和解耦、存储层优化(数据库拆分和扩容,大规模多级缓存等)和一些关键服务的升级。而应对研发迭代速度慢的问题,更多的是做一些动态化和复用性的尝试,尽可能降低协同成本,提高代码和模块的复用性。
当时的系统架构如下图所示:
经过第二轮的升级改造,服务端在性能上已经不惧任何规模的流量。基本上,只要做好容量规划和采取各种高可用措施,就能保障系统的服务能力足以应对业务增长。
而在这一轮的改造中,团队又看到从原来纯粹的工程主导,开始逐步有更多算法能力的接入,“我们在整个搜索和推荐链路中初步具备智能化的能力”。
很快,服务端又面临新挑战:来自业务需要带来的新增长点的压力。巴滕表示,“近几年来,最大的技术红利莫过于 AI 和数据,如何让 AI 和数据在闲鱼有效地落地和发挥价值,这是算法和工程团队共同面临的问题,因此第三轮的技术优化应运而生。”
想拥抱 AI 和数据,首当其冲的是透彻的分析业务本身面临的核心问题。因为算法最擅长的是解决明确目标下的优化问题,所以团队又对闲鱼的业务进行抽象,分析找到闲鱼最核心的业务问题。
下图是将闲置市场和新品市场做的对比。由图可知,在新品市场上,由于商品和服务的确定性,价格基本上可以反映商品本身的价值。但是,在闲置市场上,无论是卖家,还是买家,都面临较高的信任成本。信任成本不仅仅体现在防范欺诈,还有买家和卖家对商品本身新旧程度认知差异带来的纠纷。
从图可以得知,闲置市场的特点决定了团队扩大市场空间的两个核心路径:
提高匹配效率 降低信任成本
因此,闲鱼的很多工程和算法就开始围绕这两个核心方向展开,并构建一整套的产品技术体系。
巴滕称,“对于降低信任成本,暂且按下不表,因为信任的解决和构建涉及到大量的琐碎和细致的工作,甚至依赖于整个社会的进步,我们主要介绍如何提升交易效率的一些思路。”
首先分析导致闲鱼交易效率低的关键要素:
轻发布。商品发布门槛低,只需少量图片 / 视频和简单的描述即可发布成功,但是轻发布带来的核心问题是商品本身有效信息量太少。举个例子,用户发了一张图,写了八个字——刚买的苹果转手出。系统就无法准确判断用户发的是苹果手机,还是真的水果,这样商品在搜索推荐链路的准确率必然受到影响。 商品单库存。商品只有一件,售出立即就下架了,那么整个搜索推荐链路对实时性要求就极高,不然就会带来大量的流量浪费。
基于这两个问题,“我们做了两个关键系统:一个是系统解决商品结构化问题,一个是系统解决整个流量分发链路上的实时性问题”。
与此同时,在面临团队进一步扩大、协作成本和稳定性风险增高的前提下,他们还孵化出一系列支撑业务解耦和隔离(SWAK 解耦框架)、线上故障实施定位(神探系统)等一系列的服务端系统和工具。
此外,他们在端上全面拥抱 Flutter,逐步构建了一系列基于 Flutter 和 native 混合栈一系列工程化和规模化开发框架,同时还在端上进行了一些智能化的探索。
这时候,闲鱼服务端架构演化成下图模样:
现在,除了继续解决业务领域的问题,团队还在进一步持续探索适应未来多端一体化的研发架构,希望通过底层技术的升级,进一步压缩业务开发同学在客户端双端加上服务端这三个端上的协同成本。
这套架构的核心思想是基于 dart 语言完成三端在协议层和通信层的统一,将传统服务端最后一步的数据拼装的胶水层工作全部交由一个客户端的一位技术人员就能完成。
巴滕说:“得益于 Serverless 的快速发展,我们的胶水层服务端接口已全部托管在阿里自研的 FAAS 平台 GAIA 上,可以实现非常低成本的运维开发,让业务开发同学更多的聚焦在业务上。”
4、在服务端架构上的新尝试
据巴滕透露,闲鱼服务端架构目前在一体化开发模式和智能化上进行一些尝试。
对他们来说,Serverless 更多是基础设施。他们的工作重点是基于集团不断构建的 Serverless 能力上,持续迭代和演化他们的一体化业务开发架构。而 Serverless 本身还是交给更专业的中间件和阿里云的相关团队。“一体化开发模式有可能改变我们整体的研发模式,前后端工作边界将再一次改写”。
智能化有两个含义:一个是在业务各种场景和工程环节中,如何充分的融入算法能力。这需要充分考虑算法能力边界,构建服务于算法的实时 / 离线数据能力、ab 能力等基础设施。同时,他们还在进行云智能和端智能结合的探索、商品和内容结构化工程体系等。
智能化的第二个含义是通过智能化提升研发效率。众所周知,整个研发流程链路是非常长的,从脑图到 PRD 到视觉交互再到开发测试,每一个环节都可能由于信息传递差带来的效率降低和 bug 出现。他们希望通过一些自动化的方式降低整个研发流程中一些信息传递的损失,降低重复性劳动,比如一直不断优化的 UI2Code 项目希望把视觉稿通过图像识别自动转化为可读性高的代码。
5、对服务端架构的思考
在巴滕看来,服务端架构目前最火的就是 Serverless,“但是我们要透过现象看本质,服务端架构永远在做两个方向的优化”。
研发效率。如何让业务开发效率更高,更少的人可以做更多的事,并且保证做的又快又稳又好。其实,Serverless 只是给出了一个解法,且目前还没有事实上的行业标准(做到类似 Spring 一样),这也是他们一个持续探索的方向。 业务赋能。技术要想在公司财报上不仅仅体现为成本,就要持续的创新,尤其是在 AI 浪潮下,服务端架构如何更全面的拥抱算法,让算法在工程平台上可以充分的发挥价值,这也是尤其要关注和思考的。“可以不做算法工程师,但是不懂算法的业务架构师一定做不好业务架构,这是我个人最近的一个认知。”他说。
6、写在最后
从闲鱼创立至今,巴滕见证了闲鱼从无到有、从小变大的过程。回首这 6 年,他这样总结——凡是过往,皆为序章,闲鱼依然有大量的问题需要解决,还有更广阔的空间需要探索。而在架构方面,他现在更多的思考:一是如何真正的赋能业务,如何让不可能成为可能,让在发生的事情变得更高效;二是如何进一步解放技术,让我们的时间和精力花费在更令人愉悦的 coding 上,而非简单的消费型代码。