在前面的章节中,我们已经实现了资产模块、订单模块、撮合引擎和清算模块。接下来,我们将把这些模块组合起来,构建一个完整的交易引擎。本文将详细介绍如何实现这一过程。
1. 交易引擎基本架构
我们首先定义交易引擎服务 TradingEngineService
,它集成了四个主要模块:AssetService
、OrderService
、MatchEngine
和 ClearingService
,它们分别负责资产管理、订单管理、撮合以及清算。
java
public class TradingEngineService {
@Autowired
AssetService assetService;
@Autowired
OrderService orderService;
@Autowired
MatchEngine matchEngine;
@Autowired
ClearingService clearingService;
}
2. 事件驱动的交易引擎
交易引擎是基于事件驱动的架构来处理消息的。我们使用Kafka来接收消息队列中的事件,通过订阅Topic来批量读取消息,并依次处理每个事件。
2.1 消息处理方法
首先,定义一个处理消息的方法 processMessages()
,接收一批消息,并逐个事件进行处理:
java
void processMessages(List<AbstractEvent> messages) {
for (AbstractEvent message : messages) {
processEvent(message);
}
}
每个事件会交给 processEvent()
方法进行处理。我们目前处理的事件包括订单请求、订单取消以及资金转移。
2.2 事件处理方法
java
void processEvent(AbstractEvent event) {
if (event instanceof OrderRequestEvent) {
createOrder((OrderRequestEvent) event);
} else if (event instanceof OrderCancelEvent) {
cancelOrder((OrderCancelEvent) event);
} else if (event instanceof TransferEvent) {
transfer((TransferEvent) event);
}
}
3. 创建订单
在事件类型为 OrderRequestEvent
时,我们需要根据订单信息来生成订单,并进行撮合和清算。
3.1 创建订单
以下是创建订单的核心方法。它通过从 OrderService
创建订单,然后通过 MatchEngine
进行撮合,最后调用 ClearingService
来完成清算。
java
void createOrder(OrderRequestEvent event) {
// 生成订单ID
long orderId = event.sequenceId * 10000 + (year * 100 + month);
// 创建订单
OrderEntity order = orderService.createOrder(event.sequenceId, event.createdAt, orderId, event.userId, event.direction, event.price, event.quantity);
if (order == null) {
logger.warn("create order failed.");
return;
}
// 撮合
MatchResult result = matchEngine.processOrder(event.sequenceId, order);
// 清算
clearingService.clearMatchResult(result);
}
3.2 异步处理已完成订单
在清算后,我们还需要处理已完成的订单。为了提高系统性能,避免阻塞主线程,我们将已完成的订单加入队列中,然后在异步线程中处理它们。
java
Queue<List<OrderEntity>> orderQueue = new ConcurrentLinkedQueue<>();
void createOrder(OrderRequestEvent event) {
...
// 清算完成后,收集已完成订单
if (!result.matchDetails.isEmpty()) {
List<OrderEntity> closedOrders = new ArrayList<>();
if (result.takerOrder.status.isFinalStatus) {
closedOrders.add(result.takerOrder);
}
for (MatchDetailRecord detail : result.matchDetails) {
OrderEntity maker = detail.makerOrder();
if (maker.status.isFinalStatus) {
closedOrders.add(maker);
}
}
this.orderQueue.add(closedOrders);
}
}
// 启动一个线程将orderQueue的订单异步写入数据库
void saveOrders() {
// TODO: 异步保存已完成的订单
}
4. 消息去重与缺失消息处理
为了保证交易引擎的鲁棒性,我们需要处理消息的去重和丢失。如果接收到的消息是重复的,我们应该跳过它;如果消息丢失,我们需要从数据库中加载缺失的消息。
4.1 处理重复消息
java
void processEvent(AbstractEvent event) {
if (event.sequenceId <= this.lastSequenceId) {
logger.warn("skip duplicate event: {}", event);
return;
}
// 继续处理
}
4.2 处理丢失的消息
如果检测到有消息丢失,我们需要根据 previousId
来判断,并加载缺失的事件:
java
if (event.previousId > this.lastSequenceId) {
List<AbstractEvent> events = storeService.loadEventsFromDb(this.lastSequenceId);
if (events.isEmpty()) {
System.exit(1); // 丢失消息严重,退出
return;
}
// 处理丢失的消息
for (AbstractEvent e : events) {
this.processEvent(e);
}
return;
}
5. 交易引擎的状态验证
为了确保交易引擎的内部状态在每次事件处理后都保持一致,我们可以在 debugMode
下自动验证每个子系统的状态完整性。
java
void processEvent(AbstractEvent event) {
...
if (debugMode) {
this.validate(); // 验证内部状态
}
}
5.1 验证内容
-
资产系统总额为零,且除负债账户外,其余账户的资产不可为负。
-
订单系统中的未成交订单所冻结的资产与资产系统中的冻结金额一致。
-
订单系统的订单与撮合引擎中的订单簿一对一对应。
6. 交易引擎的崩溃恢复
交易引擎如果崩溃,我们可以通过重启来恢复状态。重启后,我们从最新的状态文件恢复并重新执行事件。
6.1 状态序列化
为了避免交易引擎崩溃后丢失状态,我们可以定期将交易引擎的状态序列化到文件中。例如,每隔10分钟序列化一次。
json
{
"sequenceId": 189000,
"assets": { ... },
"orders": [ ... ],
"match": { ... }
}
交易引擎启动时,读取序列化的状态文件并恢复系统状态。
7. 小结
在本文中,我们介绍了如何实现一个完整的交易引擎系统,涵盖了事件驱动模型、消息处理、订单创建、清算以及交易引擎崩溃恢复等关键技术。通过事件驱动的架构,我们可以保证交易引擎的健壮性,并且能够在崩溃后快速恢复交易状态。