回忆人人车用户端架构演进
架构 刘宇帅 7天前 阅读量: 71
最近入职了新的公司,新公司当前的技术架构和人人车当年的架构演进的过程很像,我尽量回忆了下当时在人人车的记忆,整理本文并在新公司内部做了分享。虽然离开人人车已经5年了,但是人人车技术团队对我不管是技术上还是作为程序员的基本素质上的影响都是终身受益的,感谢人人车公司、感谢当时的技术团队及每一位成员。
人人车介绍
人人车是二手车 C2C 模式首创者,于 2014 年 7 月上线,2017年月销数万台。
起步 — 能用就行
- LNMP一键安装(CI框架)
- Redis缓存
- Solr搜索
V0.1架构
业务高速发展
- 线下团队爆炸性的增长,销售、评估师、客服人员等都增长不止一个量级
- 车辆数、订单量、客服量等也在飞速增长
技术架构负重前行
- 研发每天处理大量 bug
- 全员皆运维,都在敲命令
- 各种服务异常、全站异常
- 研发很辛苦,产量却很低
- 业务吐槽不靠谱
V0.2架构
V0.2架构问题-运维与流程
- 服务器:2台服务器承担了所有服务,服务相互影响,资源竞争
- 发布:发布方式原始,svn checkout, scp 覆盖
- Rollback:回滚流程与工具缺失,回滚不方便、不完全
- 流程:上线流程不规范、无通报相关机制、无回滚预案、上线后回归验证不全面
V0.2架构问题-服务与架构
- 框架:无统一的框架,项目结构不统一,冗余,通用库、工具、类无法复用
- 服务拆分:服务分层不清晰,服务耦合严重,拆分不合理,业务调用不统一,跨层、跃层调用问题严重
- Cache:核心业务缺少缓存,系统响应慢
- DB层:DB 数据库耦合严重,未按业务独立拆分,部分字段设计不合理包含大 json、 长字段、慢查询
- 日志打印不规范、无统一的日志监控和可视化服务
- 服务监控缺失
V0.2架构优化方向
- 服务拆分
- 运维部署流程优化
- 服务监控
- DB 拆分
- 慢 SQL 优化
- 同步转异步
- 微服务架构探索
服务拆分原则
- 各模块 git 代码层面独立管理与维护
- 线上独立 SLB、独立部署、独立运维
- DB 层面数据隔离,独立从库
- 通用功能抽取到 api 层
- 核心业务逻辑增加 cache 层
DB慢查询优化
- 每天要求研发同学优化 top5 慢查询
- DB 读写分离
- 收敛数据库操作,提供API接口,可控
- 加强SQL审核
- DB schema 规范和review
DB schema规范
权限
1. 所有数据库的 DDL 操作全部收回,由 OP 统一管理
2. 收回 RD 的 delete 权限,所有数据不能做物理删除(逻辑上删除的需求可以通过 flag 标签的方式来处理)
基础字段
1. 每张表三个必加字段,id (自动增长), create_time , update_time 用作存储主键, 记录创建时间,记录更新时间 , 这三个字段由 OP 维护,RD 无需处理
2. 有逻辑删除需求的表,可选增加 delete_flag 字段(int 类型,默认值为 0),用来 标识字段是否被逻辑删除
统一注释
1. 每张表/视图要有注释(comment),格式为 模块|用途|负责人|创建日期 ,例如: 贷款|记录贷款用户身份证号码|张三|2016-03-25
2. 每个字段要有注释(comment),格式为 用途|负责人|创建日期 , 例如:记录用 户性别@1:男@0:女@2:未知|李四|2016-03-25
3. 字段来源需要说明,来自哪张表的那个字段,例如:记录车辆 ID,#取自 cp_used_car.car_id|李四|2016-03-25
4. 如果字段为枚举类型,或普通数据类型当做枚举使用,需要列举枚举范围并说明每 个枚举值的含义,例如:记录用户性别,#系统根据用户身份证号码判断@1:男@0: 女@2:未知|李四|2016-03-25 ( | 用来分割不同项目 , # 用来标识数据来源 , @ 用来分割多个枚举 ,: 用来分割枚举名称和值 , 正常的描述中不要包含 | # @ :)
DB拆分优化
- 各实体分库分表设计
- DB 主从拆分
- 数据加密杜绝明文存储
- 表与 DB 设计:必须遵循人人车 DB 设计规范与 schema 设计规范
- 索引设计:合理使用索引,杜绝滥用索引
v1.0架构
V1.0架构体感
业务拆分很清晰,开发体验很一般
V2.0 2017年“微服务架构”
监控系统:Openfalcon 报警与可视化(系统监控、应用监控)
功能性监控:healthcheck(服务本身及依赖资源检查)、无头浏览器监控与报警
日志系统:ELK(日志搜索与服务质量仪表盘)
接口框架:内网提供SDK(PHP compoer包,统一http调用实现和异常封装)、外网统一http结构和状态封装(全局异常捕获封装异常请求)
API网关:Kong(流控、黑名单、鉴权、日志、爬虫识别)
服务注册与发现:etcd+confd
配置中心:etcd+confd
自动化部署:Jenkins+gitlab
发布:小流量&&预发
自动化测试:单元测试、接口测试(Mock测试、行覆盖率和分支覆盖率)
服务跟踪:LOG_ID PLOG_ID
负载均衡:内网SLB + SDK
高可用架构:所有服务最少部署两份,多台ECS处于不同可用区
服务安全:服务调用统一授权(SDK封装实现)+ IP白名单
数据安全:Zeus(统一的数据加密服务)
框架:日志、Memcache、Redis、队列、接口Base SDK、统一异常等封装
API文档:Swagger
搜索服务架构
- search服务4台服务器,两个可用区
- solr集群(多可用区)
- 在售集群——C端
- 热备(预防在售solr集群出现不可恢复等故障)
- 已售集群——业务端
- spider集群——全量数据,SEO优化
- 在售集群——C端
- 降级(定时刷新核心筛选项到缓存服务,手动开启降级)
- 排序策略:公式算分+solr插件
那些痛苦而美好的回忆
- 静态检查:代码风格严格检查,否则无法上线
- 测试:每次MR必须有新增的单元测试、接口测试,分支覆盖率、行覆盖率必须提升
- review:所有代码必须有comment才能合并
- Merge code:自己合自己代码报警
- 函数50行:所有函数不能超过50行
- push code:10点之后不能提代码(有效提高代码质量)
- 旧代码:所有在MR中被修改的文件必须满足静态检查、函数50行才能被合并
最后
PHP是世界上最好的语言
最后的最后
任何一门语言都可以是世界上最好的语言