随着加密货币适应性的上升,加密货币交易所的需求也越来越大,大量的新代币被投放到交易所以展示它们的巨大潜力和吸引用户。
交易所平台由许多组件组成,这些组件为了实现功能而相互通信,但交易所中最关键和最中心的主要组件是匹配引擎,它能够同时接受多个订单、维护它们、执行计算并推出交易。
它之所以成为最关键的组件,是因为单次失败或任何错误的计算都可能使交易所面临风险,这些风险主要是在财务上破坏用户的信任。
我制作的初始匹配引擎是使用 node.js、typescript 和 rabbitMq,它运行非常顺利,没有延迟,也没有故障,但当企业级解决方案被要求将订单放置到更高的级别时,注意到交易的延迟,服务器崩溃消耗 100% CPU 容量,消息队列开始卡住。
当这些问题被检查出,就意味着这将不仅仅需要进行一个小的改变,而是必须重新设计和建立。
交易平台是按照微服务架构构建的,因此可以考虑和试验此类修改和重新设计。
了解到对于高数据负载和低延迟预期的高计算量,“节点”不是用于此级别处理的最佳技术。
一般来说,任何CPU密集型操作都将取消节点通过其事件驱动、非阻塞I/O模型提供的所有吞吐量优势,因为当线程被数字处理占用时,任何传入请求都将被阻塞。
当涉及到在多核服务器上添加并发性时,节点核心团队以集群模块的形式完成了一些工作。
但仍然使用集群,应该将所有繁重的计算都转移到在更合适的环境中编写的后台进程,并让它们通过消息代理相互通信。
选择GO LANG
选择一个多核系统并提高并发性并不能保证相应的速度提高,笨拙的并发性控制会使多核上的情况变得更糟。
Go为高可扩展并发控制提供了一些非常好的设计选择。
· 是 进程吗? 不
· 是 线程吗? 不
· 是 Go-例程? 是的
上下文管理
当涉及到资源共享和上下文管理时,以及并发任务共享处理器和内存时,通常需要处理的任务数量会超过可用的处理器。
它所需要处理的只是暂停和恢复执行,但是非常有效。
上下文切换过程需要很多昂贵的操作,如:
· 找出下一步要运行的进程,等待/挂起进程的管理
· 备份所有当前的CPU寄存器,恢复所有的CPU寄存器备份到下一个进程
· 刷新虚拟内存高速缓存
· 所有操作都在内核中运行,因此可以管理操作系统内核模式和用户模式之间的切换。
线程
线程与进程相似,但它们共享地址空间,因为线程比进程小。
线程比创建和切换游戏的进程快,但仍然存在上下文切换开销。
对于线程和进程,内核需要备份/恢复整个寄存器,因为内核不知道哪些寄存器正在使用。
线程在创建时为其堆栈分配固定的大小,而不考虑所需的堆栈大小,该大小限制了一次运行的并发任务的数量。
GO-例程
与线程相比,GO例程具有最小的上下文开销。
GO -例程还具有协作调度,会切换至最小化。它们只在定义良好的情况下进行上下文切换,如通道发送/接收操作、GO语句、阻塞系统调用(文件/网络IO)和垃圾收集。
如果工作方式不合作,饥饿就很可能发生。
Go编译器会发出代码用于对每个上下文切换事件进行寄存器检查和备份。
Go编译器知道给定函数需要什么堆栈大小。它首先分配一个非常小的堆栈,在函数调用之前,Go会检查当前堆栈是否能容纳函数堆栈大小要求,如果不够,它根据需要动态增加堆栈大小。反之亦然,它还可以缩小堆栈大小。因此,GO 例程可以保持必要的堆栈大小,并允许最大的并发GO例程。
Go在多核系统上很特别,这是由于它聪明的设计选择,它对于上下文管理非常便宜和快速,而GO例程的动态堆栈大小管理允许更多并发性。
比较节点引擎和GO引擎
构建新系统之后,是时候比较两个系统的可扩展性和效率了,结果令人兴奋。
Go引擎的结果是这样的
· 高并发
· 低延时
· 交易速度/吞吐量计算为14微秒
所有核心之间的CPU使用率都管理得很好
结论
选择简单有效的东西总是有效的。我本可以设计一个包含许多队列、后台工作程序等的复杂系统,但我决定利用Go-lang为我们提供的高效和简单的并发方法。
工作中总会有合适的工具,了解这些工具有助于做出最好的选择。