通信模型
介绍
本篇,我们将介绍框架提供的一些通信模型。通常用于客户端与服务器通信、或服务器内部之间的通信。
在客户端的角度
在客户端的角度,我们提供了如下的通信模型
- 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();
});
}
上面客户端的代码由框架生成,在实际开发中通常会使用代码生成。
具体请阅读: here
内部通信
内部通信主要用于服务器内部之间的通信,跨服、跨进程通信。我们提供了如下的通信模型
- request/response,请求/响应
- request/void,请求/无响应
- request/multiple_response,同时请求同类型多个逻辑服
- EventBus,分布式事件总线
- OnExternal,访问对外服与扩展
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;
}
}
request/void 模型是异步调用的。
request/multiple_response,请求同类型多个逻辑服
request/multiple_response 是逻辑服之间的请求,能同时请求同类型多个逻辑服。
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,访问对外服
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 时,不要被过去的传统框架束缚住,可以做任何大胆的设计,因为通信方式足够丰富。