京麦开放平台架构演进与优化之路
您目前处于:  2016-10-19

简介

—— 从全视角以时间线的方式,介绍京麦开放平台的发展演变和架构体系,重点讲解京麦HTTP网关和京麦TCP平台,如何实现API快速接入承载海量HTTP请求调用,以及如何建立TCP全双工的长链接会话通道;深度剖析基于Zookeeper的网关Register Center、基于Netty的平台TCP长链接Container、基于ElasticSearch、HBase的消息Search Engine等。


京麦(jingmai.jd.com)是京东面向卖家的多端多角色一站式协同工作台,其核心是为商家整合:店铺管理工具、经营咨询消息、商业伙伴关系,借此提升卖家的经营效率,促进彼此间的合作共赢。

京麦从2014年初创,逐步完成平台化转型,实现对外服务开放

开发版图

不断优化京麦插件流程,搭建京麦开发者中心、运营中心、服务市场等,提供应用发布、订购、支付、结算等服务能力,构建京麦新生态。为商家提供多款服务应用,日活跃商家 1万+。

以打造高可用高性能服务以己任,优化网关调用、细化错误码、可追踪的调用日志等,开放服务接口 500+,日调用量上亿次,99% 的调用性能在 200ms 以下。

平台版图

完成平台与网关的隔离,保证平台服务的稳定性。改用 TCP 通信框架,以 ProtoBuf 为传输协议,建立 Session 会话机制,实现请求响应、通知应答的上下行通道,保持长链接在线 1万+。

重构消息通讯模型,以基于 ElasticSearch 存储的消息云端,实现 TCP 在线通知和 IOS Push 的半推半查的消息推送模型,并通过互通协议实现消息与插件的协同办公模式。

① 基于OAuth2的插件授权流程


京麦插件授权采用 OAuth2 协议,OAuth2 在客户端与服务提供商(Resource Server)之前,设置了一个授权层(Authorization Layer)。客户端不能直接登录服务提供商,只能登陆授权层,以此将用户和客户端区分开来。客户端登录授权层所用的令牌(token),与用户的密码不同。用户在登录的时候,指定授权层令牌的权限范围和有效期。

京麦通过用户在启动插件时,客户端通过授权层生成用户令牌(Token),用户通过令牌访问服务提供商获取数据。

参考:An Introduction to OAuth 2

② 基于HTTP和TCP长连接的消息推送

早期搭建 HTTP 和 TCP 长连接功能主要用于消息通知的推送。消息从消息源被发送出来,首先经过消息路由,消息路由解析定位 keep-alive 客户端,生成消息通知,交由 HTTP Push 或 TCP Push 推送到客户端,客户端收到通知后,再请求消息服务端,将消息实体拉取到客户端本地,存库进行展示。而消息推送的整个过程,通过消息追踪埋点进行统计和监控。

补充:HTTP 长连接采用 Servlet 3 的特性,TCP 长连接采用了 Netty 4 框架。

③ 移动端JSSDK


JSSDK 实现了客户端 Natvie 和 Html5 Java Script 的相互调用。客户端编程包括了 C++、iOS 和 Android,虽然实现不同,但基本逻辑类似,通过实现 JavaScript Bridge 和 Native Bridge,它类似两个队列,基于事件驱动和回调函数,采用异步通信方式,实现相互调用。

④ Gateway


网关作为京麦最重要的业务之一,承载海量调用,网关架构分为多层结构,每层中包含多级开关和限流策略。

到达网关的请求首先由网关接入层拦截处理,包括两部分:首先是网关防御校验,这里包含降级和限流,以及多级缓存等,数据校验正确之后,交由网关接入分发;网关分发会根据网关注册中心的数据进行协议解析,之后动态构建调用实例,完成泛化反射调用。

说的简单点,就是网关实现了动态将一个外部请求调用(http-json)转化成为内部请求调用,这个内部调用可以使 http,也可以是 JSF/dubbo 调用等。

Gateway Kernal

在网关内核的抽象模型中,最核心的就是注册中心和分发中心。

⑤ Register Center Of Gateway

网关注册中心作为网关内核一个高可用的元数据部件,如何保证网关集群元数据的一致性和灵活性?

网关注册中心采用 Zookeeper 作为中间件,并在网关配置读写操作过程中,进行了多级容灾策略。

