Orleans 自定义二进制协议在 TCP 上层实现的完整过程

Orleans 自定义二进制协议实现详解

1. 协议栈架构

复制代码
┌─────────────────────────────────────────┐
│         Orleans 自定义协议层              │  ← 应用层协议
│  ┌─────────┬─────────┬─────────┬──────┐  │
│  │Grain路由│方法调度 │异常处理 │超时控制│  │
│  └─────────┴─────────┴─────────┴──────┘  │
├─────────────────────────────────────────┤
│            TCP 传输层                    │  ← 可靠传输
│        (Socket 连接管理)                 │
└─────────────────────────────────────────┘

2. 核心组件实现

(1) 消息结构定义 (Message.cs)
csharp:1:77:src/Orleans.Core/Messaging/Message.cs 复制代码
[Id(101)]
internal sealed class Message : ISpanFormattable
{
    public const int LENGTH_HEADER_SIZE = 8;  // 帧长度头
    public const int LENGTH_META_HEADER = 4;  // 元数据头
    
    // 核心字段
    public object BodyObject { get; set; }           // 消息体
    public PackedHeaders _headers;                   // 压缩头信息
    public CorrelationId _id;                        // 消息ID
    public GrainId _targetGrain;                     // 目标Grain
    public GrainId _sendingGrain;                    // 发送Grain
    public SiloAddress _targetSilo;                  // 目标Silo
    public SiloAddress _sendingSilo;                 // 发送Silo
    public ushort _interfaceVersion;                 // 接口版本
    public GrainInterfaceType _interfaceType;        // 接口类型
}
(2) 二进制协议序列化 (MessageSerializer.cs)
csharp:56:78:src/Orleans.Core/Messaging/MessageSerializer.cs 复制代码
public (int RequiredBytes, int HeaderLength, int BodyLength) TryRead(ref ReadOnlySequence<byte> input, out Message? message)
{
    if (input.Length < FramingLength)
    {
        message = default;
        return (FramingLength, 0, 0);
    }

    // 1. 读取帧长度头 (8字节)
    Span<byte> lengthBytes = stackalloc byte[FramingLength];
    input.Slice(input.Start, FramingLength).CopyTo(lengthBytes);
    var headerLength = BinaryPrimitives.ReadInt32LittleEndian(lengthBytes);
    var bodyLength = BinaryPrimitives.ReadInt32LittleEndian(lengthBytes[4..]);

    // 2. 验证长度有效性
    ThrowIfLengthsInvalid(headerLength, bodyLength);

    var requiredBytes = FramingLength + headerLength + bodyLength;
    if (input.Length < requiredBytes)
    {
        message = default;
        return (requiredBytes, 0, 0);
    }
}
(3) 消息头序列化实现
csharp:224:248:src/Orleans.Core/Messaging/MessageSerializer.cs 复制代码
private void Serialize<TBufferWriter>(ref Writer<TBufferWriter> writer, Message value, PackedHeaders headers) where TBufferWriter : IBufferWriter<byte>
{
    writer.WriteUInt32((uint)headers);                    // 压缩头标志
    writer.WriteInt64(value.Id.ToInt64());                // 消息ID
    WriteGrainId(ref writer, value.SendingGrain);         // 发送Grain ID
    WriteGrainId(ref writer, value.TargetGrain);          // 目标Grain ID
    _writerSiloAddressCodec.WriteRaw(ref writer, value.SendingSilo);  // 发送Silo地址
    _writerSiloAddressCodec.WriteRaw(ref writer, value.TargetSilo);   // 目标Silo地址

    if (headers.HasFlag(MessageFlags.HasTimeToLive))
    {
        writer.WriteInt32((int)value.GetTimeToLiveMilliseconds());  // 超时控制
    }

    if (headers.HasFlag(MessageFlags.HasInterfaceType))
    {
        _idSpanCodec.WriteRaw(ref writer, value.InterfaceType.Value);  // 接口类型
    }
}

3. TCP 连接管理

