升讯威微信营销系统开发实践:(2)中控服务器的详细设计( 完整开源于 Github)
GitHub:https://github.com/iccb1013/Sheng.WeixinConstruction
因为个人精力时间有限,不会再对现有代码进行更新维护,不过微信接口比较稳定,经测试至今没有变化,功能依然全部可用,你可以在此基础上,二次开发,完成你的业务功能,也可以抽取本平台中的代码复用在你的项目中,请遵循 MIT 开源协议保留我的版权声明和网站链接即可。
GitHub:https://github.com/iccb1013/Sheng.WeixinConstruction.WeixinContract
微信协议包装的项目还有一个单独的工程,这个工程的版本稍新,我会进行一定的更新维护,如最近增加了几个小程序开发需要使用到的接口。但是注意因为代码结构经过优化调整,直接引用到升讯威微信平台中,需要修改一些类的引用和名称。
升讯威微信营销系统开发实践系列
升讯威微信营销系统开发实践:(1)功能概要与架构设计
升讯威微信营销系统开发实践:(2)中控服务器的详细设计
升讯威微信营销系统开发实践:(3)功能介绍与此项目推广过程的一些体会
升讯威微信营销系统开发实践:(4)源代码结构说明 与 安装部署说明
在上一篇文章中,简要介绍了升讯威微信营销系统的功能设计和架构设计,限于篇幅只能抛砖引玉,从本章节开始将围绕功能的设计和架构的设计进行详细的论述。
中控服务器的设计
在上文中,我们谈到需要一个中控服务器,用来维护公众号的AccessToken等。本节首先围绕这个内容讨论。
背景
微信接口调用需要首先获取一个AccessToken进行鉴权,目前每次获取的AccessToken有效期为2个小时,每天获取次数限制在2000次以下。当然我们不需要如此频繁的请求新的AccessToken,我们只需要把它存储起来,在过期前刷新即可。
对于只为单个公众号服务的系统,中控服务器的设计实现可以从简,只需定时刷新AccessToken,并为应用服务器提供获取AccessToken的接口即可。
对于直接将AccessToken写入Redis,在我实践过程中发现不能100%保证写入成功,存在写入失败,数据没有更新的情况,这个问题是致命的,因为一旦获取了新的AccessToken,旧的就会很快失效,如果不能写入成功,应用服务器继续使用旧的AccessToken,将导致微信API调用全部失败。
我没有找到相关资料说明是否Redis写入机制不保证一定成功?如果有熟悉Redis的大神请指教,谢谢。
对于需要同时服务于多个公众号的系统,中控服务器的设计要稍微复杂一些。
我们现在将问题放大一些,在升讯威微信营销系统中,所需要维护和管理的数据很多,其中有一些数据,如公众号的基本信息数据、个性化设置数据、授权数据、以及各种AccessToken、Ticket等,我将之打包称之为“公众号上下文”。中控服务器除了维护AccessToken,更重要的作用是维护起整个公众号上下文。
设计目标
概括来说中控服务器有以下几个职责:
1) 维护鉴权所需的各种AccessToken、Ticket。
2) 维护基本信息、授权信息等基础数据。
3) 提供一套供应用服务器使用的API,用于同步用户的个性化设置及其它需要动态更新的数据。
4) 提供基于 TCP/IP 的接口以提高通信能力。
5) 可通过增加服务器横向扩展以提高承载能力。
架构设计
这里的架构设计是针对上一篇《功能设计及架构设计》中“架构设计”环节里中控服务器部分的展开。
中控服务器的具体实现方法我想放到后面的章节中介绍,本篇专注于架构的设计。
中控服务器最重要的核心即“域容器”。
对于我们的系统来说,要提供承载多个公众号(100000+)的能力,所以需要将不同公众号的上下文数据组织好之后,放到一个域容器中进行维护。为什么这里叫做“域容器”而不叫做“公众号容器”?这里为了方便论述,我对相关的设计做了简化,实际上单个公众号上下文是被包含在一个“域”中的,“域”除了承载公众号上下文数据之外,还需要承载并维护其它一些数据。
中控服务器在维护公众号上下文时,有多种架构设计方案,根据项目的技术指标,由易到难。
入门级
入门级只需要单台中控服务器即可,维护所有的公众号上下文,通过Http Web API 接口向应用服务器提供服务。
可以适用于一般小规模服务,如果中控服务器Out of Service,需要运维团队及时响应,重启服务或服务器,数据可以在启动时从缓存中恢复,但是会造成短时间的服务中断。
门厅级
鉴于中控服务器的重要性,我们不能接受它出现问题,我们的目标是达到服务99.999% 以上的可用时间。
进一步的方案是对中控服务器进行冗余,使用多台服务器维护公众号上下文并提供服务。
在此方案中,每台中控服务器所维护的数据是一致的,都是彼此的副本。
规模不大时已经适用了,可以将部分接口基于TCP/IP来实现,主要是为应用服务器提供AccessToken的接口,请求频率会非常高。
如果系统所需要服务的公众号数量非常多,达到数万甚至数十万,单台中控服务器的承载能力将受到很大影响,甚至根本不能承受,我们需要对公众号上下文数据的维护进行拆分。
客厅级
在客厅级方案中,最重要的目的是拆分中控服务器对公众号上下文的维护,使系统的整体承载能力得到提高,并使之具有横向扩展的能力。
举例来说中控服务器1维护第1~3000个公众号上下文,中控服务器2维护第3001~6000个公众号上下文。具体维护数量根据服务器配置和网络情况,以及你的中控服务器实现质量决定。
中控服务器对公众号上下文的维护的拆分,带来的问题当然是应用层怎么样才能找到指定公众号所需的上下文数据在哪台服务器上呢?偷懒的方案是在公众号对接时直接定死,但是这个方案很容易造成运维阶段的成本浪费,因为公众号的活跃度是不同的,有很多公众号在对接后可能长时间处于不活动状态。而且定死的话你虽然能通过横向扩展增加服务的承载能力,但是没有办法通过增加单台服务器的配置提高既有服务器的承载能力。所以在我们的具体实现中,中控服务器对于公众号上下文的加载和维护都是懒加载的,单台可加载数量可以根据运维期的服务器配置进行调整。
如何使应用层的服务器找到指定公众号上下文在哪台服务器上?其实很简单,增加一个中控服务器路由即可,这个路由服务器必须知道公众号上下文和中控服务器的对应关系,知道它在哪里。是不是每次请求中控服务器都需要经过路由服务器去定位哪?并不需要这样做,只需要在应用层服务器初次定位时到路由服务器上查询即可,后续的通信直接与中控服务器进行。
卧室级
客厅级的加强版,对所有中控服务器进行一对一的冗余,以防单台故障Out Of Service。
其实这里有一个技术点是要注意的,AccessToken的同步问题,这个问题在上文的门厅级方案中也是一样存在的,冗余的服务器之间如何确保AccessToken是同步的,当然不能各自去调用微信API刷新,后刷新的就把先刷新的冲掉了,所以需要把刷新AccessToken的工作放到中控服务器对外代理中,在其中实现一个队列来处理这件事,并在得到新的AccessToken后,分发到中控服务器。
阳台级
生活不只是眼前的苟且,还有远方的苟且等着你,慢慢来……
中控服务器的设计实现可以采取不断演进的方式进行,可以随着运维规模的扩大适时迭代,但是必须在前期规划设计时考虑到这些问题,特别是横向扩展的能力。