Route消息¶
在现代网络开发中,消息的传输过程往往不仅仅是简单的从发送端直接到达接收端。通常情况下,消息会通过一个中转服务器进行转发。这种设计带来了许多优势。首先,它可以有效减少暴露给外部的网络端口数量,从而增强系统的安全性。其次,在需要切换服务器时,由于消息传输和连接的处理都在中转服务器上进行,因此可以避免中断现有的socket连接,实现平滑的服务器切换。这种架构不仅提高了系统的灵活性,也提升了整体的稳定性和可维护性。
框架中的Route协议要求每个实体都需要绑定到Route协议上。框架内的任意实体都可以作为Route协议的实现,而该实体的RunTimeId则会作为对应Route协议的RouteId。换句话说,只要获取到某个实体的RunTimeId,就可以通过Route协议将消息发送给这个实体。这种机制确保了消息可以精准地传递到目标实体,从而实现了框架内的高效通信。
RouteType.Config¶
目录结构¶
.
├─ Book
├─ Examples/
│ ├─ Config/
│ │ └─ NetworkProtocol/
│ │ └─ RouteType.Config # Route协议配置文件
│ └─ Src
└─ README.md
新的RouteType¶
如果需要增加一个新的Route协议,必须在RouteType.Config文件中定义一个新的类型,并遵循以下要求:
- 每个定义的Route协议应独占一行,不能在同一行内定义多个协议。
- 每个Route协议的格式为:协议名称 + 空格 + "=" + 空格 + 协议类型号。
- 协议类型号必须唯一,不得重复。
添加一个ChatRoute的示例
在文件中定义新的 Route 类型后,通过导表工具选择导出网络协议时,系统会自动生成一个名为 RouteType.cs 的文件,并将其保存在你定义的网络协议所对应的生成目录中。
RouteType.cs
Route协议¶
请按照定义网络通信协议的格式,在协议名后面添加一个逗号,紧接着写上你定义的RouteType名称,并且在此基础上进行以下替换:
- 将IMessage替换为ICustomRouteMessage。
- 将IRequest替换为ICustomRouteRequest。
- I将IResponse替换为ICustomRouteResponse。
RPC的Route消息示例
Route消息处理¶
服务器需要接收一个 Route 协议,为此,首先需要定义一个实体类,用于承载和处理 Route 协议的数据。这一实体将负责接收并解析协议中的各项参数和信息,为后续的逻辑处理提供支持和基础。
Route¶
接收发送端发送的Route消息。
需要定义一个类来继承Route,Route接受两个泛型类型:
- 泛型1:指定要接收这个Route协议的实体。
- 泛型2:指定了需要接收消息的类型。
在继承了Route类之后,必须实现一个名为Run的方法。这个方法接受两个参数:
- entity:当前Route协议绑定的实体,类型就是Route后面泛型1定义的类型。
- message:发送端发送过来的消息数据,类型就是Route后面泛型2定义的类型。
Route示例
RouteRPC¶
接收发送端发送的RouteRPC消息。
需要定义一个类来继承RouteRPC,RouteRPC接受三个泛型类型:
- 泛型1:指定要接收这个Route协议的实体。
- 泛型2:指定了需要接收消息的类型。
- 泛型3:指定了发送端需要接收消息的类型。
在继承了RouteRPC类之后,必须实现一个名为Run的方法。这个方法接受四个参数:
- entity:当前Route协议绑定的实体,类型就是RouteRPC后面泛型1定义的类型。
- request:发送端发送过来的消息数据,类型就是RouteRPC后面泛型2定义的类型。
- response:需要返回给发送端的响应数据,类型就是RouteRPC后面泛型3定义的类型。
- response:一个用于立即发送 response 消息给发送端的函数。如果不调用 reply,系统会在 Run 方法执行完毕后自动调用它,将 response 发送回发送端。
RouteRPC示例
public sealed class C2Chat_TestMessageRequestHandler : RouteRPC<ChatUnit, C2Chat_TestMessageRequest, Chat2C_TestMessageResponse>
{
protected override async FTask Run(ChatUnit entity, C2Chat_TestMessageRequest request, Chat2C_TestMessageResponse response, Action reply)
{
Log.Debug($"C2Chat_TestMessageRequestHandler request = {request}");
response.Tag = "Hello RouteRPC"; // 这里给发送端返回的消息
await FTask.CompletedTask;
}
}
注册Route¶
1.获取RouteId¶
现在,如果我们已经获取了某个实体的 RunTimeId,它将会被用作相应路由协议中的 RouteId。接下来需要解决的问题是如何在某个中转服务器上将消息转发到持有该 RouteId 的目标服务器。为此,在注册过程中,需要在中转服务器与目标服务器之间建立一次通信,以获取并确认这个 RouteId。
Gate服务器中向Chat服务器发送消息获取一个RouteId
// 首先需要找到一个需要建立Route的Scene的SceneConfig。
// 例子演示的连接的ChatScene,所以这里我通过SceneConfigData拿到这个SceneConfig。
// 如果是其他Scene,用法跟这个没有任何区别。
var chatSceneConfig = SceneConfigData.Instance.GetSceneBySceneType(SceneType.Chat)[0];
// 通过chatSceneConfig拿到这个Scene的RouteId
var chatRouteId = chatSceneConfig.RouteId;
// 通过Scene拿到当前Scene的NetworkMessagingComponent。
// NetworkMessagingComponent是服务器之间通讯的唯一手段。
var networkMessagingComponent = session.Scene.NetworkMessagingComponent;
// 通过CallInnerRoute方法发送一个RPC消息给ChatScene上。
// 任何一个实体的RunTimeId都可以做为RouteId使用。
// 所以这个传递了一个session.RunTimeId,是方便Chat发送消息回Gate上。
var routeResponse = (Chat2G_CreateRouteResponse)await networkMessagingComponent.CallInnerRoute(chatRouteId,
new G2Chat_CreateRouteRequest()
{
GateRouteId = session.RunTimeId
});
if (routeResponse.ErrorCode != 0)
{
// 如果ErrorCode不是0表示请求的协议发生错误,应该提示给客户端。
// 这里就不做这个了。
return;
}
Log.Debug($"ChatRouteId={routeResponse.ChatRouteId}");
routeResponse.ChatRouteId 是在 Chat 服务器上生成的唯一标识符 (RouteId),它用来标识当前会话或请求的路由路径。当 Gate 服务器获取到这个 RouteId 后,可以利用该标识符高效地将后续消息直接路由到对应的目标服务器,从而避免了重复的路由解析和定位过程,提升了消息传输的速度和准确性。
现在看一下G2Chat_CreateRouteRequest这个接收器的实现:
G2Chat_CreateRouteRequest
public sealed class G2Chat_CreateRouteRequestHandler : RouteRPC<Scene, G2Chat_CreateRouteRequest, Chat2G_CreateRouteResponse>
{
protected override async FTask Run(Scene scene, G2Chat_CreateRouteRequest request, Chat2G_CreateRouteResponse response, Action reply)
{
// 接收到Gate消息后,首先建立一个实体用来后面接收Route消息。
// 这里就拿ChatUnit来做这个。
var chatUnit = Entity.Create<ChatUnit>(scene, true, true);
// 把Gate传递过来的RouteId保存住,以便后面可以直接给Gate发送消息。
// 例如断线等操作,都可以通过这个GateRouteId发送到Gate的Session上。
chatUnit.GateRouteId = request.GateRouteId;
// 把chatUnit的RunTimeId发送给Gate。
// 正如之前所说任何实体的RunTimeId都可以当做RouteId使用。
response.ChatRouteId = chatUnit.RunTimeId;
await FTask.CompletedTask;
}
}
2.注册Route到中转服务器¶
为了实现消息自动转发到目标服务器,可以在中转服务器在会话(Session)上挂载一个 RouteComponent 组件。当 RouteComponent 挂载到会话后,消息发送至中转服务器时,中转服务器会根据 RouteComponent 中提供的路由信息,自动将消息转发到指定的目标服务器上。通过这种方式,消息能够高效且准确地传递到最终目的地,无需手动干预。
挂载RouteComponent
这里routeComponent.AddAddress方法接受两个参数: - routeType: 指定需要绑定的 RouteType 类型。这个类型定义在 RouteType.Config 中,我们在其中定义了一个名为 ChatRoute 的类型。 - routeId: 指定需要绑定的 RouteId,这个参数通常是通过获取 RouteId 方法获取的。
到现在为止已经可以通过中转服务器自动中转Route消息了。
发送端发送Route协议¶
发送端没有任何变化,跟Session里一样发送就可以了。