我正在处理一个从数据库队列中选择消息的小型控制台应用程序 并将消息转发给rest api(ASP.NET Web Api)。
通常,应用程序执行以下步骤:
使程序更灵活,并能够处理每条消息 在单独的数据库转换中,步骤2 - 3将作为任务执行。
这意味着如果数据库中有四条消息,我们将有四个任务 应该几乎并行运行并处理消息。
这就是代码的样子:
数据库消息
public class DatabaseMessage
{
public string Message { get; set; }
}
UnitOfWork(接口IUnitOfWork)
public class UnitOfWork
{
// ... extermely simplified
public int GetNumberOfPendingMessages() { ... }
public DatabaseMessage GetNextPendingMessage() { ... }
public void DeleteMessage(DatabaseMessage message) { ... }
}
HttpService(接口IHttpService)
public class HttpService
{
private readonly HttpClient _httpClient;
public HttpService()
{
_httpClient = new HttpClient();
/* Some initalization stuff for the HttpClient object */
}
public async Task<HttpResponse> PostMessage(DatabaseMessage message)
{
var content = /* Create content object */
return await _httpClient.PostAsync(..., content);
}
}
MessageProcessingService(接口IMessageProcessingService)
public class MessageProcessingService
{
private readonly IHttpService _httpService;
private readonly Semaphore _databaseProcessingSemaphore;
public MessageProcessingService(IHttpService httpService)
{
_httpService = httpService;
}
public async Task ProcessDatabaseMessages()
{
var unitOfWork = new UnitOfWork();
var numberOfPendingMessages = unitOfWork.GetNumberOfPendingMessages();
var messageProcessingTasks = new List<Task>();
for(int t = 0; t < numberOfPendingMessages; t++)
{
messageProcessingTasks.Add(new Task(() => {
ProcessMessageAsTask();
}));
}
var continuationHandler = Task.WhenAll(messageProcessingTasks);
messageProcessingTasks.ForEach(e => e.Start());
await continuationHandler;
}
private void ProcessMessageAsTask()
{
// Single unit of work for each tasks
var unitOfWork = new UnitOfWork();
try{
// Starting a database transaction
unitOfWork.StartTransaction();
_databaseProcessingSemaphore.OnWait();
var message = unitOfWork.GetNextPendingMessage();
_databaseProcessingSemaphore.Release();
if(message != null)
{
var response = _httpService.PostMessage(message).Result;
if(response == HttpStatus.OK)
{
unitOfWork.DeleteMessage(message);
unitOfWork.Commit();
}
else
{
unitOfWork.Rollback();
}
}
else
{
unitOfWork.Commit();
}
}
catch(Exception ex)
{
unitOfWork.Rollback();
// Further error handling...
}
}
}
为了更好地理解,HttpClient对象由Unity创建和管理并被注入 进入MessageProcessingService对象。 HttpClient在容器中保持为单例。
我现在面临调用方法_httpService.PostMessage()
的问题。例如,如果有五个
消息队列中的消息,调用失败五次,异常告诉我任务已被取消。
我的问题是,现在.NET HttpClient的PostAsync调用有什么问题?该问题是由.Result
选项引起的还是会引起的
为每个消息处理任务创建一个新的HttpClient实例会更好吗?
或者,具有任务的架构和休息api调用的处理是否存在普遍问题?
更新2018-04-04 - 08:09
我现在使方法ProcessMessageAsTask
异步,我现在正在等待HttpService
的调用。
但现在我根本没有任何例外。在资源监视器和调试过程中,我可以看到所有任务都可以调用HttpClient
(return await _httpClient.PostAsync(..., content);
)
但是没有例外,也不会发布消息。但我没有任何例外。该程序将在HttpClient
的调用后立即关闭。所有进一步的陈述都没有得到处理。
的变化:
public async Task ProcessDatabaseMessages()
{
var unitOfWork = new UnitOfWork();
var numberOfPendingMessages = unitOfWork.GetNumberOfPendingMessages();
var messageProcessingTasks = new List<Task>();
for(int t = 0; t < numberOfPendingMessages; t++)
{
messageProcessingTasks.Add(new Task(async () => {
await ProcessMessageAsTask();
}));
}
var continuationHandler = Task.WhenAll(messageProcessingTasks);
messageProcessingTasks.ForEach(e => e.Start());
await continuationHandler;
}
private async Task ProcessMessageAsTask()
{
// Single unit of work for each tasks
var unitOfWork = new UnitOfWork();
try{
// Starting a database transaction
unitOfWork.StartTransaction();
_databaseProcessingSemaphore.OnWait();
var message = unitOfWork.GetNextPendingMessage();
_databaseProcessingSemaphore.Release();
if(message != null)
{
var response = await _httpService.PostMessage(message);
if(response == HttpStatus.OK)
{
unitOfWork.DeleteMessage(message);
unitOfWork.Commit();
}
else
{
unitOfWork.Rollback();
}
}
else
{
unitOfWork.Commit();
}
}
catch(Exception ex)
{
unitOfWork.Rollback();
// Further error handling...
}
}