tornado 实时服务架构设计

背景介绍

这是在公司训练的一个小项目。
每分钟需要从tushare的接口中获取到实时的股价数据,存储到文件中和mongodb中,然后对数据进行处理,包括数据检查,分钟线的补齐等。经过以上的处理后,程序需要对外提供API,共web,app等终端使用。

股价数据每分钟都在改变,api的调用次数和频率非常高,因此服务架构在设计的时候需要满足以下几点:

1、容错性能好:
至少要保证在开盘阶段不出问题,由于tushare的接口可能返回不了数据或着请求返回超时等,需要及时的对股票的分钟数据进行补全,如第一分钟获取到一条数据,然后第5分钟才来第二条数据,那么中间的三分钟都需要进行补齐。
假设服务一旦挂掉了,也要求服务重启之后能够及时的恢复数据,继续对外提供数据。

2、实时响应请求:
应用每分钟都要向api请求数据,如果请求被阻塞了,就会影响后续的请求进来。因此要求api能够实时响应,不阻塞IO,并发高,假设并发数达到3w/s。

现阶段的设计先满足以上两点即可,后续在不断的改进。根据上面的分析,考虑到最近在学习python,我选择了tornado。

tornado介绍

tornado和现在的主流 Web 服务器框架(包括大多数 Python 的框架)有着明显的区别:它是非阻塞式服务器,而且速度相当快。得利于其非阻塞的方式和对 epoll 的运用,tornado 每秒可以处理数以千计的连接,这意味着对于实时 Web 服务来说,tornado 是一个理想的 Web 框架。并且tornado内置了一个服务器,开发效率高。虽然tornado实现了非阻塞的网络请求,但是其仍然是单线程,在做耗时的CURD任务,仍然会阻塞进程,导致外部访问变慢。

架构的设计

从整体上来看,要支撑如此高的并发量,一个tornado肯定不行的,因此需要多个torando,再加上用nginx做负载均衡,这样就可以支撑高并发。

具体每个tornado的设计就有两种方案。

方案一

在tornado运行一个定时任务,然后每分钟取数据,先更新内存,然后写入文件中,最后写入mongo。

按照这种设计方案,一个tornado干了所有的事,数据采集,数据存储,数据更新,对外提供api 。先来看下Tushare实时数据采集耗时统计。

从图中可以看到:最高28s,平均值为9s。

tornado在执行该任务时会阻塞线程,如果后台正在进行请求任务时,此时来了api请求,那么这个请求就会被挂起,直到后台的数据采集处理完毕后才会对api请求处理。

方案二

我们让采集进程定时取采集数据,然后处理好了,推送给tornado,这样tornado只管对外提供数据,而不管取获取数据,让tornado专注于干自己擅长的事情,并且可以起一个后台进程,每10分钟可以将采集到的数据导入到mongo中。这种设计让每个进程都只专注于干一件事。

异常处理

整个设计中,程序随时可能会出问题,要保证运行稳定,异常处理部分是非常重要的一块。主要有以下几点:
1、网络请求:每分钟从tushare获取股价数据,可能会遇到请求超时,数据错误等异常。采取的解决措施主要有超时重试,数据补齐措施
2、 文件、数据库写入异常:遇到文件,数据库写入异常时,要保证数据存储成功,应该保存重试几次和错误日志记录等,方便后续对错误的处理
3、 服务重启:后台检测到服务挂掉之后,自动重启服务,先从数据库中加载数据,处理后保存在内存中,如果从数据库加载失败,在从文件中读取数据,保证服务的可用性

总结

通过这个小项目的练习,我对框架的选择,方案的设计,数据的获取,存储,处理和对外提供api都有了更加深入的了解。

框架的选择要紧贴业务的需要,能够解决实际的问题。首先要仔细的分析业务的需求是什么,存在什么样的问题,要达到什么样的目标,通过对问题的分析,我们才能清楚需要什么。然后调研分析有哪些可以使用的框架,每种框架的特点是什么,缺点是什么,能否解决现阶段的问题,学习成本高低。通过这些分析,才能选择适合的框架解决问题。

之前在写程序的时候,大多数都不会去处理异常,认为程序会一直按照这个逻辑正确的运行下去,现在认识到,程序设计的每个阶段都需要充分的考虑到异常的处理,才能保证程序能够稳定的运行。