Skip to main content

通信模型

介绍

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

在客户端的角度

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

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

request/response,请求/响应

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

@ActionController(1)
public final class MyAction {
@ActionMethod(1)
private 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)
private 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)
private void say(String name, FlowContext flowContext) {
String dataString = "ionet";
CommunicationKit.getCommunication().broadcastMulticast(cmd, dataString);
}

}

客户端使用

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

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

broadcast,广播

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

public final class MyAction {
void test1() {
String dataString = "ionet";
CommunicationKit.getCommunication().broadcastMulticast(cmd, dataString);
}
}

客户端使用

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

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

具体请阅读: here

内部通信

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

request/response,请求/响应

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

// By CommunicationKit
@ActionController(CallCmd.cmd)
public class CallAction {
@ActionMethod(CallCmd.callString)
private String callString() {
// The two below are equivalent, one is synchronous and the other is asynchronous.
var cmdInfo = InternalCmd.of(InternalCmd.stringAction);
String data = "hello";

// Asynchronous callback
communication().callback(cmdInfo, data, response -> {
log.info("{}", response.getString());
});

// Synchronous call
var response = communication().call(cmdInfo, data);
return response.getString();
}

private Communication communication() {
return CommunicationKit.getCommunication();
}
}

// By FlowContext
@ActionController(FlowContextCallCmd.cmd)
public class FlowContextCallAction {
@ActionMethod(FlowContextCallCmd.callString)
private String callString(FlowContext flowContext) {
// The two below are equivalent, one is synchronous and the other is asynchronous.
var cmdInfo = InternalCmd.of(InternalCmd.stringAction);
String data = "hello";

// Asynchronous callback
flowContext.callback(cmdInfo, data, response -> {
log.info("{}", response.getString());
});

// Synchronous call
var response = flowContext.call(cmdInfo, data);
return response.getString();
}
}

request/void,请求/无响应

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

// By CommunicationKit
@ActionController(SendCmd.cmd)
public class SendAction {
private Communication communication() {
return CommunicationKit.getCommunication();
}

@ActionMethod(SendCmd.sendString)
private boolean sendString() {
var cmdInfo = InternalCmd.of(InternalCmd.sendStringAction);
String data = "hello";

communication().send(cmdInfo, data);
return true;
}
}

// By FlowContext
@ActionController(FlowContextSendCmd.cmd)
public class FlowContextSendAction {
@ActionMethod(FlowContextSendCmd.sendString)
private boolean sendString(FlowContext flowContext) {
var cmdInfo = InternalCmd.of(InternalCmd.sendStringAction);
String data = "hello";

flowContext.send(cmdInfo, data);
return true;
}
}
warning

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

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

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

warning

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

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


场景举例

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

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

下面两个方法是等价的

// By CommunicationKit
@ActionController(CallCollectCmd.cmd)
public class CallCollectAction {
private Communication communication() {
return CommunicationKit.getCommunication();
}

@ActionMethod(CallCollectCmd.callCollectString)
private List<String> callString() {
// The two below are equivalent, one is synchronous and the other is asynchronous.
var cmdInfo = InternalCmd.of(InternalCmd.stringAction);
String data = "hello";

// Asynchronous callback
communication().callbackCollect(cmdInfo, data, responseCollect -> {
List<Response> responseList = responseCollect.getResponseList();
for (Response response : responseList) {
log.info("{}", response.getString());
}
});

// Synchronous call
var responseCollect = communication().callCollect(cmdInfo, data);
List<Response> responseList = responseCollect.getResponseList();
return responseList.stream()
.map(Response::getString)
.toList();
}
}

// By FlowContext
@ActionController(FlowContextCallCollectCmd.cmd)
public class FlowContextCallCollectAction {
@ActionMethod(FlowContextCallCollectCmd.callCollectString)
private List<String> callString(FlowContext flowContext) {
// The two below are equivalent, one is synchronous and the other is asynchronous.
var cmdInfo = InternalCmd.of(InternalCmd.stringAction);
String data = "hello";

// Asynchronous callback
flowContext.callbackCollect(cmdInfo, data, responseCollect -> {
List<Response> responseList = responseCollect.getResponseList();
for (Response response : responseList) {
log.info("{}", response.getString());
}
});

// Synchronous call
var responseCollect = flowContext.callCollect(cmdInfo, data);
List<Response> responseList = responseCollect.getResponseList();
return responseList.stream()
.map(Response::getString)
.toList();
}
}

EventBus,分布式事件总线

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


Subscriber 订阅者

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

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

@ToString
@ProtobufClass
@FieldDefaults(level = AccessLevel.PUBLIC)
public class UserLoginEventMessage {
long userId;

public static UserLoginEventMessage of(long userId) {
var message = new UserLoginEventMessage();
message.userId = userId;
return message;
}
}

fire 发布事件

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

@ActionController(EventBusCmd.cmd)
public class EventBusAction {
@ActionMethod(EventBusCmd.fire)
private boolean fire(FlowContext flowContext) {
long userId = flowContext.getUserId();

var message = UserLoginEventMessage.of(userId);
flowContext.fire(message);

return true;
}
}

OnExternal,访问对外服

tip

OnExternal, 是框架提供的通信方式之一,主要用于逻辑服与对外服的交互上。

@ActionController(OnExternalCmd.cmd)
public final class OnExternalAction {
@ActionMethod(OnExternalCmd.userIp)
private String getIp(FlowContext flowContext) {
var externalResponse = flowContext.callExternal(MyOnExternalTemplateId.userIp);
return externalResponse.getPayloadAsString();
}

// or
private String getIp() {
var externalResponse = CommunicationKit.getCommunication().callExternal(MyOnExternalTemplateId.userIp);
return externalResponse.getPayloadAsString();
}
}

public interface MyOnExternalTemplateId {
/** get userIp */
int userIp = 1;
}

如何扩展

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

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

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