Skip to main content

Communication Models

Introduction

In this section, we introduce several communication models provided by the framework. They are commonly used for client-server communication and internal server-to-server communication.

From the Client Perspective

From the client perspective, we provide the following communication models:

  • request/response - request/response
  • request/void - request/no response
  • request/broadcast - request/broadcast response
  • broadcast - broadcast

request/response, request/response

After the client sends a request, the action returns data.

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

Client usage, integrated with SDK C#:

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

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

request/void, request/no response

After the client sends a request, the action does not return any data.

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

}

Client usage

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

request/broadcast, request/broadcast response

After the client sends a request, the action does not return direct response data. Instead, it sends data through broadcast.

@ActionController(1)
public final class MyAction {
@ActionMethod(2)
private void say(String name, FlowContext flowContext) {
String dataString = "ionet";
CommunicationKit.getCommunication().broadcastMulticast(cmd, dataString);
}

}

Client usage

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

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

broadcast, server-initiated broadcast

Broadcast means the server actively sends data to clients.

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

Client usage

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

The client-side code above is generated by the framework. In actual development, code generation is typically used.

See: here

Internal Communication

Internal communication is mainly used for server-to-server communication across services and processes. We provide the following communication models:

request/response, request/response

For logic-service requests, the two methods below are equivalent.

// 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().callAsync(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.callAsync(cmdInfo, data, response -> {
log.info("{}", response.getString());
});

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

request/void, request/no response

For logic-service requests, the two methods below are equivalent.

// 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

The request/void model is asynchronous.

request/multiple_response, request multiple logic services of the same type

request/multiple_response is an internal logic-service communication model that can call multiple logic services of the same type simultaneously.

warning

The request/multiple_response model must have return values. The action on the called side cannot be void.

If you only want to trigger actions on multiple logic services of the same type, consider using EventBus.


Example scenario

The framework supports multiple instances of the same logic service type, such as LogicService-B-1 and LogicService-B-2.

Sometimes we need to access actions on multiple same-type logic services at the same time. The framework supports calling multiple logic services of the same type and collecting their results together.

The following two methods are equivalent

// By CommunicationKit
@ActionController(CallCollectCmd.cmd)
public class CallCollectAction {
@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().callCollectAsync(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();
}

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

// 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.callCollectAsync(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, distributed event bus

Distributed EventBus is one of the communication models, similar to Guava EventBus, Redis Pub/Sub, MQ, and similar products.


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, publish event

After publishing the event, the two subscribers above will receive the message.

@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, access external services

tip

OnExternal is one of the communication models provided by the framework, mainly used for interaction between logic services and external services.

@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;
}

How to Extend

All these communication models are implemented through extension mechanisms. You can extend any communication model you need to meet specific business requirements.

Traditional frameworks often require many third-party middlewares to implement similar communication capabilities, while ionet can provide them without any middleware. This means simpler usage and reduced enterprise deployment and maintenance costs.

Traditional frameworks usually only provide request handling and then respond by broadcasting after processing. When using ionet, do not be constrained by legacy framework patterns. You can design more freely because the available communication models are rich enough.