租房业务APP后端服务重构之路

导语

本文通过结合实际的业务痛点,阐述了如何进行服务重构与性能优化,介绍了诸多实际落地方案。

背景

随着租房业务的快速发展,业务系统开发人员在忙于业务系统快速迭代的过程中往往会忽视了系统本身的优化,需求不断的累积,导致冗余逻辑越来越多,代码耦合度越来越高,开发迭代效率也变的大不如前。一个同样的功能,集成在不同页面,有时候需要写多套同样的逻辑代码。在APP端尤其明显,严重影响了用户体验和迭代效率,因此需要对业务系统做一次升级优化。

业务现状

租房业务系统在2017年经历过一次服务化改造和重构,解耦了核心的业务和服务,抽离出了几个公共应用服务层,对于性能和开发效率都有一定的提升,然而随着业务的快速迭代、变更,我们的业务系统也需要随之升级。我们之前的业务系统是为房产所有类目如租房、二手房、商业地产等而设计的,业务系统重点关注的是不同类目不同场景下,相同类型页面的通用性以及扩展性。而2019年集团战略调整,我们只专注于租房业务即可。这就需要我们在优化设计系统时重点对租房业务的快速发展以及未来的业务可能变化提前做好准备,避免以后系统反复重构,浪费资源。当然也要考虑到避免过度设计,否则会带来更高的维护成本和线上排查问题的难度。

问题&设计方案

1、开发效率方面  1.1、问题

  • 业务代码长时间积累,可读性差。

  • 单一接口,模块众多逻辑耦合,需求改动互相影响,不利于业务系统稳定性。

  • 相同模块的扩展性,复用性差,影响迭代速度。

  • 各个系统职责模糊,流程不规范,不利于新人介入。

目前影响开发效率最主要的原因是由于业务调整业务层系统架构设计的扩展性和复用性差,不同页面系统的相同模块有产品逻辑迭代需要更改多个页面系统。那么如何更改系统架构提升系统的开发效率呢?
1.2、方案
1)拆分通用模块系统
对现有的业务系统进行全局分析,分析出哪些通用模块可以拆出单独的系统,既需要考虑的业务的扩展性,又需要考虑的业务的差异性以及对于各个版本的兼容性。最后我们从原有的系统之上又拆分出了四个业务子系统,分别是推荐系统、列表系统、微聊电话系统、房东信息系统。

老系统结构图VS新系统结构图

2)子模块不同页面体验一致:
统一交互&视觉规范,接口协议字段对齐且增加区分场景标识,保证各个页面场景上的相同模块业务逻辑保持一致。
3)统一系统流程规范
统一开发规范,统一各个系统职责规范,保证系统的单一职责性。
4) 通用模块系统平滑迁移
由于新的公共模块协议系统返回协议格式适配了所有APP迭代的版本,业务迁移的话成本还是比较低的。

  • 迁移方式采用内部系统互相调用的方式平移迁移,对上下游页面系统无感知。

  • 采取灰度配置策略,保留老代码逻辑增加灰度配置开关,实时监控,保证有问题随时回滚。按照流量10%、20%、50%、100%逐步灰度放量直至全量。

1. 3.效果

  • 需求层面:支持单个系统快速迭代,以及多个系统互相组合适配出新的页面,伸缩性强,支持业务场景最小成本的快速试错。

  • 开发层面:各个系统模块独立开发,代码解耦,维护成本低,职责更清晰。

  • 运维层面:各个系统独立部署,单独扩容,互不影响。

  • 实际效果:例如以下页面都是根据拆分后的系统快速互相组合适配出的新页面,以前开发周期至少2人/日,而现在基本上只需要几个小时就能适配出来,大大提升了我们的开发效率,如下图线上复用配置模块:

2、用户体验方面  2.1、问题

  • 接口加载速度慢?

  • 用户感知卡顿?

  • 接口拆分不合理?

  • 请求合并没做到位?

2. 2、

解决方案

1)对接口异步拆分模块数据缓存

