我正在评估新奥尔良的一个新项目,我们即将开始。
最终,我们想要运行一群持之以恒的演员,但我目前正在努力争取在记忆版的新奥尔良成为演员的底线。
给出以下粮食
using Common.UserWallet;
using Common.UserWallet.Messages;
using Microsoft.Extensions.Logging;
namespace Grains;
public class UserWalletGrain : Orleans.Grain, IUserWalletGrain
{
private readonly ILogger _logger;
public UserWalletGrain(ILogger<UserWalletGrain> logger)
{
_logger = logger;
}
public async Task<CreateOrderResponse> CreateOrder(CreateOrderCommand command)
{
return new CreateOrderResponse(Guid.NewGuid());
}
public Task Ping()
{
return Task.CompletedTask;
}
}以下筒仓配置:
static async Task<IHost> StartSiloAsync()
{
ServicePointManager.UseNagleAlgorithm = false;
var builder = new HostBuilder()
.UseOrleans(c =>
{
c.UseLocalhostClustering()
.Configure<ClusterOptions>(options =>
{
options.ClusterId = "dev";
options.ServiceId = "OrleansBasics";
})
.ConfigureApplicationParts(
parts => parts.AddApplicationPart(typeof(HelloGrain).Assembly).WithReferences())
.AddMemoryGrainStorage("OrleansMemoryProvider");
});
var host = builder.Build();
await host.StartAsync();
return host;
}以及以下客户端代码:
static async Task<IClusterClient> ConnectClientAsync()
{
var client = new ClientBuilder()
.UseLocalhostClustering()
.Configure<ClusterOptions>(options =>
{
options.ClusterId = "dev";
options.ServiceId = "OrleansBasics";
})
//.ConfigureLogging(logging => logging.AddConsole())
.Build();
await client.Connect();
Console.WriteLine("Client successfully connected to silo host \n");
return client;
}
static async Task DoClientWorkAsync(IClusterClient client)
{
List<IUserWalletGrain> grains = new List<IUserWalletGrain>();
foreach (var _ in Enumerable.Range(1, 100))
{
var walletGrain = client.GetGrain<IUserWalletGrain>(Guid.NewGuid());
await walletGrain.Ping(); //make sure grain is loaded
grains.Add(walletGrain);
}
var sw = Stopwatch.StartNew();
await Parallel.ForEachAsync(Enumerable.Range(1, 100000), async (o, token) =>
{
var command = new Common.UserWallet.Messages.CreateOrderCommand(Guid.NewGuid(), 4, 5, new List<Guid> { Guid.NewGuid(), Guid.NewGuid() });
var response = await grains[o % 100].CreateOrder(command);
Console.WriteLine($"{o%10}:{o}");
});
Console.WriteLine($"\nElapsed:{sw.ElapsedMilliseconds}\n\n");
}我能在30秒内发送100,000毫希。相当于每秒3333毫希。这比我在看(https://github.com/yevhen/Orleans.PingPong)时所期望的要少得多。
如果我从10粒、100粒或1000粒开始,这似乎也不重要。
当我在配置表存储时添加持久性时
.AddAzureTableGrainStorage(
name: "OrleansMemoryProvider",
configureOptions: options =>
{
options.UseJson = true;
options.ConfigureTableServiceClient(
"secret);
})还有一张单曲
等待WriteStateAsync();在CreateOrder中,情况急剧恶化,大约为280个msgs /s
当我更进一步并实现一些基本的域逻辑时。打电话给其他演员等,我们基本上是以1.2msgs/s的速度慢吞吞的
怎么回事?
编辑:
发布于 2022-11-04 00:28:19
构建高性能的应用程序可能是棘手的和微妙的。在新奥尔良的一般解决方案是,您有许多谷物和许多调用者,因此您可以获得高度的并发性,从而实现吞吐量。在您的例子中,您有许多谷物(100),但是很少有调用者(我相信在默认情况下,Parallel.ForEachAsync是每个核心的一个),每个调用者在每次调用之后都会写信到控制台,这将大大减慢速度。
如果我删除Console.WriteLine并在我的机器上使用Ororle7.0-Rc2运行您的代码,那么对100个谷物的100 K调用将在850 my内完成。如果我将CreateOrderRequest & CreateOrderResponse类型从类更改为结构,则持续时间将减少到750 If。
如果我运行一个更优化的ping测试(一个来自新奥尔良存储库的测试),我会看到我的计算机上每秒大约有550 K请求,其中一个客户端和一个筒仓进程共享同一个CPU。新奥尔良3.x的数字大约是这个数字的一半。如果我在筒仓进程中共同托管客户机(即从筒仓的IServiceProvider中提取IServiceProvider),那么每秒将看到超过500万次请求。
一旦你开始在你的谷物中做一些不平凡的工作,你就会开始遇到其他的限制。最近,我测试了从同一个过程中调用一个粒,发现一个粒可以处理500 K的RPS,如果它正在做一些琐碎的工作(乒乓)。如果每个请求都必须对存储进行写入,而每次存储写入都需要1ms,那么它将无法处理超过1000个RPS,因为每个调用默认等待上一次调用完成。如果您想要选择退出这种行为,可以通过在您的谷物上启用可重入性来做到这一点,如这里的文档:https://learn.microsoft.com/en-us/dotnet/orleans/grains/reentrancy所描述的那样。Chirper示例提供了更多关于如何使用存储更新实现重入的详细信息:https://github.com/dotnet/orleans/tree/main/samples/Chirper。
当谷物方法变得更加复杂,需要执行大量的I/O来服务每个请求(例如,存储更新和随后的谷物调用)时,每个单独的谷物的吞吐量就会下降,因为每个请求都需要更多的工作。希望上面的数字能给你一个大致的指导。
https://stackoverflow.com/questions/74310628
复制相似问题