C# 从持久功能向服务总线发送扩展消息
我有一个场景,其中一个活动函数检索了一组记录,这些记录可以在1000到100万之间的任何位置,并存储在一个对象中。然后,下一个活动函数将使用该对象向服务总线并行发送消息 目前,我在这个对象上使用for循环将对象中的每个记录发送到服务总线。请告诉我是否有更好的替代模式,将对象或内容(无论存储在何处)清空以发送到服务总线,并且功能自动扩展,而不将处理限制为for循环C# 从持久功能向服务总线发送扩展消息,c#,azure,orchestration,azure-durable-functions,C#,Azure,Orchestration,Azure Durable Functions,我有一个场景,其中一个活动函数检索了一组记录,这些记录可以在1000到100万之间的任何位置,并存储在一个对象中。然后,下一个活动函数将使用该对象向服务总线并行发送消息 目前,我在这个对象上使用for循环将对象中的每个记录发送到服务总线。请告诉我是否有更好的替代模式,将对象或内容(无论存储在何处)清空以发送到服务总线,并且功能自动扩展,而不将处理限制为for循环 使用了来自某个函数的for循环,该函数编排调用对象中记录的活动函数 我们已经研究了活动函数的伸缩性,对于一组18000条记录,它已伸
- 使用了来自某个函数的for循环,该函数编排调用对象中记录的活动函数李>
- 我们已经研究了活动函数的伸缩性,对于一组18000条记录,它已伸缩到15个实例,并在4分钟内处理了整个集合李>
- 当前该功能正在使用消费计划。已选中以查看只有此功能应用程序正在使用该计划且其未共享
- 邮件发送到的主题有另一个服务正在侦听,以读取邮件
- 默认情况下,编排和活动功能的实例计数都可用
for(int i=0;i我建议您使用批处理发送消息
Azure Service Bus客户端支持成批发送消息(QueueClient和TopicClient的SendBatch和SendBatchAsync方法)。但是,单个批的大小必须保持在256k字节以下,否则整个批将被拒绝
我们将从一个简单的用例开始:我们知道每条消息的大小。它由假设的Func getSize函数定义。下面是一个有用的扩展方法,它将根据度量函数和最大块大小拆分任意集合:
public static List<List<T>> ChunkBy<T>(this IEnumerable<T> source, Func<T, long> metric, long maxChunkSize)
{
return source
.Aggregate(
new
{
Sum = 0L,
Current = (List<T>)null,
Result = new List<List<T>>()
},
(agg, item) =>
{
var value = metric(item);
if (agg.Current == null || agg.Sum + value > maxChunkSize)
{
var current = new List<T> { item };
agg.Result.Add(current);
return new { Sum = value, Current = current, agg.Result };
}
agg.Current.Add(item);
return new { Sum = agg.Sum + value, agg.Current, agg.Result };
})
.Result;
}
公共静态列表ChunkBy(此IEnumerable源代码,Func度量,long maxChunkSize)
{
返回源
.合计(
刚出现的
{
总和=0升,
当前=(列表)null,
结果=新列表()
},
(agg,项目)=>
{
var值=度量(项目);
if(agg.Current==null | | agg.Sum+value>maxChunkSize)
{
var current=新列表{item};
累计结果添加(当前);
返回新的{Sum=value,Current=Current,agg.Result};
}
累计当前添加(项目);
返回新的{Sum=agg.Sum+value,agg.Current,agg.Result};
})
后果
}
现在,SendBigBatchAsync的实现很简单:
public async Task SendBigBatchAsync(IEnumerable<T> messages, Func<T, long> getSize)
{
var chunks = messages.ChunkBy(getSize, MaxServiceBusMessage);
foreach (var chunk in chunks)
{
var brokeredMessages = chunk.Select(m => new BrokeredMessage(m));
await client.SendBatchAsync(brokeredMessages);
}
}
private const long MaxServiceBusMessage = 256000;
private readonly QueueClient client;
public async Task SendBigBatchAsync(IEnumerable messages,Func getSize)
{
var chunks=messages.ChunkBy(getSize,MaxServiceBusMessage);
foreach(var chunk in chunks)
{
var brokeredMessages=chunk.Select(m=>newbrokeredmessages(m));
等待client.SendBatchAsync(brokeredMessages);
}
}
private const long MaxServiceBusMessage=256000;
私有只读队列客户端;
如何确定每条消息的大小?如何实现getSize函数
BrokeredMessage类公开Size属性,因此可能会尝试用以下方式重写我们的方法:
public async Task SendBigBatchAsync<T>(IEnumerable<T> messages)
{
var brokeredMessages = messages.Select(m => new BrokeredMessage(m));
var chunks = brokeredMessages.ChunkBy(bm => bm.Size, MaxServiceBusMessage);
foreach (var chunk in chunks)
{
await client.SendBatchAsync(chunk);
}
}
公共异步任务SendBigBatchAsync(IEnumerable消息)
{
var brokeredMessages=messages.Select(m=>newbrokeredmessages(m));
var chunks=brokeredMessages.ChunkBy(bm=>bm.Size,MaxServiceBusMessage);
foreach(var chunk in chunks)
{
等待client.SendBatchAsync(块);
}
}
我想考虑的最后一种可能性是允许自己违反批处理的最大大小,然后处理异常,重试发送操作并根据失败的消息的实际测量大小调整将来的计算。在尝试发送批处理之后,该大小是已知的,即使操作失败,也可以使用此通知。反倾销
// Sender is reused across requests
public class BatchSender
{
private readonly QueueClient queueClient;
private long batchSizeLimit = 262000;
private long headerSizeEstimate = 54; // start with the smallest header possible
public BatchSender(QueueClient queueClient)
{
this.queueClient = queueClient;
}
public async Task SendBigBatchAsync<T>(IEnumerable<T> messages)
{
var packets = (from m in messages
let bm = new BrokeredMessage(m)
select new { Source = m, Brokered = bm, BodySize = bm.Size }).ToList();
var chunks = packets.ChunkBy(p => this.headerSizeEstimate + p.Brokered.Size, this.batchSizeLimit);
foreach (var chunk in chunks)
{
try
{
await this.queueClient.SendBatchAsync(chunk.Select(p => p.Brokered));
}
catch (MessageSizeExceededException)
{
var maxHeader = packets.Max(p => p.Brokered.Size - p.BodySize);
if (maxHeader > this.headerSizeEstimate)
{
// If failed messages had bigger headers, remember this header size
// as max observed and use it in future calculations
this.headerSizeEstimate = maxHeader;
}
else
{
// Reduce max batch size to 95% of current value
this.batchSizeLimit = (long)(this.batchSizeLimit * .95);
}
// Re-send the failed chunk
await this.SendBigBatchAsync(packets.Select(p => p.Source));
}
}
}
}
//发送方可跨请求重用
公共类批处理发送器
{
专用只读队列客户端;
专用长批量SizeLimit=262000;
private long headerSizeEstimate=54;//从尽可能最小的头开始
公共BatchSender(QueueClient QueueClient)
{
this.queueClient=queueClient;
}
公共异步任务SendBigBatchAsync(IEnumerable消息)
{
var数据包=(来自消息中的m)
设bm=newbrokeredmessage(m)
选择new{Source=m,Brokered=bm,BodySize=bm.Size});
var chunks=packets.ChunkBy(p=>this.headerSizeEstimate+p.Brokered.Size,this.batchSizeLimit);
foreach(var chunk in chunks)
{
尝试
{
等待这个.queueClient.SendBatchAsync(chunk.Select(p=>p.Brokered));
}
捕获(MessageSizeExceedeException)
{
var maxHeader=packets.Max(p=>p.Brokered.Size-p.BodySize);
如果(maxHeader>this.headerSizeEstimate)
{
//如果失败消息的标题较大,请记住此标题大小
//如max所观察到的,并在未来的计算中使用
this.headerSizeEstimate=maxHeader;
}
其他的
{
//将最大批量减少到当前值的95%
this.batchSizeLimit=(长)(this.batchSizeLimit*.95);
}
//重新发送失败的区块
等待这个.SendBigBatchAsync(packets.Select(p=>p.Source));
}
}
}
}
您可以进一步使用此博客。希望能有所帮助。感谢您的详细回复。我们将尝试同样的方法并进行检查。
// Sender is reused across requests
public class BatchSender
{
private readonly QueueClient queueClient;
private long batchSizeLimit = 262000;
private long headerSizeEstimate = 54; // start with the smallest header possible
public BatchSender(QueueClient queueClient)
{
this.queueClient = queueClient;
}
public async Task SendBigBatchAsync<T>(IEnumerable<T> messages)
{
var packets = (from m in messages
let bm = new BrokeredMessage(m)
select new { Source = m, Brokered = bm, BodySize = bm.Size }).ToList();
var chunks = packets.ChunkBy(p => this.headerSizeEstimate + p.Brokered.Size, this.batchSizeLimit);
foreach (var chunk in chunks)
{
try
{
await this.queueClient.SendBatchAsync(chunk.Select(p => p.Brokered));
}
catch (MessageSizeExceededException)
{
var maxHeader = packets.Max(p => p.Brokered.Size - p.BodySize);
if (maxHeader > this.headerSizeEstimate)
{
// If failed messages had bigger headers, remember this header size
// as max observed and use it in future calculations
this.headerSizeEstimate = maxHeader;
}
else
{
// Reduce max batch size to 95% of current value
this.batchSizeLimit = (long)(this.batchSizeLimit * .95);
}
// Re-send the failed chunk
await this.SendBigBatchAsync(packets.Select(p => p.Source));
}
}
}
}