FlowContext 增加通信能力,提供同步、异步、异步回调的便捷使用
iohao opened this issue · comments
FlowContext 增加通信能力,提供同步、异步、异步回调的便捷使用
通讯方式特点:单个游戏逻辑服之间的交互 - 可接收响应
示例代码中提供了同步、异步回调、异步的编码风格。其中,异步回调写法具备全链路调用日志跟踪。
... ...省略部分代码
void invokeModuleMessage() {
// 同步
invokeModuleMessageSync();
// 异步回调
// --- 此回调写法,具备全链路调用日志跟踪 ---
invokeModuleMessageAsync();
// 异步
invokeModuleMessageFuture();
}
void invokeModuleMessageSync() {
// 路由
ResponseMessage responseMessage = flowContext.invokeModuleMessage(cmdInfo);
RoomNumMsg roomNumMsg = responseMessage.getData(RoomNumMsg.class);
log.info("同步调用 : {}", roomNumMsg.roomCount);
// 路由、请求参数
ResponseMessage responseMessage2 = flowContext.invokeModuleMessage(cmdInfo, yourData);
RoomNumMsg roomNumMsg2 = responseMessage2.getData(RoomNumMsg.class);
log.info("同步调用 : {}", roomNumMsg2.roomCount);
}
void invokeModuleMessageAsync() {
// --- 此回调写法,具备全链路调用日志跟踪 ---
// 路由、回调
flowContext.invokeModuleMessageAsync(cmdInfo, responseMessage -> {
RoomNumMsg roomNumMsg = responseMessage.getData(RoomNumMsg.class);
log.info("异步回调 : {}", roomNumMsg.roomCount);
});
// 路由、请求参数、回调
flowContext.invokeModuleMessageAsync(cmdInfo, yourData, responseMessage -> {
RoomNumMsg roomNumMsg = responseMessage.getData(RoomNumMsg.class);
log.info("异步回调 : {}", roomNumMsg.roomCount);
});
}
void invokeModuleMessageFuture() {
CompletableFuture<ResponseMessage> future;
try {
// 路由
future = flowContext.invokeModuleMessageFuture(cmdInfo);
ResponseMessage responseMessage = future.get();
} catch (InterruptedException | ExecutionException e) {
throw new RuntimeException(e);
}
// 路由、请求参数
future = flowContext.invokeModuleMessageFuture(cmdInfo, yourData);
}
通讯方式特点:单个游戏逻辑服之间的交互 - void
无阻塞、适合不需要接收响应的业务。被请求的 action 通常是声明为 void 的,因为知道是无响应的,所以只提供了无阻塞的调用。
示例代码
... ...省略部分代码
void invokeModuleVoidMessage() {
// 路由
flowContext.invokeModuleVoidMessage(cmdInfo);
// 路由、请求参数
flowContext.invokeModuleVoidMessage(cmdInfo, yourData);
}
通讯方式特点:请求同类型多个游戏逻辑服通信结果
可接收多个游戏逻辑服的响应
示例代码中提供了同步、异步回调、异步的编码风格。其中,异步回调写法具备全链路调用日志跟踪。
void invokeModuleCollectMessage() {
// 同步
this.invokeModuleCollectMessageSync();
// 异步回调
// --- 此回调写法,具备全链路调用日志跟踪 ---
this.invokeModuleCollectMessageAsync();
// 异步
this.invokeModuleCollectMessageFuture();
}
void invokeModuleCollectMessageSync() {
// 路由
ResponseCollectMessage response = flowContext.invokeModuleCollectMessage(cmdInfo);
for (ResponseCollectItemMessage message : response.getMessageList()) {
RoomNumMsg roomNumMsg = message.getData(RoomNumMsg.class);
log.info("同步调用 : {}", roomNumMsg.roomCount);
}
// 路由、请求参数
ResponseCollectMessage response2 = flowContext.invokeModuleCollectMessage(cmdInfo, yourData);
log.info("同步调用 : {}", response2.getMessageList());
}
void invokeModuleCollectMessageAsync() {
// --- 此回调写法,具备全链路调用日志跟踪 ---
// 路由、回调
flowContext.invokeModuleCollectMessageAsync(cmdInfo, responseCollectMessage -> {
List<ResponseCollectItemMessage> messageList = responseCollectMessage.getMessageList();
for (ResponseCollectItemMessage message : messageList) {
RoomNumMsg roomNumMsg = message.getData(RoomNumMsg.class);
log.info("异步回调 : {}", roomNumMsg.roomCount);
}
});
// 路由、请求参数、回调
flowContext.invokeModuleCollectMessageAsync(cmdInfo, yourData, responseCollectMessage -> {
log.info("异步回调 : {}", responseCollectMessage.getMessageList());
});
}
void invokeModuleCollectMessageFuture() {
CompletableFuture<ResponseCollectMessage> future;
try {
// 路由
future = flowContext.invokeModuleCollectMessageFuture(cmdInfo);
ResponseCollectMessage response = future.get();
log.info("异步 : {}", response.getMessageList());
} catch (InterruptedException | ExecutionException e) {
throw new RuntimeException(e);
}
// 路由、请求参数
future = flowContext.invokeModuleCollectMessageFuture(cmdInfo, yourData);
}
通讯方式特点:获取玩家所在的游戏对外服的数据与扩展
只会访问玩家所在的【游戏对外服】(即使启动了多个游戏对外服)。
示例代码中提供了同步、异步回调、异步的编码风格。其中,异步回调写法具备全链路调用日志跟踪。
void invokeExternalModuleCollectMessage() {
// 同步
this.invokeExternalModuleCollectMessageSync();
// 异步回调
// --- 此回调写法,具备全链路调用日志跟踪 ---
this.invokeExternalModuleCollectMessageAsync();
// 异步
this.invokeExternalModuleCollectMessageFuture();
}
void invokeExternalModuleCollectMessageSync() {
ResponseCollectExternalMessage response;
// 业务码(类似路由)
response = flowContext.invokeExternalModuleCollectMessage(bizCode);
log.info("同步调用 : {}", response);
// 业务码(类似路由)、请求参数
response = flowContext.invokeExternalModuleCollectMessage(bizCode, yourData);
log.info("同步调用 : {}", response);
}
void invokeExternalModuleCollectMessageAsync() {
// 业务码(类似路由)、回调
flowContext.invokeExternalModuleCollectMessageAsync(bizCode, response -> {
response.optionalAnySuccess().ifPresent(itemMessage -> {
Serializable data = itemMessage.getData();
log.info("异步回调 {}", data);
});
});
// 业务码(类似路由)、请求参数、回调
flowContext.invokeExternalModuleCollectMessageAsync(bizCode, yourData, response -> {
log.info("异步回调");
});
}
void invokeExternalModuleCollectMessageFuture() {
CompletableFuture<ResponseCollectExternalMessage> future;
try {
// 业务码(类似路由)
future = flowContext.invokeExternalModuleCollectMessageFuture(bizCode);
ResponseCollectExternalMessage response = future.get();
log.info("异步");
} catch (InterruptedException | ExecutionException e) {
throw new RuntimeException(e);
}
// 业务码(类似路由)、请求参数
future = flowContext.invokeExternalModuleCollectMessageFuture(bizCode, yourData);
log.info("异步");
}
通讯方式特点:分步式事件总线
事件发布后,除了当前进程所有的订阅者能接收到,远程的订阅者也能接收到(跨机器、跨进程)。可以代替 redis pub sub 、 MQ ,并且具备全链路调用日志跟踪,这点是中间件产品做不到的。
示例代码中提供了同步、异步的编码风格。
void eventBus() {
int userId = 100;
// 事件源
YourEventMessage yourEventMessage = new YourEventMessage(userId);
// 异步执行 - 事件发布后,只会交给 flowContext 关联的业务框架中的订阅者处理
flowContext.fireMe(yourEventMessage);
// 同步执行 - 事件发布后,只会交给 flowContext 关联的业务框架中的订阅者处理
flowContext.fireMeSync(yourEventMessage);
// 异步执行 - 事件发布后,当前进程所有的订阅者都会接收到。(同进程启动了多个逻辑服)
flowContext.fireLocal(yourEventMessage);
// 同步执行 - 事件发布后,当前进程所有的订阅者都会接收到。(同进程启动了多个逻辑服)
flowContext.fireLocalSync(yourEventMessage);
// 异步执行 - 除了当前进程所有的订阅者能接收到,远程的订阅者也能接收到(跨机器、跨进程)
flowContext.fire(yourEventMessage);
}
大多数情况下,在发布事件时,使用 fire 方法就足够了。因为除了当前进程所有的订阅者能接收到,远程的订阅者也能接收到(跨机器、跨进程)
需要注意的是,如果没有任何远程订阅者,将不会触发网络请求。简单的说,事件发布后,当其他进程(其他机器)没有相关订阅者时,只会在内存中传递事件给当前进程的相关订阅者。所以,可以将该通讯方式当作 guava EventBus 来使用。
通讯方式:广播(推送)
示例代码中展示了
- 全服广播
- 指定单个用户广播
- 指定多个用户广播
- 给自己发送广播(提供了两种便捷的使用方式,分别是: 1.可指定路由的方式,2.复用当前 action 路由的方式)
@ActionMethod(1)
public void broadcast(FlowContext flowContext) {
// 全服广播 - 路由、业务数据
flowContext.broadcast(cmdInfo, yourData);
// 广播消息给单个用户 - 路由、业务数据、userId
long userId = 100;
flowContext.broadcast(cmdInfo, yourData, userId);
// 广播消息给指定用户列表 - 路由、业务数据、userIdList
List<Long> userIdList = new ArrayList<>();
userIdList.add(100L);
userIdList.add(200L);
flowContext.broadcast(cmdInfo, yourData, userIdList);
// 给自己发送消息 - 路由、业务数据
flowContext.broadcastMe(cmdInfo, yourData);
// 给自己发送消息 - 业务数据
// 路由则使用当前 action 的路由。
flowContext.broadcastMe(yourData);
}
通讯方式:顺序 - 广播(推送)
示例代码中展示了
- 全服广播
- 指定单个用户广播
- 指定多个用户广播
- 给自己发送广播(提供了两种便捷的使用方式,分别是: 1.可指定路由的方式,2.复用当前 action 路由的方式)
@ActionMethod(2)
public void broadcastOrder(FlowContext flowContext) {
// 顺序 - 全服广播 - 路由、业务数据
flowContext.broadcastOrder(cmdInfo, yourData);
// 顺序 - 广播消息给单个用户 - 路由、业务数据、userId
long userId = 100;
flowContext.broadcastOrder(cmdInfo, yourData, userId);
// 顺序 - 广播消息给指定用户列表 - 路由、业务数据、userIdList
List<Long> userIdList = new ArrayList<>();
userIdList.add(100L);
userIdList.add(200L);
flowContext.broadcastOrder(cmdInfo, yourData, userIdList);
// 顺序 - 给自己发送消息 - 路由、业务数据
flowContext.broadcastOrderMe(cmdInfo, yourData);
// 顺序 - 给自己发送消息 - 业务数据
// 路由则使用当前 action 的路由。
flowContext.broadcastOrderMe(yourData);
}
该广播会严格按照顺序发送(使用单线程处理),如果不是极端的业务,建议使用 broadcast 系列方法。
通常情况下,在使用 broadcast 系列方法时,同一用户业务处理的时间间隔通常会 > 2 ms ,有这点时间的间隔,用户接收到消息通常都是理论有序的。(游戏客户端每秒刷 60 帧,每帧也需要大约 16.6 ms)