a)根据实际功能页面模块拆分

b)首屏数据在没有数据依赖的情况下尽量拆分成单独接口

c)非首屏数据拆分异步接口实现

d)数据分级,根据级别设置缓存和数据超时时间

2)并发请求数据

a)数据之间有依赖无法并发去请求数据?例如租房详情页需要展示房源数据和小区数据,小区数据是根据房源数据里返回的小区id获取的,这就导致了这两块数据无法并发获取,针对于这种情况我们可以对上游页面做下分析,在不影响上游页面性能的基础上可把参数传递过来,达到并发效果。

b)并发如何实现?由于现有业务系统采用nginx+php-fpm架构,对于单个接口里依赖的多个数据都是依次顺序获取,延时随着需求迭代不断的累加,又由于租房业务系统基本都是io密集型的场景,综合考虑最终我们运用了nginx+swoole+php架构模式,利用swoole的协程进行请求合并。

3)核心数据内置

APP内置变化频率较低的地域数据,地铁数据,筛选项数据,以及一些通用配置数据,已达到更顺畅的用户体验的效果。内置的数据支持主动和被动的更新机制,引入了数据版本概念在数据源切换时用户能够无感知平滑切换。

a)内置数据特点:变化频率较低,使用频率高,用户体验提升明显。

例如地域、商圈、地铁、学校等筛选项数据,以及一些通用配置数据。

b)通过和客户端端约定规则,使内置的数据支持主动更新。

例如有时候会受一些政府政策影响,需要屏蔽一些区域或者商圈,并且需要马上生效,我们通过现有的技术策略能根据服务端下发新的数据主动实时更新生效。

c)数据源切换时用户无感知平滑切换。在底层数据源有切换时服务端根据数据版本的概念,针对于不同的版本用不同的逻辑去处理,例如租房的地铁数据从a切到了b,如果服务端还用a的处理逻辑就有问题了,服务端会先上线a和b不同版本号的处理逻辑,之后在进行底层数据源切换。

在数据源进行切换时如下图所示:

4)预加载&数据前置

预加载数据前置,就是把有依赖的数据在上一步预先准备好,怎么做好前置数据依赖取决于你对上下游页面逻辑的了解,以及上下游页面系统技术栈的了解。

a)在不影响上游页面性能前提下,把上游页面与下游页面重叠的数据直接带到下游页面,以达到预先加载数据的效果。

b)上游页面与下游页面重叠数据要提前规划好,保证获取数据逻辑的一致性。

c)预加载后的数据根据数据和业务场景特点决定是否进行数据覆盖。


3、具体实例

1)APP租房大类页

对于首屏的金刚位,先显示后更新,接入配置平台,数据缓存,异步更新。其余模块拆分接口异步加载。

2)APP列表页

对地域、地铁、学校等数据内置,预拉取筛选项本地选中高亮实时交互,列表页接口拆分成筛选项接口、列表数据接口,非核心模块数据、引流位数据超时抛弃上报。

3)APP详情页

首屏数据预加载列表页带入预加载数据, 接口拆分主房源接口+房东/经纪人模块接口+小区模块接口+推荐附近/同价位模块接口,各个模块基础数据缓存,数据并发请求。


4、效果

主要场景

首屏渲染avg时间 服务接口avg耗时

优化前后对比

优化前后对比

APP大类页 下降60% 下降65%
APP列表页 下降55% 下降60%
APP详情页 下降72% 下降80%

团队介绍

58租房技术部,负责58和安居客双平台的租房业务研发,业务上致力于提高租客和房东在房屋出租上的用户体验和效率,技术上我们采用PHP和JAVA的双语言技术栈,业务流量、数据量、复杂度都比较高,在系统的性能、稳定性、研发的效率提升方面有丰富的技术积累,团队技术氛围浓厚。

作者简介

果海涛,房产租房技术部。16年加入58同城先后负责从0到1的PHP技术栈的服务化重构、租客端业务,专注于业务系统架构和性能提升等工作。