以读为例:网关首先从内存中读取配置,如无数据,从 Zookeeper 读取,读取后同步到内存,并异步保存本次快照。如果 Zookeeper 数据变更,通过监听 Zookeeper 的 DataChangeWatcher 变更同步数据。如果 Zookeeper 宕机,重启服务器,系统还可以通过本地快照恢复最近一次的元数据配置。

⑥ Buried Point & Data Statistics


数据埋点和数据统计采用了大数据的方式对数据进行收集和规整。数据收集采用异步方式,数据首先被序列化成十六进制,采用 MMap(NIO) 的方式存储到本地文件中,再异步的通过消息队列发送给消息接收端,规整批量存储 HBase,HBase 以增量存储设计 rowKey,通过 ETL 存储 Hadoop,再通过 Hive 计算结果,存储到 MySQL。

⑦ Plugin Order Process


订单逻辑的复杂在于事务操作,以及异常流的补偿操作。订单确认支付采用异步的方式,创建订单成功之后即返回,通过消息队列在异常情况,不断重试保证业务正常运行。

⑧ TCP Container

为什么选择 TCP?

我们知道,网关是负责接口调用获取请求数据的,属于单向操作。而搭建客户端与服务平台的 TCP 双向通道,保持客户端与服务平台的会话状态,则可以提供更多、更灵活的技术实现和业务实现。

采用 Netty 作为 TCP 容器,在 ChannelPipe 中加载自定义 ChannelHandler,构建 Container 容器,将每个 TCP Connection 封装到一个 Session 会话中,保存在 Container 容器中,由 Container 容器构建 Session Layer 提供 Logic 请求调用。

How to Re-Connect?

TCP 长连接断开之后,如果重连? 我们知道,移动客户端可能由于网络抖动、弱网络情况下,遭遇非正常网络闪断,如果处理断开后的连接再重连后,找到上一次连接的会话呢?

这个解决得益于 Netty 的 ChannelHandlerContext,它可以存储一些自定义属性到 Channel 的上下文中。在每次成功建立请求的 Channel 里存入 SessionId,将它作为一个钩子,当网络闪断后,再次请求建立连接的 Channel,可以通过判断是否存在 SessionId 的钩子,进行处理。

⑨ Message Push


消息架构的改造的几个大项:

一、解耦消息接入层和消息推送层,消息接入层只负责Request-Response和Notice-Repley,而消息解析、适配、推送等逻辑处理都全部由消息推送层处理,而消息接入层和消息推送层之间则有消息队列异步进行通信。

二、消息存储由原来的半推半拉改为半推半查,即消息只推送通知,所以消息实体存储在云端。采用 ElasticSearch 作为消息搜索引擎,通过 Routing 实现靠性能查询。

三、优化推送方式,将 TCP 通知由异步改同步,计算消息送到率。

Optimize ElasticSearch:  query with routing

ElasticSerach 在进行 Query 查询时,会由命中节点分发给所有 Node,每个 Node  处理请求 Merge 后,返回给命中节点,命中节点在 Merge 所有 Node 返回的结果,最终返回。

这样请求性能很低,通过对 ElasticSearch 自定义 Routing,实现 Index 和 Query 保证在一个 Shard,极大的提供的 Query 性能。

Optimize Notify: async to sync

TCP 是一个异步请求,它不像 HTTP,TCP 只要把数据推送到网卡就会返回成功,那如果将下行通知与上行返回的应答进行关联呢?

我们通过实现 Futre 自定义 NotifyFutre,为每个下行通知分配一个 seq,并定义 NotifyFutre 的 timeout。即每个下行通知分配一个 seq 存储缓存中,等待客户端回应这个应答,如果应答, 则从缓存移出这个 seq,否则等待超时,自动从缓存中被移出。


Authour

Frank

2009年参加工作,拥有7年IT开发工作经验,对高性能高可用服务端开发有丰富的开发经验,熟悉PC/Mobile客户端开发模式。

精通Java、Scala、JavaScript、Netty编程,熟悉Linux系统;熟悉Hadoop分布式计算,Zookeeper及HBase、Redis、ElasticSearch等NoSQL数据库。

2011年接触敏捷,拥有4年作为团队Scrum Master的工作经验,熟悉Scrum、Kanban、XP和CI等敏捷开发模式。

拥有Scrum&Kanban&ScrumBan国际认证,曾在敏捷之旅、PMI敏捷沙龙和TiD AgileChina进行敏捷专场的分享。


本文受原创保护,未经作者授权,禁止转载。 linkedkeeper.com (文/张松然)  ©著作权归作者所有