我需要通过系统中的所有用户创建Web API搜索功能。 客户端(使用电话)使用端点向我发送请求:
HTTP 1.1 GET http://sf.cluster:80/
Path /search/users?q=Aa&take=10
q 是在搜索字段中输入的字符串用户。 接受 - 手机想要显示多少条目。
我从Azure存储表中上载了我的Reliable Dictionary 89000项目。它有结构:
IReliableDictionary<Guid, string>
我的搜索方法如下:
public async Task<IEnumerable<UserInfo>> Search(string q, int take)
{
var usersDictionary = await GetUsersDictionary();
IEnumerable<UserInfo> results;
using (var tx = StateManager.CreateTransaction())
{
var searchResults = (from r in (await usersDictionary.CreateEnumerableAsync(tx)).ToEnumerable()
where r.Value.StartsWith(q, StringComparison.InvariantCultureIgnoreCase)
select new UserInfo()
{
Id = r.Key,
Name = r.Value
}).Take(take);
results = new List<UserInfo>(searchResults);
await tx.CommitAsync();
}
return results;
}
问题:它在手机上运行良好,我得到了我的预期。但是当我开始使用一堆请求(使用Soap UI工具同时约60个线程)推送我的端点时,超时开始从1秒上升到35秒!看起来我在某处犯了错误或者选择了错误的搜索实现方式。
有人实现了这样的功能吗?任何人都可以帮助正确的搜索方法吗?
UPD:实施无状态服务,我在其中存储List<string>
名称并执行相同的操作(搜索列表)。结果:150-300ms。看起来我应该将List存储在状态中(在有状态服务中)并根据请求获取它。
答案 0 :(得分:4)
我不确定您的ToEnumerable
方法的实现方式是什么,但我发现的大部分都是一个相当懒惰的实现,只是将异步可枚举并将其复制到列表中。现在,凭借一份可靠的890,000元的字典,效率很低。此外,事务就像一个互斥体,因此当您复制这个巨大的列表时,您将锁定底层集合。我建议在this library中检查AsyncEnumerable linq实现,因为它实现了一种将linq与服务结构AsyncEnumerable一起使用的有效方法。使用它,您的搜索将如下所示:
using (var tx = StateManager.CreateTransaction())
{
var enumerable = await usersDictionary.CreateEnumerableAsync(tx);
results = await enumerable.Where(kvp=>kvp.Value.StartsWith(q, StringComparison.InvariantCultureIgnoreCase))
.Select(kvp=> new UserInfo()
{
Id = r.Key,
Name = r.Value
})
.Take(take)
.ToListAsync(tx);
}
另外,作为附注,由于您不以任何方式修改基础集合,因此您不需要提交事务。提交事务只是告诉状态管理器您已修改状态并且您已完成更改的方法,然后它会将更改的值传播到辅助节点。您甚至可以在辅助节点上调用此方法,如果这是一个读取较重的状态,但请注意,写入可能尚未传播。