C# TPL数据流两阶段提交
我想实现类似于两阶段提交协议的东西来消费消息 为此,我自己实现了C# TPL数据流两阶段提交,c#,.net,dataflow,tpl-dataflow,bufferblock,C#,.net,Dataflow,Tpl Dataflow,Bufferblock,我想实现类似于两阶段提交协议的东西来消费消息 为此,我自己实现了ITargetBlock public class Worker : ITargetBlock<Message> { // Is connected to remote server // Maintaining connection removed for brevity in this example private bool _isConnectionAlive; priva
ITargetBlock
public class Worker : ITargetBlock<Message>
{
// Is connected to remote server
// Maintaining connection removed for brevity in this example
private bool _isConnectionAlive;
private readonly ActionBlock<MessageWithSource> _action;
public Worker()
{
_action = new ActionBlock<MessageWithSource>(DoWork);
}
public DataflowMessageStatus OfferMessage(
DataflowMessageHeader messageHeader, Message messageValue,
ISourceBlock<Message> source, bool consumeToAccept)
{
if (consumeToAccept || source == null)
{
return DataflowMessageStatus.Declined;
}
if (!_isConnectionAlive)
{
return DataflowMessageStatus.Postponed;
}
var reservedMessage = source.ReserveMessage(messageHeader, this);
if (reservedMessage)
{
_action.Post(new MessageWithSource(messageValue, source, messageHeader));
}
return DataflowMessageStatus.Postponed;
}
// Other methods removed for brevity
private async Task DoWork(MessageWithSource value)
{
try
{
// sending message to the server removed for brevity
// commit that we finished processing without error
var message = value.SourceBlock.ConsumeMessage(value.MessageHeader, this, out _);
if (message != value.Message)
{
// In which cases can we get here?
throw new InvalidOperationException("Consumed some other message... oh my");
}
}
catch (WebSocketException)
{
// Release reservation if we can't finish work, so other Workers can pickup this message and process it
value.SourceBlock.ReleaseReservation(value.MessageHeader, this);
}
}
private class MessageWithSource
{
public Message Message { get; }
public ISourceBlock<Message> SourceBlock { get; }
public DataflowMessageHeader MessageHeader { get; }
}
}
公共类工作程序:ITargetBlock
{
//已连接到远程服务器
//在本例中,为了简洁起见,删除了维护连接
私人楼宇与外界的联系仍然活跃;
私有只读操作块_操作;
公职人员()
{
_动作=新动作块(定位销);
}
公用数据流消息消息的状态(
DataflowMessageHeader消息头,消息值,
ISourceBlock源,bool consumeToAccept)
{
if(consumeToAccept | | source==null)
{
返回DataflowMessageStatus。已拒绝;
}
如果(!\u断开连接活动)
{
返回DataflowMessageStatus。已推迟;
}
var reservedMessage=source.ReserveMessage(messageHeader,this);
如果(保留消息)
{
_action.Post(newmessagewithsource(messageValue、source、messageHeader));
}
返回DataflowMessageStatus。已推迟;
}
//为简洁起见,删除了其他方法
专用异步任务DoWork(MessageWithSource值)
{
尝试
{
//为简洁起见,已删除向服务器发送消息的操作
//确认我们完成了处理,没有错误
var message=value.SourceBlock.ConsumeMessage(value.MessageHeader,this,out);
if(message!=value.message)
{
//在什么情况下我们能到这里?
抛出新的InvalidOperationException(“消耗了一些其他消息…哦,天哪”);
}
}
捕获(WebSocketException)
{
//如果我们无法完成工作,请释放保留,以便其他工作人员可以接收此消息并处理它
value.SourceBlock.ReleaseReservation(value.MessageHeader,this);
}
}
私有类MessageWithSource
{
公共消息{get;}
公共ISourceBlock源块{get;}
公共数据流消息头消息头{get;}
}
}
在中,它表示ConsumeMessage
可以返回与以前提供的不同的实例
我想知道它是在什么情况下发生的,以什么方式发生的?@StephenCleary如果您能看看为什么在数据流中使用事务语义,更不用说两阶段提交了,我会非常感激的?涉及哪些交易协调人?处理问题的数据流方法是将失败消息或错误消息重定向到另一个块,例如使用
LinkTo()
中的谓词。这样可以避免阻塞输入队列中的其他消息。如果消息被包装在一个“信封”中,表明这是一条好消息还是“坏消息”,则重定向会变得容易得多。信封可以包含重试计数,以确保同一消息不会无限期重试。@PanagiotisKanavos我需要事务语义,因为我需要确保排序,并且其中一些Worker
实例可能会中断一段时间。可能还有另一种好方法吗?您描述的是排队、发送消息和重试。它与事务语义无关。事务语义意味着要么所有服务器接受并提交操作,要么所有服务器放弃操作。“2PC就是这样做的。@如果您能看看为什么在数据流中使用事务语义,更不用说两阶段提交了,StephenCleary会非常感激的。”?涉及哪些交易协调人?处理问题的数据流方法是将失败消息或错误消息重定向到另一个块,例如使用LinkTo()
中的谓词。这样可以避免阻塞输入队列中的其他消息。如果消息被包装在一个“信封”中,表明这是一条好消息还是“坏消息”,则重定向会变得容易得多。信封可以包含重试计数,以确保同一消息不会无限期重试。@PanagiotisKanavos我需要事务语义,因为我需要确保排序,并且其中一些Worker
实例可能会中断一段时间。可能还有另一种好方法吗?您描述的是排队、发送消息和重试。它与事务语义无关。事务语义意味着要么所有服务器接受并提交操作,要么所有服务器放弃操作。他们是如何做到这一点的。