1

環境

次の構造のファイルがあります。

[ProtoContract]
public class Data
{
    [ProtoMember(1)]
    public string Header { get; set; }

    [ProtoMember(2)]
    public byte[] Body { get; set; }
}

データをファイルに読み書きするコードは、asp.net mvc webapi コンテキストで実行されています。ブロッキングを最小限に抑え、最高のスケーラビリティを実現するために、すべてのブロッキング IO を非同期に保つようにしています。ファイルの読み取りと書き込みでは、ReadAsync、WriteAsync、および CopyToAsync がサポートされています。

本文はかなり大きくなる可能性があり (>> ヘッダー)、ヘッダーが特定の条件に一致する場合にのみ本文を読み取る必要があります。

バイナリ ファイルの一部をデシリアライズするで説明されている方法を使用して、ヘッダーを同期的に部分的に読み取り、デシリアライズし、本体を同じ方法で読み込んでデシリアライズすることができます。

問題

非同期ファイル IO を使用して、ヘッダー Async の読み取りと逆シリアル化、および本体の読み取りと逆シリアル化を同じ方法で行うにはどうすればよいですか?

Asynchronous protobuf serialization is not an optionを読みました。

4

1 に答える 1

2

技術的には protobuf フィールドは順不同になる可能性がありますが、ほとんどの場合 (表示されているものを含む)、フィールドが順不同であると合理的に想定できます (ここでそれらを順不同にする唯一の方法は、個別にシリアル化することです)これは protobuf 仕様で技術的に有効です)。

そう; 私たちが持つものは次のとおりです。

  • varint を示す: フィールド 1、文字列 - 常に 10 進数10
  • ヘッダーの長さ「a」を表す varint
  • 「a」バイト、UTF-8 でエンコードされたヘッダー
  • varint を示す: フィールド 2、文字列 - 常に 10 進数18
  • 本体の長さ「b」を表す varint
  • "b" バイト、本体

おそらく、「a」は>= 0and< int.MaxValueであると想定できます。つまり、エンコードには最大で 5 バイトかかります。したがって、少なくとも6 バイトをバッファリングすると、ヘッダーの大きさを知るのに十分な情報が得られます。もちろん、技術的には体の一部も含まれている可能性があるため、保持する必要があります。しかし、 sync-over-asyncがある場合は、次のようにしてストリームのその部分だけStreamを読み取ることができます。

int protoHeader = ProtoReader.DirectReadVarintInt32(stream); // 10
int headerLength = ProtoReader.DirectReadVarintInt32(stream);
string header = ProtoReader.DirectReadString(stream, headerLength);

または、「非同期を介した同期」が難しい場合は、明示的な読み取り:

static byte[] ReadAtLeast6()
{
    return new byte[] { 0x0A, 0x0B, 0x68, 0x65, 0x6C, 0x6C, 0x6F };
}
static byte[] ReadMore(int bytes)
{
    return new byte[] { 0x20, 0x77, 0x6F, 0x72, 0x6C, 0x64 };
}
static void Main()
{
    // pretend we read 7 bytes async
    var data = ReadAtLeast6();
    using (var ms = new MemoryStream())
    {
        ms.Write(data, 0, data.Length);
        ms.Position = 0;
        int protoHeader = ProtoReader.DirectReadVarintInt32(ms); // 10
        int headerLength = ProtoReader.DirectReadVarintInt32(ms); // 11

        int needed = (headerLength + (int)ms.Position) - data.Length; // 6 more
        var pos = ms.Position;
        ms.Seek(0, SeekOrigin.End);
        data = ReadMore(needed);
        ms.Write(data, 0, needed);
        ms.Position = pos;
        string header = ProtoReader.DirectReadString(ms, headerLength);
    }
}
于 2013-08-16T09:26:16.487 に答える