gpt4 book ai didi

.net - Azure Service Fabric InvokeWithRetryAsync 巨大的开销

转载 作者:行者123 更新时间:2023-12-03 05:55:47 24 4
gpt4 key购买 nike

我目前正在开发需要高吞吐量的 Service Fabric 微服务。

我想知道为什么我无法使用环回在我的工作站上实现每秒超过 500 条 1KB 消息。

我删除了所有业务逻辑并附加了性能分析器,只是为了测量端到端性能。

似乎 ~96% 的时间花在解析客户端上,只有 ~2% 的时间花在实际的 Http 请求上。

我在紧密循环中调用“发送”进行测试:

private HttpCommunicationClientFactory factory = new HttpCommunicationClientFactory();

public async Task Send()
{
var client = new ServicePartitionClient<HttpCommunicationClient>(
factory,
new Uri("fabric:/MyApp/MyService"));

await client.InvokeWithRetryAsync(c => c.HttpClient.GetAsync(c.Url + "/test"));
}

Profiler output

对此有什么想法吗?根据文档,我调用服务的方式似乎是 Service Fabric 最佳实践。

更新:缓存 ServicePartioningClient 确实提高了性能,但是使用分区服务,我无法缓存客户端,因为我不知道给定 PartitionKey 的分区。

更新 2:很抱歉,我没有在最初的问题中提供完整的详细信息。在最初实现基于套接字的通信时,我们注意到 InvokeWithRetry 的巨大开销。

如果您使用 http 请求,您不会太注意到它。 http 请求已经花费了大约 1 毫秒,因此为 InvokeWithRetry 添加 0.5 毫秒并不那么明显。

但是,如果您使用原始套接字,在我们的例子中大约需要 0.005 毫秒,为 InvokeWithRetry 添加 0.5 毫秒的开销是巨大的!

这是一个 http 示例,使用 InvokeAndRetry 需要 3 倍的时间:

public async Task RunTest()
{
var factory = new HttpCommunicationClientFactory();
var uri = new Uri("fabric:/MyApp/MyService");
var count = 10000;

// Example 1: ~6000ms
for (var i = 0; i < count; i++)
{
var pClient1 = new ServicePartitionClient<HttpCommunicationClient>(factory, uri, new ServicePartitionKey(1));
await pClient1.InvokeWithRetryAsync(c => c.HttpClient.GetAsync(c.Url));
}

// Example 2: ~1800ms
var pClient2 = new ServicePartitionClient<HttpCommunicationClient>(factory, uri, new ServicePartitionKey(1));
HttpCommunicationClient resolvedClient = null;
await pClient2.InvokeWithRetryAsync(
c =>
{
resolvedClient = c;
return Task.FromResult(true);
});

for (var i = 0; i < count; i++)
{
await resolvedClient.HttpClient.GetAsync(resolvedClient.Url);
}
}

我知道 InvokeWithRetry 添加了一些我不想错过的好东西。但是否需要在每次调用时解析分区?

最佳答案

我认为最好能够实际进行基准测试并看看实际的差异是什么。我使用有状态服务创建一个基本设置,该服务打开 HttpListener 和一个以三种不同方式调用该服务的客户端:

  • 为每个调用创建一个新客户端并按顺序执行所有调用

    for (var i = 0; i < count; i++)
    {
    var client = new ServicePartitionClient<HttpCommunicationClient>(_factory, _httpServiceUri, new ServicePartitionKey(1));
    var httpResponseMessage = await client.InvokeWithRetryAsync(c => c.HttpClient.GetAsync(c.Url + $"?index={id}"));
    }
  • 仅创建客户端一次,并按顺序在每次调用中重用它

    var client = new ServicePartitionClient<HttpCommunicationClient>(_factory, _httpServiceUri, new ServicePartitionKey(1));
    for (var i = 0; i < count; i++)
    {
    var httpResponseMessage = await client.InvokeWithRetryAsync(c => c.HttpClient.GetAsync(c.Url + $"?index={id}"));
    }
  • 为每个调用创建一个新客户端并并行运行所有调用

    var tasks = new List<Task>();
    for (var i = 0; i < count; i++)
    {
    tasks.Add(Task.Run(async () =>
    {
    var client = new ServicePartitionClient<HttpCommunicationClient>(_factory, _httpServiceUri, new ServicePartitionKey(1));
    var httpResponseMessage = await client.InvokeWithRetryAsync(c => c.HttpClient.GetAsync(c.Url + $"?index={id}"));
    }));
    }
    Task.WaitAll(tasks.ToArray());

然后我对一些计数进行了测试以获得平均值:

Per call duration for different client and call models

现在,这应该是真实的,而不是在受控环境中进行完整和全面的测试,有很多因素会影响此性能,例如集群大小、调用的服务实际执行的操作(在这种情况下,实际上什么都没有)以及有效负载的大小和复杂性(在这种情况下是一个非常短的字符串)。

在此测试中,我还想了解 Fabric 传输的行为方式以及性能与 HTTP 传输类似(老实说,我预计会稍好一些,但在这个简单的场景中可能不可见)。

值得注意的是,并行执行 10,000 个调用时,性能显着下降。这可能是由于该服务耗尽了工作内存。这样做的结果可能是某些客户端调用出现故障并在延迟后重试(待验证)。我测量持续时间的方法是直到所有调用完成为止的总时间。同时应该注意的是,测试并没有真正允许服务使用多个节点,因为所有调用都路由到同一个分区。

总而言之,重用客户端的性能影响是名义上的,对于简单的调用,HTTP 的执行类似于 Fabric Transport。

关于.net - Azure Service Fabric InvokeWithRetryAsync 巨大的开销,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41790829/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com