Processing after the request was completed

2019-07-16 11:05发布

I have one API endpoint that needs to return to the caller as soon as possible. It currently returns under 1ms. If, however, I log some stuff to the database it now takes closer to 10ms.

How would you approach completing the request and then doing some kind of processing after the request was completed? I tried with Response.Body.Flush(), but that doesn't complete the request and it still takes the full 10ms. It looks like it's sending/flushing the payload, but the request still doesn't complete until the action method is completed.

Would it work to do the logging in a middleware?

EDIT:

One workaround that I found was to use FluentScheduler like this:

JobManager.Initialize(new Registry());
JobManager.Start();

in Program.cs before host.Run() and then schedule an immediate task from an action like this:

JobManager.AddJob(() =>
{
    // do something...
}, (s) => s.ToRunOnceIn(1).Seconds());

4条回答
forever°为你锁心
2楼-- · 2019-07-16 11:34

Async is the correct way to solve this problem. During your request (whenever you want) start an async connection/query to the database. Just don't wait for it to finish.

查看更多
Evening l夕情丶
3楼-- · 2019-07-16 11:41

Logging in middleware won't help you, as you will still be within the request pipeline and you may or may not have the necessary information to log what you need to do.

You could log asynchronously, which would probably give you the biggest improvement without major architectural changes: Async Programming : Introduction to Async/Await on ASP.NET

If that doesn't work, you can do something that is multithreaded. That gets more complicated, but it is still doable: Multithreading in C# .NET 4.5 (Part 1)

查看更多
姐就是有狂的资本
4楼-- · 2019-07-16 11:48

This is just a thought, so don't down-vote if I'm way off. Could you use HttpResponse.RegisterForDispose()? For example:

public class JobToRunAfterRequestIsComplete : IDisposable {
    public void Dispose()
    {
        // Do your post-processing here.
    } 
}

public class YourController {
    public void YourAction() {
        // Do your normal processing here
        ...
        // Register your post processing here
        this.Response.RegisterForDispose(new JobToRunAfterRequestIsComplete ());
    }
}
查看更多
再贱就再见
5楼-- · 2019-07-16 11:53

You need to start somehow a different thread. A middleware put in front of the pipeline will give you the chance to do work just before the request completes. If you spin a thread, from there, then it might work.

You probably want to use some sort of producer-consumer pattern so you don't kill your server. Otherwise, if every request starts a thread that does work immediately and you have many requests at the same time, you might end up running out of resources. A producer-consumer would help you throttle that work.

If you're not in a hurry, you can wait another week or so when I'll deliver the file logger for ASP.NET and then you'll see a similar implementation there.

查看更多
登录 后发表回答