2012 年,在开放云融推动各产业全面发展的大背景下,苏宁 API 对外开放。基于苏宁各内部业务系统的资源,开放丰富的 API 服务,提供给苏宁商家、供应商、售后服务商、物流公司、软件服务商等合作伙伴所需的数据和信息。实现外部系统与苏宁的完美对接,使业务的处理更加高效、便捷。
通过阅读该文章你会了解到如下信息:
- 一切皆基于标准
- 系统重构详解
- 高可用设计
- 监控之道
- 应对 O2O 购物节
到现在为止,苏宁已开放了平台业务、自营业务、自采业务、O2O 业务、政企业务、特卖业务、4PS 业务等几百个 API 接口,每天承载海量的 API 接口调用。
另外,由 InfoQ 举办的 ArchSummit 全球架构师峰会即将于 12 月 8-11 日北京举行,大会与阿里巴巴合作策划了双 11 架构专场,并邀请了顶级技术专家担任出品人,设置了“新一代 DevOps”、“人工智能与业务应用”、“架构升级与优化”等 17 个热门话题,目前大会日程已出,欢迎前来讨论交流。
一、一切皆基于标准
俗话说“无标准不平台”,苏宁API 设计之初,没认识到标准的重要性,遇到过很多问题,走过很多弯路,接下来细说下苏宁API 的标准化设计的过程。
1. 从接口名开始
谈起 API 接口,首先会想到接口名(接口英文名)。
先来看一组接口名:
suning.order.get suning.selfmarket.order1.query suning.custom.book.item.query suning.retuenBadArticleHandleResults.add suning.shoppingmallsalesdata.saveandupdate
以上接口名有如下问题:
- 长度不一。
- 风格不一致。
而一个标准的接口名应该有如下特征:
- 风格统一 (都为英文小写)
- 模式固定 (xx.xx.xx.xx)
- 简洁明了
- 有扩展性
- 分类与分层
举例:suning.custom.order.get
格式:suning. 业务分类. 模块简称. 操作符
2. 文档规范化
用户通过阅读 API 文档来使用 API 接口。一个规范的 API 文档有助于用户理解 API 的用途,掌握 API 的使用。
API 文档组成
API 文档详情
3. 协议主流化
苏宁 API 主要是基于 Http(s)+OAuth2.0 协议实现的。可以使用 xml 或者 json 格式进行报文传输。并提供主流 (Java、.Net、PHP、Python) 四种语言 SDK 方便用户使用。
API 接口为一个全局统一的入口:
https://open.suning.com/api/http/sopRequest
4. 服务契约固定化
这里服务契约是指苏宁 API 系统与苏宁内部系统之间的服务契约。苏宁 API 承接着苏宁内部多方系统。如果不固定服务契约,苏宁 API 就无法做到标准化,这是一个 API 标准化的一个前提。有了服务契约的固定,才能实现动态发布 API,缩短 API 的研发周期。
5. 异常码标准化
什么是异常码
即调用 API 接口异常场景下返回的错误码。苏宁的 API 接口返回异常码的同时也会返回中文详细描述,以增强异常码的可读性。异常码通常表示某种异常场景,具有唯一指向性。
异常码的设计问题
异常码常见的问题包括:无统一风格、无分类、无唯一性、难以理解等。基于以上问题,苏宁的 API 网关在异常码方面进行一些标准化设计,使异常码变得有如下特征。
改进后的异常码特征
- 唯一性
- 风格一致
- 有层级有分类
- 可扩展
异常码的分类
异常码标准化带来的意义
- 异常码监控
每个异常码都代表不同的异常场景,异常也有主次之分,重要的异常场景可通过异常码配置监控。 - 接口告警
基于异常码监控,一个接口可配置多个异常码监控告警。 - 定位问题
异常码具有唯一指向性,能快速地位问题。 - 解决问题
在基于异常码具有唯一指向性的前提下,能快速解决问题。
6. API 接口配置化
在以上标准化的基础上,为了缩短 API 接口上线周期,我们实现了 API 接口配置化,能支持自动发布 API 接口、动态修改配置项。具体界面如下图:
- 基本信息配置
- 外部请求 API
- API 请求内部
7. SDK 自动化
当 API 接口发布上线后,会自动生成(Java、.Net、PHP、Python)四种语言的 SDK,并执行自动化测试,测试通过后上传官网,供外部用户使用。
二、系统重构详解
随着 API 接口数量越来越多,一些问题逐渐暴露出来,下面详细介绍几个关键问题的重构详解。
1. 响应时间慢
系统上线之初,选用 IBM 的 IHS 和 Websphere、DB2 来部署 API 系统,系统架构如下:
这套架构的优点是简单,整个 API 运行在 Servlet 容器中,API 全局入口是一个 Spring MVC 的 Controller。缺点是 API 系统不是通过服务调用业务系统,而是自己要连接业务数据库,并处理业务逻辑。一个 API 接口对应一个 API Service,实现了简单的流控、校验、权限认证、本地配置功能。随着时间变迁,API 接口数量越来越多,API 接口开发周期变长、响应时间逐渐变慢了。
2. 并发支持低
为了解决 API 接口响应时间慢的问题,对系统进行如下重构:
此时 API 系统架构变成 Nginx+Wildfly(JBoss),整个 API 运行在 Servlet 容器中,API 全局入口是一个 Servlet;同时新增了流控、监控、调用器组件;API 业务逻辑处理修改为通过 Hessian 方式调用业务系统;监控组件同步发送日志到 kafka,Elasticsearch 从 kafka 消费日志,建立索引。
经过上面系统重构后,系统运行一段时间,发现接口响应返回越来越慢,很多无效调用和攻击影响正常的请求,同时核心链路并发支持低,也是影响接口响应慢的原因。
3. 接口分流
为了解决无效调用、安全攻击、核心并发支持低导致 API 接口响应时间慢的问题,对系统进行如下重构:
此次系统重构主要是强化接入层的功能,在接入层实现了防攻击、基础校验、日志、降级、流控等功能,同时将核心服务权限认证进行平台化,增加核心链路的并发度和增强系统扩展性,将监控组件异步化。
运行一段时间后,又出现了一个现象,运行快的 API 接口会被运行慢的接口影响,也变得运行慢。
4. 核心组件重构
为了解决接口之间互相影响的问题,对系统进行如下重构:
在接入层进行接口分流,不同的接口请求到不同的后端服务,这样接口进行应用层的资源隔离;并对应用层进行重构,重写核心组件;整个应用层基于 Netty 提供 API 服务;IO 线程负责连接的接收与建立;Work 线程负责 IO 的读写;同时支持多组 work 线程池,用来隔离不同接口和业务直接的互相影响;服务调用组件修改为苏宁自研分布式服务框架 (RSF),使用异步回调方式,提高系统的吞吐量,核心权限认证服务,也修改 RSF 调用方式,提高服务的性能,新增 Zookeeper 来提供配置管理,来实现分流的动态配置修改。
三、高可用设计
在系统重构过程中,除了提高系统的性能,系统的高可用设计也尤其重要。下面将详细介绍苏宁 API 的高可用设计包括哪些内容。
1. 安全保障
一次 API 接口请求链路如下,每个链接职责分离。
- CDN:能够实时地根据网络流量和各节点的连接、负载状况以及到用户的距离和响应时间等综合信息将用户的请求重新导向离用户最近的服务节点上
- 硬件防火墙:重点关照外联链路和外联 VPN 做了一些通用的防火墙策略
- 负载均衡:能支持域名为维度做并发控制,限制每秒的最大请求数
- WAF:基于 Nginx+Lua 实现,提供同样的安全攻击防护和域名维度、URL 维度、IP+URL 维度的准实时流量管控。
- Nginx+Lua,也是基于 Nginx+Lua 实现,提供 API 接口维度的安全攻击防护和多层级的流控策略,来保证系统的稳定性。
2. 流量管控
API 系统流量管控,主要分为接入层和应用层流控。接入层主要是基于 Nginx+Lua 来实现的,应用层基于计数器和信号量等实现。具体信息如下:
3. 接口分流
接口分流基于 Nginx+Lua 实现,预先对每一个 API 接口进行逻辑执行区域的分类,不同的分类对应不同的后端服务,通过 Zookeeper 动态管理配置项,一个接口请求会找到对应 upstream Server。
4. 服务降级
接口降级同样基于 Nginx+Lua 实现,Nginx 共享内存中存放接口的降级信息,通过 Zookeeper 动态管理配置项,如果接口被降级就会直接返回异常码,否则接口就会执行接口分流逻辑,请求会找到对应 upstream Server。
同时服务降级也支持多个层级,具体如下图:
四、监控之道
当 API 接口数量和调用量达到一定程度后,面临了如下问题:
- 日志监控
- 如何记录接口完整调用记录?
- 用什么存储接口调用记录?
- 如何准确查询接口调用记录?
- 查询日志能否准实时?
- 接口统计
- 如何实现接口调用统计?
- 是离线统计还是实时统计?
- 按照哪种维度进行统计?
- 如何存储统计数据?
- 如何展示统计数据?
1. 日志明细
第一版日志明细实现系统设计如下图:
上线后,由于 Flume 采集任务和 Hadoop 任务导致日志明细不能实时查看,整个日志延迟 20 分钟。基于上述原因对系统进行重构,结果如下:
去掉 Flume 采集任务和 Hadoop,直接采用 Elasticsearch 消费 kafka 数据,实时建索引。
日志源和 kafka、Elasticsearch 都具备扩展性,满足性能要求。
2. 接口统计
依赖于苏宁数据大数据平台来完成接口的统计。
- 使用 Hadoop 进行离线统计,根据不同维度编写 Hive 语句转换为 MapReduce 任务进行计算,通过 Sqoop 将计算结果导入到 MySQL 数据库中。
- 使用 Libra 实时计算平台进行实时统计,根据不同维度编写 epl 语句转换为实时计算任务进行计算,计算结果发送到 Kafka,应用监听 Kafka 消费实时计算结果,存储到 Redis 中,并设置数据过期时间。
Libra 实时计算平台是苏宁在 Storm 的基础上二次开发封装的,通过编写 EPL 计算语句(EPL 是 esper 中一种类似 SQL 的语言), 转换为一个个计算规则,提供实时计算功能的平台,主要特点为配置简单能够实时计算需求的快速上线、支持分布式计算、支持动态扩容,以满足大数据量的计算需求。
3. 监控的意义
在没有监控之前,存在如下问题:
- 定位问题很困难
- 没有接口运行数据,无法优化接口性能
- 没有接口统计数据,无法考核服务质量
而有了监控之后:
- 方便定位问题
- 优化性能隐患
- 方便考核服务质量
- 方便实现业务扩展
4. 智能告警
苏宁有如下告警平台和告警机制,能够准确地通知开发及运维人员系统的异常状况,有助于快速定位问题和解决问题。
- ZABBIX 监控系统。全方位监控操作系统、应用系统层面 (ZABBIX 是一个基于 WEB 界面的提供分布式系统监视以及网络监视功能的企业级的开源解决方案)。主要监控项包括 CPU、系统负载、内存、网卡流量、磁盘使用量、连接数、宕机检测等。告警方式为短信或者邮件。
- 穆加。苏宁智能告警平台,整合了 ZABBIX 监控系统、云迹服务端性能监控、Kafka 后台管理系统、决策分析平台、机器学习平台、准实时计算平台监控来源,提供更细致化的、丰富的指标种类,提供豆芽(苏宁自研的实时通讯工具)通知、短信、邮件、语言等多种告警方式。
- 云迹异常监管系统。能针对某个系统的某种异常,在单位时间内异常量或环比增长量超过配置的阀值,则进行短信或者邮件告警。
以上都是苏宁系统提供的通用告警机制,接下来介绍苏宁 API 系统实现的告警机制:
- 提供了基于 API 接口维度的告警,调用时长、调用次数、失败次数、失败率等维度,在单位时间内超过配置的阀值,则进行短信或者邮件告警。
- 提供了基于 API 接口异常码维度的告警。在单位时间内异常量或环比增长量超过配置的阀值,则进行短信或者邮件告警。
五、苏宁 API 网关应对 O2O 购物节
从 2012 年开始,每年双十一苏宁都带给消费者一次次购物享受之旅。今年苏宁易购双十一的主题是“不止所见嗨购 11 天”,换句话说,这场狂欢盛宴将始于购物,但不止于购物,为消费者提供其他电商没法实现的“任性”。
O2O 购物节是一次狂欢盛宴,对每一个苏宁人来说是一场必胜的战役,为了保证 O2O 购物节系统正常,需提前做好系统保障工作,正所谓不打无准备的战役。
O2O 购物节中苏宁 API 网关提供哪些应用场景辅助商家、供应商、服务商进行商品交易?下面列举了主要的应用场景:
- 商品发布
- 获取苏宁采购目录
通过接口 suning.custom.category.query 获取 categoryCode - 获取商品品牌信息
通过接口 suning.custom.brand.query 获取 brandCode - 获取苏宁产品库信息
通过接口 suning.custom.product.query 获取苏宁产品信息 - 获取苏宁产品详情信息
调用接口 suning.custom.productdetail.query - 商品内容维护
调用接口 suning.custom.itemcontents.add - 获取商品参数模板
调用接口 suning.custom.itemparameters.query - 产品申请
调用接口 suning.custom.item.add
- 获取苏宁采购目录
- 订单同步
- 获取三个月前的订单
调用接口 suning.custom.historyorder.query 批量获取订单(三个月前的历史订单) - 获取三个月内的订单
调用接口 suning.custom.order.query 批量获取订单(三个月内的订单)
调用接口 suning.custom.ordercode.query 批量获取订单号
调用接口 suning.custom.order.get 查询单笔订单 - 获取增量订单
调用接口 suning.custom.ord.query 根据订单修改时间批量查询订单信息
- 获取三个月前的订单
- 物流发货
- 苏宁物流或第三方物流配送
调用 suning.custom.orderdelivery.add 对订单进行发货。 - 商家自配送
调用 suning.custom.orderselfdist.add 对订单进行发货。
采用该接口发货时,需要填写商户自己执行配送的人员的姓名和手机号。
- 苏宁物流或第三方物流配送
战前
- 容量评估
根据 O2O 购物节订单预估量,对系统进行容量评估。分场景压测系统得到性能测试结果,再根据结果进行性能达标评估。不达标的系统需进行自动扩容,扩容后需对系统进行性能测试,检验系统容量是否达标。
- 性能测试
每次战前,各个系统都要提前对关键路径进行性能测试,找出性能瓶颈并优化。
- 预案梳理
梳理系统的紧急预案,在 O2O 购物节中异常事件发生时进行预案处理,如服务降级,关闭非核心功能,保证核心功能。
- 预案演练
根据 O2O 购物节中异常事件启动对应的紧急预案,看看是否能达到预期效果。
战时
- 集中值班
O2O 购物节期间安排 24 小时集中值班,来监控系统运行情况,应对突发紧急问题,并解决问题。
- 系统监控
- 监控系统运行情况,各项指标 (CPU、IO、内存、网卡流量等)
- 苏宁云迹链路监控大盘
- 预案执行
O2O 购物节期间发生的异常事件,根据战前准备的预案执行,监控执行结果。并更新预案执行状态,好做后期恢复。
- 问题解决
O2O 购物节期间发生的异常事件,会成立问题解决专项小组,严格跟进问题解决状态,直到问题解决才关闭。
战后
- 预案恢复
O2O 购物节结束后,针对 O2O 购物节期间执行过的预案进行恢复,并监控系统运行状态。
- 问题总结
O2O 购物节结束后,针对期间发生的问题进行总结和反思,制定方案解决问题。
- 性能优化
O2O 购物节结束后,针对期间发生的性能问题进行系统重构和性能优化。
作者介绍
何翔,2010 年加入苏宁,主要从事 JAVA 领域开发与设计工作,现担任苏宁云商 IT 总部多终端开发技术负责人,全面负责苏宁 API 网关管理与设计工作。从零开始搭建苏宁 API 网关,通过一次次系统重构与性能优化,经历了多次 818 促销和双十一 O2O 购物节的考验,支撑了亿级流量调用,保证系统稳定高性能。在 NIO、性能优化、高可用、扩展性、高稳定性等方面有一定的思考和领悟,目标是用技术打造一个高性能高可用的服务平台。
评论