Skip to main content

通讯模型

介绍

本篇,我们将介绍框架提供的一些通讯模型。通常用于客户端与服务器通信、或服务器内部之间的通信。

在客户端的角度

在客户端的角度,我们提供了如下的通讯模型

  • request/response,请求/响应
  • request/void,请求/无响应
  • request/broadcast,请求/广播响应
  • broadcast,广播

request/response,请求/响应

当客户端发起请求后,action 会返回数据。

@ActionController(1)
public final class MyAction {
@ActionMethod(1)
public String hello(String name) {
return "hello, " + name;
}
}

客户端使用,下面代码通过 SDK C# + 代码生成完成的联调。

// C# Code
public void TestHello()
{
var name = "Michael Jackson";

MyAction.OfHello(name, result =>
{
var data = result.GetString();
result.Log(data);
});
}

request/void,请求/无响应

当客户端发起请求后,action 不会响应任何数据。

@ActionController(1)
public final class MyAction {
@ActionMethod(2)
public void say(String name) {
... your biz
}

}

客户端使用

// C# Code
public void TestSay()
{
var name = "Michael Jackson";
MyAction.OfSay(name);
}

request/broadcast,请求/广播响应

当客户端发起请求后,action 不会响应任何数据,而是通过广播的方式来发送数据。

@ActionController(1)
public final class MyAction {
@ActionMethod(2)
public void say(String name, FlowContext flowContext) {
...
flowContext.broadcast(cmd, StringValue.of("hello"));
}

}

客户端使用

// C# Code
public void TestSay()
{
var name = "Michael Jackson";
MyAction.OfSay(name);

var cmd = ...
ListenCommand.Of(cmd, result =>
{
var value = result.GetString();
});
}

broadcast,广播

广播,由服务器主动把数据广播给客户端

@ActionController(1)
public final class MyAction {
void test1() {
var data = StringValue.of("my broadcast data");
var cmdInfo = CmdInfo.of(2, 2);

var context = BrokerClientHelper.getBroadcastContext();
context.broadcast(cmdInfo, data);
}

// or
void test2(FlowContext flowContext) {
var data = StringValue.of("my broadcast data");
var cmdInfo = CmdInfo.of(2, 2);
flowContext.broadcast(cmdInfo, data);
}
}

客户端使用

// C# Code
public void TestLoginVerify()
{
var cmd = ...
ListenCommand.Of(cmd, result =>
{
var value = result.GetString();
});
}
tip

上面客户端的代码由 ioGame 生成,在实际开发中通常会使用代码生成。

具体请阅读: here

内部通讯

内部通讯主要用于服务器内部之间的通信,跨服、跨进程通信。我们提供了如下的通讯模型

request/response,请求/响应

游戏逻辑服之间的请求,下面两个方法是等价的。

@ActionController(3)
public final class YourAction {
@ActionMethod(1)
public void testHello(FlowContext flowContext) {
// call MyAction.hello method
var cmd = CmdInfo.of(1, 1);
var name = StringValue.of("Michael Jackson");
var responseMessage = flowContext.invokeModuleMessage(cmd, name);
var data = responseMessage.getString();
}

void testHello2() {
var cmd = CmdInfo.of(1, 1);
var name = StringValue.of("Michael Jackson");

var context = BrokerClientHelper.getInvokeModuleContext();
var responseMessage = context.invokeModuleMessage(cmd, name);
var data = responseMessage.getString();
}
}

request/void,请求/无响应

游戏逻辑服之间的请求,下面两个方法是等价的。

@ActionController(3)
public final class YourAction {
@ActionMethod(1)
public void testHello(FlowContext flowContext) {
// call MyAction.say method
var cmd = CmdInfo.of(1, 2);
var name = StringValue.of("Michael Jackson");

flowContext.invokeModuleVoidMessage(cmd, name);
}

void testHello2() {
var cmd = CmdInfo.of(1, 2);
var name = StringValue.of("Michael Jackson");

var context = BrokerClientHelper.getInvokeModuleContext();
context.invokeModuleVoidMessage(cmd, name);
}
}
warning

request/void 模型是异步调用的。

request/multiple_response,请求同类型多个逻辑服

request/multiple_response 是游戏逻辑服之间的请求,能同时请求同类型多个游戏逻辑服。

warning

