我第一次在项目上使用Reactive,遇到性能非常重要的问题。
我正在通过TCP套接字检索大量数据,我必须将其解析为对象并插入到数据库中。每封邮件都有以下签名:
<payload-size> <payload>
其中size是uint32(4kb),它以字节为单位描述了后续有效负载的大小。
我想使用Reactive Framework提供的功能来并行化以下步骤(如下所示),以最大限度地提高性能并避免成为瓶颈。此外,我要求实施这个“最佳实践”。
TCP Socket ---> Observable (ArraySegment<byte>) --> Observable (Message)
我已经实现了以下代码,它为我提供了Observable (ArraySegment<byte>)
。
IObservable<TcpClient> observableTcpClient = endPoint.ListenerObservable(1);
IObservable<ArraySegment<byte>> observableSocket = observableTcpClient
.SelectMany(client => client.ToClientObservable(bufferSize));
我现在想要将Observable (ArraySegment<byte>)
转换为Observable (Message)
。我的第一个解决方案看起来有点像这样,因为我可以像流一样使用一个observable。
Read continous bytestream from Stream using TcpClient and Reactive Extensions
使用以下方法创建observable是否可能(以及如何)?或者你会推荐一种更好的方法吗?我真的很感激一个很好的例子。
注意:Observable(ArraySegment)的行为类似于流,因此我不知道它推送给我的数据的大小。 (我需要实现某种缓冲区,还是Reactive Framework可以帮助我?)
Observable (ArraySegment<byte>)
--> Buffer(4kb)
--> ReadSize --> Buffer(payload-size)
--> ReadPayload
--> Parse Payload
--> (Start over)
提前致谢! :)
答案 0 :(得分:1)
编辑:在Dimitri的评论之后,我在下面提出修改后的解决方案。有一条线需要绝望的重构,但似乎有效..
Window
使用了重载,因此我们可以编写自定义缓冲。
var hinge = new Subject<Unit>();
observableSocket
.SelectMany(i => i) // to IObservable<byte>
.Window(() => hinge) // kinda-like-buffer
.Select(buff =>
{
return
from size in buff.Buffer(SIZEOFUINT32).Select(ConvertToUINT32)
from payload in buff.Buffer(size)
//Refactor line below! Window must be closed somehow..
from foo in Observable.Return(Unit.Default).Do( _ => hinge.OnNext(Unit.Default))
select payload;
})
.SelectMany(i=>i)
.ObserveOn(ThreadPoolScheduler.Instance)
.Select(ConvertToMessage);
编辑2:删除旧解决方案
答案 1 :(得分:0)
这是我最终使用的解决方案。随意改进评论。
public static IObservable<DataMessage> Convert(IObservable<ArraySegment<byte>> bytes)
{
const int headerSize = 12; // bytes
return bytes.Scan(
new
{
Leftovers = new byte[0],
Messages = new List<DataMessage>(),
Header = (Header) null
},
(saved, current) =>
{
var data = ConcatdArrays(saved.Leftovers, current.ToArray());
var messages = new List<DataMessage>();
var header = saved.Header;
while (true)
{
// Header
if (header == null && data.Count >= headerSize)
{
header = ReadHeader(ref data, headerSize);
}
// Payload
else if (header != null)
{
var type = header.Type;
var size = DataItem.Size(type);
if (data.Count < size) break; // Still missing data
// Create new message with the gathered data
var payload = ReadPayload(ref data, size);
messages.Add(new DataMessage(header, payload));
header = null;
}
// Can't do more with the available data - try again next round.
else
{
break;
}
}
return new
{
Leftovers = data.ToArray(),
Messages = messages,
Header = header
};
}).SelectMany(list => list.Messages);