(1) 连接管理器 (ConnectionManager.cs)
csharp:45:60:src/Orleans.Core/Networking/ConnectionManager.cs 复制代码
public ValueTask<Connection> GetConnection(SiloAddress endpoint)
{
    if (this.connections.TryGetValue(endpoint, out var entry) && entry.NextConnection() is { } connection)
    {
        if (!entry.HasSufficientConnections(connectionOptions) && entry.PendingConnection is null)
        {
            this.GetConnectionAsync(endpoint).Ignore();
        }
        return new(connection);  // 返回现有连接
    }
    return new(this.GetConnectionAsync(endpoint));  // 创建新连接
}
(2) Socket 连接工厂 (SocketConnectionFactory.cs)
csharp:31:53:src/Orleans.Core/Networking/Shared/SocketConnectionFactory.cs 复制代码
public async ValueTask<ConnectionContext> ConnectAsync(EndPoint endpoint, CancellationToken cancellationToken)
{
    var socket = new Socket(endpoint.AddressFamily, SocketType.Stream, ProtocolType.Tcp)
    {
        LingerState = new LingerOption(true, 0),
        NoDelay = _options.NoDelay,  // 禁用Nagle算法
    };

    if (_options.KeepAlive)
    {
        socket.EnableKeepAlive(
            timeSeconds: _options.KeepAliveTimeSeconds,
            intervalSeconds: _options.KeepAliveIntervalSeconds,
            retryCount: _options.KeepAliveRetryCount);
    }

    socket.EnableFastPath();  // 启用快速路径优化
}

4. 消息路由和处理流程

(1) 消息发送 (MessageCenter.cs)
csharp:178:219:src/Orleans.Runtime/Messaging/MessageCenter.cs 复制代码
if (targetSilo.Matches(_siloAddress))
{
    // 本地消息 - 直接处理
    this.ReceiveMessage(msg);
}
else
{
    if (this.connectionManager.TryGetConnection(targetSilo, out var existingConnection))
    {
        existingConnection.Send(msg);  // 通过TCP发送
        return;
    }
    else if (this.siloStatusOracle.IsDeadSilo(targetSilo))
    {
        // 目标Silo已死亡 - 发送拒绝消息
        this.SendRejection(msg, Message.RejectionTypes.Transient, "Target silo is known to be dead");
        return;
    }
    else
    {
        // 异步建立连接并发送
        var connectionTask = this.connectionManager.GetConnection(targetSilo);
        if (connectionTask.IsCompletedSuccessfully)
        {
            var sender = connectionTask.Result;
            sender.Send(msg);
        }
    }
}
(2) 消息接收和路由 (MessageCenter.cs)
csharp:508:537:src/Orleans.Runtime/Messaging/MessageCenter.cs 复制代码
public void ReceiveMessage(Message msg)
{
    try
    {
        this.messagingTrace.OnIncomingMessageAgentReceiveMessage(msg);
        if (TryDeliverToProxy(msg))
        {
            return;  // 代理消息处理
        }
        else if (msg.Direction == Message.Directions.Response)
        {
            this.catalog.RuntimeClient.ReceiveResponse(msg);  // 响应消息处理
        }
        else
        {
            // 请求消息 - 路由到目标Grain
            var targetActivation = catalog.GetOrCreateActivation(
                msg.TargetGrain,
                msg.RequestContextData,
                rehydrationContext: null);

            if (targetActivation is null)
            {
                ProcessMessageToNonExistentActivation(msg);
                return;
            }

            targetActivation.ReceiveMessage(msg);  // 转发到Grain激活
        }
    }
    catch (Exception ex)
    {
        HandleReceiveFailure(msg, ex);
    }
}
(3) Grain 方法调用 (InsideRuntimeClient.cs)
csharp:250:291:src/Orleans.Runtime/Core/InsideRuntimeClient.cs 复制代码
public async Task Invoke(IGrainContext target, Message message)
{
    try
    {
        if (message.IsExpired)
        {
            this.messagingTrace.OnDropExpiredMessage(message, MessagingInstruments.Phase.Invoke);
            return;
        }

        Response response;
        try
        {
            switch (message.BodyObject)
            {
                case IInvokable invokable:
                    {
                        invokable.SetTarget(target);
                        
                        // 应用Grain调用过滤器
                        if (GrainCallFilters is { Count: > 0 } || target.GrainInstance is IIncomingGrainCallFilter)
                        {
                            var invoker = new GrainMethodInvoker(message, target, invokable, GrainCallFilters, this.interfaceToImplementationMapping, this.responseCopier);
                            await invoker.Invoke();
                            response = invoker.Response;
                        }
                        else
                        {
                            response = await invokable.Invoke();  // 直接调用
                            response = this.responseCopier.Copy(response);
                        }
                        break;
                    }
            }
        }
        catch (Exception ex)
        {
            // 异常处理
            response = Response.FromException(ex);
        }
    }
}