request/multiple_response 模型必须要有返回值,被调用端的 action 不能是 void。

如果只想触发同类型多个游戏逻辑服的 action,可以考虑使用 EventBus。


场景举例

框架支持启动多个相同的游戏逻辑服,比如 游戏逻辑服-B-1、游戏逻辑服-B-2

有时,我们需要同时访问多个相同游戏逻辑服的 action, 框架支持访问【同类型】的多个游戏逻辑服,并把多个相同游戏逻辑服的结果收集到一起。

下面两个方法是等价的

@ActionController(3)
public final class YourAction {
@ActionMethod(1)
public void testHello(FlowContext flowContext) {
// call MyAction.hello method
var cmd = CmdInfo.of(1, 1);
var name = StringValue.of("Michael Jackson");
flowContext.invokeModuleCollectMessage(cmdInfo, name, responseCollectMessage -> {
var messageList = responseCollectMessage.getMessageList();

for (ResponseCollectItemMessage itemMessage : messageList) {
var data = itemMessage.getString();
}
});
}

void testHello2() {
var cmd = CmdInfo.of(1, 1);
var name = StringValue.of("Michael Jackson");

var context = BrokerClientHelper.getInvokeModuleContext();
var responseCollectMessage = context.invokeModuleCollectMessage(cmd, name);
var messageList = responseCollectMessage.getMessageList();

for (ResponseCollectItemMessage itemMessage : messageList) {
var data = itemMessage.getString();
}
}
}

EventBus,分布式事件总线

分布式事件总线是通讯方式之一,该通讯方式与 Guava EventBus、Redis 发布订阅、MQ ... 等产品类似。


Subscriber 订阅者

@Slf4j
@EventBusSubscriber
public class EmailEventBusSubscriber {
@EventSubscribe
public void mail(UserLoginEventMessage message) {
log.info("{}", message.userId);
}
}

@Slf4j
@EventBusSubscriber
public class UserEventBusSubscriber {
@EventSubscribe
public void userLogin(UserLoginEventMessage message) {
log.info("{}", message.userId);
}
}

public class UserLoginEventMessage implements Serializable {
public final long userId;

public UserLoginEventMessage(long userId) {
this.userId = userId;
}
}

fire 发布事件

发布事件后,上面两个订阅者将会接收到消息。

@ActionController(UserCmd.cmd)
public class UserAction {
@ActionMethod(UserCmd.fireEvent)
public void fireEventUser(FlowContext flowContext) {
long userId = flowContext.getUserId();
var message = new UserLoginEventMessage(userId);
flowContext.fire(message);
}
}

访问游戏对外服

tip

访问游戏对外服, 是框架提供的通讯方式之一,主要用于游戏逻辑服与游戏对外服的交互上。

javadoc - InvokeExternalModuleContext

void test1() {
...
int bizCode = ...
var collectExternalMessage = flowContext
.invokeExternalModuleCollectMessage(bizCode);
}

void test1() {
...
int bizCode = ...
var context = BrokerClientHelper.getInvokeExternalModuleContext();
var collectExternalMessage = context.invokeExternalModuleCollectMessage(bizCode);
}

如何扩展

这些通讯方式都是通过扩展实现的,参考已有的实现类,你可以扩展任意你所需要的通讯方式来满足你的特殊业务。

传统框架想要实现类似的通讯功能,只能借助大量的第三方中间件,而 ioGame 则无需任何中间件就能实现这些通讯功能。 这意味着在使用上简单了,在部署上也为企业减少了部署成本、维护难度。

一般传统的框架只提供了接收请求,当请求处理完后使用广播的方式将数据响应给请求端。 但在使用 ioGame 时,不要被过去的传统框架束缚住,可以做任何大胆的设计,因为通讯方式足够丰富。

框架对这些通讯方式提供了代码调用点的日志,简单点说就是框架可以让开发者知道,是在哪一行代码中触发的业务逻辑。

我们可以想象一下,假如框架没有提供代码调用点的日志会是什么样的。 比如,游戏前端发送一个业务请求到游戏服务器中,但是处理这个请求的业务方法,会触发多个响应(通常是广播)给游戏前端。 一但时间久了,开发者是很难知道分别响应了哪些业务数据给游戏前端,特别是一些二手项目。 所以这将是一个灾难性的问题,因为这会耗费大量的时间来寻找这些相关的业务代码。