背景

在当今的互联网环境中,无法无天的瞬间流量与捉襟见肘的硬件资源是很难调和的矛盾(当前,土豪玩家往往会有数十倍乃至数百倍的预留硬件)。那么,作为一个程序员,你需要做的事情就是让这有限的硬件尽可能多的Hold住更多流量。

在笔者的实践中,主要总结了以下几个方向去完成我们的目标。

  • 性能优化。
    • 应用程序性能优化
    • 数据库性能优化
    • 业务逻辑的优化
  • 流量控制
    • 系统流量控制
    • 租户流量控制
  • 流量优先
    • 将流量分为同步流量与可异步流量

由于篇幅问题,本次主要讨论流量优先。

什么是流量优先?

这里,我们举一个最常见的互联网业务的例子来说明什么是优先流量。

假如,我们的业务是这样的:

  1. 用户点击某个按钮,发起Ajax请求
  2. 在请求内需要查询数据库来进行身份验证
  3. 需要向数据库更新一条记录
  4. 需要将该操作日志写入数据库
  5. 需要将该操作进行远程通知(HTTP POST请求)
  6. 需要将该通知结果写入数据库
  7. 告知用户数据更新结果。

对于这样的业务,我们首先需要考虑的是,哪些操作是本次请求相关的,哪些操作不影响本次请求结果,但是与系统相关的。

很显然的,步骤1 2 3 7 是请求相关的,是影响最终结果的。那么,我们认为这些操作是同步逻辑(优先操作),其余操作在资源有限的情况下,需要让位于该操作(示例中的业务,由于同步逻辑简单,异步化处理之后,该接口的响应性能会有数倍提升)。

如何实现同步流量与异步流量

任务队列。这是一个毋庸置疑的答案。而且有很多成熟的开源系统可供选择。但是,这些系统都是已一个TCP服务进行通信与服务的,那么,这里又会有一个潜在的问题。如果任务队列挂了怎么办?

笔者以另外一个角度思考了异步队列的实现。我们需要把我们的任务推送到统一的消息存储中吗?本地任务队列有什么优缺点(这里的本地不是指代localhost或127.0.0.1,而是指应用程序内部)?

本地任务队列

本地任务队列指的是在应用程序内部实现的任务队列系统。

  • 优点

    • 安全,可用性高。没有tcp服务带来的连接,网络等问题造成的消息丢失现象。同时,由于处理的是本地任务(函数指针),也不会有任务处理的不确定性。
  • 缺点

    • 无法支持全局状态查询。当我们的应用被部署多份的时候,我们不能简单的获知任务状态。
    • 应用程序带来了状态。由于应用程序需要维护自己的任务队列,带来了状态。这样对应用的更新,部署带来了问题。

本地任务队列就不可用了吗?

关于缺点1,我们可以引入MySQL,Redis等存储引擎来完成全局状态的迁移。

由于缺点2的存在,我们的应用在实例中销毁需要等到任务队列执行完毕之后(但是是支持停机维护的)。

笔者认为,本地任务队列可以完全替代或部分替代远程任务队列系统。部分替代指的是与远程任务队列配合工作(比如做远程任务队列的推送失败重试队列)。

LiteJob

LiteJob是笔者关于本地任务队列的是一个思考结果。使用Golang实现。最核心的还是Golang的协程系统本身就是良好的控制系统

特性

  • 支持持久化到本地,防止因应用崩溃造成消息的丢失。
  • 支持并发处理任务以及并发量控制。
  • 支持任务重入(依赖任务返回参数)。

开发目标

  • 优化调度系统,控制CPU资源消耗
  • 优化持久化,支持秒级持久化
  • 优化消息存储,同时使用文件和内存两种方式用以支撑更多的消息。