5. 关键特性实现

(1) 超时控制 (InvokeMethodOptions.cs)
csharp:17:28:src/Orleans.Core.Abstractions/CodeGeneration/InvokeMethodOptions.cs 复制代码
[Flags]
[GenerateSerializer]
public enum InvokeMethodOptions
{
    None = 0,
    OneWay = 1 << 0,          // 单向调用 - 无响应
    ReadOnly = 1 << 1,        // 只读操作 - 可并发
    AlwaysInterleave = 1 << 2, // 允许交错 - 可与其他请求并发
    Unordered = 1 << 3,       // 无序消息 - 可优化传输
}
(2) 连接前导码 (ConnectionPreamble.cs)
csharp:11:25:src/Orleans.Core/Networking/ConnectionPreamble.cs 复制代码
[GenerateSerializer, Immutable]
internal sealed class ConnectionPreamble
{
    [Id(0)]
    public NetworkProtocolVersion NetworkProtocolVersion { get; init; }
    [Id(1)]
    public GrainId NodeIdentity { get; init; }
    [Id(2)]
    public SiloAddress SiloAddress { get; init; }
    [Id(3)]
    public string ClusterId { get; init; }
}

6. 完整通信流程

  1. 调用发起

    • Grain A 调用 Grain B 的方法
    • 创建 Message 对象,包含目标Grain ID、方法参数等
    • 设置调用选项(超时、单向等)
  2. 消息序列化

    • 使用 MessageSerializer 将消息序列化为二进制格式
    • 添加帧长度头(8字节)+ 消息头 + 消息体
    • 压缩头信息以减少网络开销
  3. TCP 传输

    • 通过 ConnectionManager 获取到目标Silo的TCP连接
    • 使用 Socket 发送二进制数据
    • 支持连接池和自动重连
  4. 消息接收

    • 目标Silo的TCP服务器接收数据
    • 反序列化消息,解析帧长度和消息内容
    • 根据 TargetGrain 路由到对应的Grain激活
  5. 方法执行

    • 通过 GrainMethodInvoker 调用目标方法
    • 处理异常和超时
    • 序列化响应并返回
  6. 响应处理

    • 调用方接收响应消息
    • 反序列化结果或异常
    • 完成异步调用

这个设计实现了高性能、可靠的分布式Grain通信,同时保持了良好的可扩展性和容错能力。

相关推荐
超级大只老咪4 小时前
蓝桥杯知识点大纲(JavaC组)
java·算法·蓝桥杯
Yiii_x4 小时前
如何使用IntelliJ IDEA进行Java编程
java·课程设计·ai编程
阿杰AJie4 小时前
如何在程序中避免出现大量if和case
java·后端
摇滚侠5 小时前
Spring Boot3零基础教程,云服务停机不收费,笔记71
java·spring boot·笔记
豐儀麟阁贵5 小时前
5.5类的主方法
java·开发语言
不光头强5 小时前
maven进阶
java·maven
智海观潮5 小时前
聊聊Spark的分区
java·大数据·spark
rengang665 小时前
020-Spring AI Alibaba DashScope Image 功能完整案例
java·人工智能·spring·spring ai·ai应用编程
-Initiation5 小时前
数据库的安全与保护(下)
java·数据库·oracle