我一直在努力寻找一种方法来提高这项任务的效率。我正在使用基于REST的Web服务,需要更新超过2500个客户端的信息。
我正在使用fiddler来查看请求,并且我还在更新表时更新了表。我每秒得到大约1个响应。我的期望值很高吗?我甚至不确定我要定义的是什么?快速'在这种情况下。
我正在处理控制器中的所有内容,并尝试根据该地点的示例并行运行多个Web请求,但它似乎没有什么区别。说实话,我不太了解它,只是试图让它建立起来。我怀疑它仍在等待每个请求完成再次发射之前。
我还根据另一个建议增加了我的网络配置文件中的连接但没有成功:
<system.net>
<connectionManagement>
<add address="*" maxconnection="20" />
</connectionManagement>
</system.net>
我的控制器操作方法如下所示:
public async Task<ActionResult> UpdateMattersAsync()
{
//Only get matters we haven't synced yet
List<MatterClientRepair> repairList = Data.Get.AllUnsyncedMatterClientRepairs(true);
//Take the next 500
List<MatterClientRepair> subRepairList = repairList.Take(500).ToList();
FinalisedMatterViewModel vm = new FinalisedMatterViewModel();
using (ApplicationDbContext db = new ApplicationDbContext())
{
int jobCount = 0;
foreach (var job in subRepairList)
{
// If not yet synced - it shouldn't ever be!!
if (!job.Synced)
{
jobCount++;
// set up some Authentication fields
var oauth = new OAuth.Manager();
oauth["access_token"] = Session["AccessToken"].ToString();
string uri = "https://app.com/api/v2/matters/" + job.Matter;
// prepare the json object for the body
MatterClientJob jsonBody = new MatterClientJob();
jsonBody.matter = new MatterForUpload();
jsonBody.matter.client_id = job.NewClient;
string jsonString = jsonBody.ToJSON();
// Send it off. It returns the whole object we updated - we don't actually do anything with it
Matter result = await oauth.Update<Matter>(uri, oauth["access_token"], "PUT", jsonString);
// update our entities
var updateJob = db.MatterClientRepairs.Find(job.ID);
updateJob.Synced = true;
updateJob.Update_Time = DateTime.Now;
db.Entry(updateJob).State = System.Data.Entity.EntityState.Modified;
if (jobCount % 50 == 0)
{
// save every 50 changes
db.SaveChanges();
}
}
}
// if there are remaining files to save
if (jobCount % 50 != 0)
{
db.SaveChanges();
}
return View("FinalisedMatters", Data.Get.AllMatterClientRepairs());
}
}
当然还有处理Web请求的Update方法本身:
public async Task<T> Update<T>(string uri, string token, string method, string json)
{
var authzHeader = GenerateAuthzHeader(uri, method);
// prepare the token request
var request = (HttpWebRequest)WebRequest.Create(uri);
request.Headers.Add("Authorization", authzHeader);
request.Method = method;
request.ContentType = "application/json";
request.Accept = "application/json, text/javascript";
byte[] bytes = System.Text.Encoding.ASCII.GetBytes(json);
request.ContentLength = bytes.Length;
System.IO.Stream os = request.GetRequestStream();
os.Write(bytes, 0, bytes.Length);
os.Close();
WebResponse response = await request.GetResponseAsync();
using (var reader = new System.IO.StreamReader(response.GetResponseStream()))
{
return JsonConvert.DeserializeObject<T>(reader.ReadToEnd());
}
}
如果每秒不能超过1个请求,那么我有兴趣查看Ajax解决方案,这样我就可以在处理时给用户一些反馈。在我目前的解决方案中,我无法在操作方法未达到“返回”状态时向用户提供反馈。我可以吗?
答案 0 :(得分:2)
好的,我花了几天时间(以及大量的试验和错误),但我已经解决了这个问题。希望它可以帮助别人。我终于找到了我的银弹。它可能是我应该开始的地方: MSDN: Consuming the Task-based Asynchronous Pattern
最后,下面这行代码就是最重要的一步。
string [] pages = await Task.WhenAll(from url in urls select DownloadStringAsync(url));
我替换了一些东西,使其适用于Put请求,如下所示:
HttpResponseMessage[] results = await Task.WhenAll(from p in toUpload select client.PutAsync(p.uri, p.jsonContent));
'toUpload'是MyClass的列表:
public class MyClass
{
// the URI should be relative to the base pase
// (ie: /api/v2/matters/101)
public string uri { get; set; }
// a string in JSON format, being the body of the PUT request
public StringContent jsonContent { get; set; }
}
关键是停止尝试将我的PutAsync方法放入循环中。我的新代码行仍然阻塞,直到所有响应都回来,但这就是我想要的。另外,了解我可以使用这个LINQ样式表达式来动态创建任务列表是非常有帮助的。我不会发布所有代码(除非有人想要它),因为它不像原版那样很好地重构,我仍需要检查每个项目的响应是否为200 OK,然后我将其记录为成功保存在我的数据库中。那么它的速度有多快?
我测试了来自本地计算机的50个Web服务调用的示例。 (最后,Azure中的SQL数据库有一些保存记录)。
原始同步代码:70.73秒
异步代码:8.89秒
从每秒1.4146个请求下降到每秒0.1778个请求! (如果你将其平均化)
<强>结论强>
我的旅程尚未结束。我刚刚触及异步编程的表面并且非常喜欢它。我现在需要弄清楚如何只保存返回200 OK的结果。我可以反序列化HttpResponse,它返回一个JSON对象(它有一个我可以查找的唯一ID等)。或者我可以使用Task.WhenAny方法,并尝试交错。