Original link
Most of the online resources for async/await assume that you are developing a client application, but is there an async location on the server? Can be very sure to answer "yes". This article is a conceptual overview of asynchronous requests on ASP. NET, and provides a reference to the best online resources. I'm not going to introduce async or await syntax, because I'm already in an introductory blog post (BIT.LY/19IKOGW) and an article on Async best Practices (msdn.microsoft.com/magazine/jj991977) I've introduced it. This article will focus specifically on how async works on ASP.
For client applications, such as the Windows Store, Windows desktop, and Windows Phone applications, the main advantage of async is excellent responsiveness. These types of applications use async primarily to ensure the responsiveness of the user interface. For server applications, the main advantage of async async is good scalability. The key to node. JS Extensibility is its inherent asynchronous nature; Open Web Interface for. NET (OWIN) is a completely new design for async, and ASP. Async: Not just for UI applications!
Synchronous and asynchronous request processing
Before delving into the asynchronous request handler, I want to briefly review how the synchronous request handler works on ASP. In this example, assume that the requests in the system depend on some external resources, such as databases or Web APIs. When a request is received, ASP. NET assigns one of the thread pool threads to the request. Because it is written synchronously, the request handler calls the external resource synchronously. This will block the request thread until a call to the external resource is returned. Figure 1 illustrates a thread pool with two threads, one of which is blocked and waiting for an external resource.
Figure 1 Synchronizing waiting for external resources
Finally, the call to the external resource is returned, and the request thread resumes processing the request. When the request is completed and the response is ready to be sent, the request thread is returned to the thread pool.
It's all good, but your ASP. The request will always exceed the number of threads that its thread can handle. At this point, additional requests must wait until the thread is available to run. Figure 2 illustrates that the dual-threaded server is still on, at which point it receives three requests.
Figure 2 Two-thread server receiving three requests
In this case, the first two requests are assigned to threads in the thread pool. Each request calls an external resource and then blocks their threads. The third request must wait for a thread to be available before it can begin processing, but the request is already in the system. Its timer has been working, and it is at risk of HTTP error 503 (Service unavailable).
But consider this: the third request is waiting for the available thread, while the other two threads in the system actually do nothing. These threads are blocked and are waiting for an external call to be returned. They do not actually do any real work; they are not running, and they do not consume any CPU time. These threads are wasted, but requests are in demand. The following are the cases where asynchronous requests are resolved.
Asynchronous request handlers operate in a different way than this. When a request is received, ASP. NET assigns one of the thread pool threads to the request. This time, the request handler will invoke the external resource asynchronously. This request thread is returned to the thread pool before the call to the external resource is returned. Figure 3 illustrates a thread pool with two threads when the request waits for an external resource asynchronously.
Figure 3 Async waiting for external resources
The important difference is that, in the process of making an asynchronous call, the request thread is returned to the thread pool. When a thread is in the thread pool, it is no longer associated with the request. At this point, when an external resource call is returned, ASP. NET assigns a thread in its thread pool to the request. The thread will continue to process the request. When the request is complete, the thread returns to the thread pool again. Note that for a synchronization handler, the same thread is used for the entire life cycle of the request, whereas for an asynchronous handler, different threads can be assigned to the same request (at different times).
Now, if all three requests come in, the server can be easily processed. Because each time the request waits for asynchronous work, the threads are freed into the thread pool, and they are free to handle new and existing requests. Asynchronous requests allow a small number of threads to handle a larger number of requests. Therefore, the main advantage of asynchronous code on ASP. is excellent extensibility.
Why not increase the size of the thread pool?
At this point, you will always be asked: Why not increase the size of the thread pool? There are two answers: Async code expands deeper and faster than blocking thread pool threads.
Async code is more extensible than a blocking thread because it uses less memory, and on modern operating systems, each thread pool threads have a 1MB stack, plus a non-paged kernel stack. This may sound like a lot, but when you have a bunch of threads on your server, you'll find that it's not really enough. In contrast, the memory overhead of an asynchronous operation is much smaller. Therefore, requests that use asynchronous operations face less memory pressure than requests that use blocking threads. Asynchronous code allows you to use more memory for other tasks, such as caching.
Asynchronous code is faster than blocking threads, because the thread pool is injected at a limited rate. At press time, the speed was one thread per two seconds. Limited injection speed is a good thing; it avoids ongoing threading build-up and destruction. However, consider what happens when the request comes in droves. The synchronization code can easily become paralyzed because the request will run out of all available threads, and the remaining requests must wait for the thread pool to have new threads injected. Asynchronous code, on the other hand, does not need to have such a restriction; it is "always open", so to speak. Asynchronous code can respond better to sudden fluctuations in request volume.
Keep in mind that async code does not replace thread pooling. You should not only have a thread pool or asynchronous code, but have both a thread pool and asynchronous code. Asynchronous code allows your application to take full advantage of the thread pool. It uses the existing thread pool and raises it to 11.
How does a thread perform asynchronous work?
I've been asked this question. This means that there must be some threads that prevent I/O calls to external resources. Therefore, the asynchronous code frees the request thread, but only at the expense of another thread in the system? No, it doesn't matter.
To understand why an asynchronous request is extended, I'll keep track of a (simplified) example of an asynchronous I/O call. Suppose you have a request that needs to be written to a file. The request thread invokes the asynchronous write method. WriteAsync is implemented by the base Class library (BCL) and uses its completion port for asynchronous I/O. Therefore, the WriteAsync call is passed to the OS as an asynchronous file write. The OS then communicates with the driver stack and passes the data to write to the I/O request packet (IRP).
Now, the interesting thing has happened: if the device driver cannot process the IRP immediately, it must be processed asynchronously. Therefore, the driver tells the disk to start writing and returns a "pending" response to the OS. The OS passes the "pending" response to the BCL, and the BCL returns an incomplete task to the request processing code. The request processing code waits for the task to return the incomplete task from the method, and so on. Finally, the request processing code eventually returns an incomplete task to ASP, and the request thread is freed back to the thread pool.
Now, consider the current state of the system. Various I/O structures have been assigned (for example, task instances and IRP), and they are in a pending/incomplete state. However, no threads are blocked because they wait for the write operation to complete. The ASP. NET, BCL, OS, and device drivers do not have threads dedicated to working asynchronously.
When the disk finishes writing data, it notifies its driver by interrupting. The driver notifies the OS that the IRP has been completed, and the OS notifies the BCL through the completion port. thread pool threads should be notified by completing the task returned from WriteAsync, which in turn restores the asynchronous request code. In the completion notification phase, some threads are "borrowed" in the short term, but no threads are actually blocked during the write process.
This example is greatly simplified, but the main point is that true asynchronous work does not require a thread. Actual push bytes do not require CPU time. There is also an auxiliary course to understand. Consider how the device driver can handle an IRP immediately or asynchronously in the world of device drivers. Synchronous processing is not an option. At the device driver level, all important I/O is asynchronous. Many developers think of the "normal API" for I/O operations as synchronous, and the asynchronous API as a layer based on the common synchronization API. However, this is exactly the opposite: in fact, the common API is?? asynchronous I/O is the synchronous api! that is implemented using asynchronous I/Os
Why is there no asynchronous handler?
If asynchronous request processing is so perfect, why is it not available? In fact, asynchronous code is well-suited for scaling, so since the beginning of the Microsoft. NET Framework, the ASP has always supported asynchronous handlers and modules. ASP. NET 2.0 introduces asynchronous Web pages, and MVC in ASP 2 gets an asynchronous controller.
Recently, however, asynchronous code has always had problems writing and is difficult to maintain. Many companies decide to develop code synchronously, pay for a larger farm, or more expensive hosting, which can be simpler. Now, there's a reversal: in ASP. NET 4.5, asynchronous code that uses async and await is almost as simple as writing synchronous code. As large systems migrate to cloud hosting and require greater scale, more and more companies are beginning to favor async and await on ASP.
Async code is not a panacea
Asynchronous request processing, while powerful, does not solve all problems. There are some common misconceptions about what async and await can do on ASP.
When some developers learn about async and await, they think it's a way for server code to "compromise" to the client (such as a browser). However, async and await on ASP. NET are only "concession" to the ASP. HTTP protocol remains unchanged, and you still have only one response for each request. If you need SignalR or Ajax or UpdatePanel before async/await, then you still need SignalR or Ajax or UpdatePanel after async/await.
Asynchronous request processing using async and await can help expand your application size. However, this is an extension on a single server, and you may still need to plan for the extension. If you do need to extend the architecture, you will still need to consider stateless idempotent requests and reliable queues. Async/await is helpful: they enable you to take full advantage of server resources, so you do not need to scale frequently. However, if you do need to scale out, you will need a suitable distributed architecture.
Async and await are all about I/O on ASP. They are ideal for reading and writing files, database records, and REST APIs. However, they do not perform well with CPU-intensive tasks. You can start some background work by waiting for Task.run, but that doesn't make any sense. In fact, using heuristics to interfere with the ASP. NET thread pool can compromise your scalability. If you are doing a lot of CPU-intensive work on ASP, the best way is to perform the work directly on the request thread. In general, do not queue work to a thread pool on ASP.
Finally, the scalability of the system is considered as a whole. Ten years ago, the common architecture was to have an ASP. NET Web server that can communicate with the backend SQL Server database. In this simple architecture, the database server is typically a bottleneck for scalability, not a WEB server. Making your database calls asynchronous may not help, and of course you can use them to extend the WEB server, but the database server will block the expansion of the entire system.
In his wonderful blog post, Rick Anderson gives a case for an asynchronous database call, "should my database call be asynchronous?" "(Bit.ly/1rw66ub). Here are two support arguments: First, async code is difficult (so developers have a higher time cost than just buying a larger server), and secondly, if the database backend is a bottleneck, then extending the WEB server makes no sense. In writing this article, these two arguments are very reasonable, but over time the meaning of these two arguments has slowly weakened. First, it's much easier to write asynchronous code using async and await. Second, as the world gradually adopts cloud computing, the data back end of the website is gradually expanded. Modern backend, such as Microsoft Azure SQL database, NoSQL, and other APIs, can be extended further than a single SQL Server, pushing the bottleneck back to the WEB server. In this case, async/await can offer a huge advantage by extending ASP.
Before you start
First you need to know that only ASP. 4.5 supports Async and await. There is a NuGet package called Microsoft.Bcl.Async that enables Async and await for the. NET Framework 4, but does not use it; this will not work properly! The reason for this is that, in order to work better with async and await, ASP. NET itself must change the way it manages asynchronous request processing; The NuGet package contains all the types required by the compiler, but does not patch the ASP. There is no workaround; you need ASP. NET 4.5 or later.
Next, be aware that ASP. NET 4.5 introduces the "quirks mode" on the server. If you create a new ASP. NET 4.5 project, you don't have to worry about it. However, if you are upgrading an existing project to ASP. NET 4.5, all quirk will be opened. I suggest you?? Close all of them by editing the Web. config and setting Httpruntime.targetframework to 4.5. If an application that uses this setting fails (and you do not want to take the time to fix it), at least you can get async by adding the value "true" for the Aspnet:usetaskfriendlysynchronizationcontext appsetting key /await work. If you set Httpruntime.targetframework to 4.5, the AppSetting key is not necessary. The Web development team has published a blog about this new "quirks model" for more information in BIT.LY/1PBMNZK. Tip: If you see strange behavior or exceptions, and your call stack includes Legacyaspnetsynchronizationcontext, your application is running in this "quirks mode." Legacyaspnetsynchronizationcontext is incompatible with asynchronous; You need regular aspnetsynchronizationcontext on ASP.
in ASP. NET 4.5, all the ASP. NET settings set good default values for asynchronous requests, but there are several other settings that you might want to change. The first is IIS settings: Consider raising the queue limit for Iis/http.sys (Application pool | Advanced Settings | Queue Length) from the default of 1,000 to 5,000. The other is the. NET runtime setting: Servicepointmanager.defaultconnectionlimit, which defaults to 12 times times the number of cores. DefaultConnectionLimit the number of outgoing connections that are limited to the same host name.
Tips for aborting a request
When ASP. NET synchronously processes a request, it has a very simple mechanism to abort the request (for example, if the request exceeds its timeout value): It aborts the worker thread for the request. This makes sense, because in the synchronization realm, each request uses the same worker thread from start to finish. Aborting a thread is not perfect for the long-term stability of the AppDomain, so by default, ASP. NET will periodically recycle your application to keep it clean.
For asynchronous requests, if you want to abort the request, ASP. NET does not abort the worker thread. Instead, it cancels the request to use CancellationToken. The asynchronous request handler should accept and follow the cancellation token. Most newer frameworks, including Web APIs, MVC, and SignalR, will be built and delivered directly to you cancellationtoken; all you need to do is declare it as a parameter. You can also directly access the ASP. Httprequest.timedouttoken is a cancellationtoken that is canceled when the request times out.
As applications migrate to the cloud, aborting requests becomes more important. Cloud-based applications are also increasingly dependent on external services that may consume arbitrary amounts of time. For example, a standard pattern is to retry an external request using exponential fallback, and if your application relies on multiple services like this, it is a good idea to apply a time-out limit to your request processing as a whole.
Status of Async support
Many libraries have been updated for compatibility issues with Async. Async support has been added to the Entity Framework (in the EntityFramework NuGet package) in version 6. However, when running asynchronously, you must be careful to avoid lazy loading because lazy loading is always performed synchronously. HttpClient (in the Microsoft.Net.Http NuGet package) is a modern Http client designed with the async concept and is ideal for calling external REST APIs; HttpWebRequest and WebClient Modern version of the replacement product. In version 2.1, the Microsoft Azure Storage client library (in the Windowsazure.storage NuGet package) added asynchronous support.
Newer frameworks, such as Web APIs and SignalR, provide comprehensive support for async and await. Individual Web APIs have built an entire pipeline around Async support: not only asynchronous controllers, but also asynchronous filters and handlers. Web APIs and SignalR have a very mundane asynchronous story: You can "go ahead" and "succeed".
This brings us to a sad story: Today, ASP. NET MVC only partially supports async and await. There is basic support--the operation and cancellation of the asynchronous controller is working properly. A great tutorial on how to use asynchronous controller operations in ASP. BIT.LY/1M1LXTX is available on the ASP. This is a great resource for getting started with async on MVC. Unfortunately, ASP. NET MVC (currently) does not support asynchronous filters (BIT.LY/1OAYHLC) and footstep operations (BIT.LY/1PX47RG).
An ASP. NET Web form is an older framework, but it also fully supports async and await. Also, tutorials on asynchronous Web Forms on the ASP. NET site are a great resource for getting Started (bit.ly/ydho7w). With Web forms, asynchronous support has the option to join. You must set Page.async to True before you can use PageAsyncTask to register for asynchronous work (or you can use the Async void event handler). PageAsyncTask also supports cancellation.
If you have a custom HTTP handler or HTTP module, then ASP. NET can now also support their asynchronous version. HTTP handlers are supported through Httptaskasynchandler (BIT.LY/1NWPWFJ), and HTTP modules are supported through Eventhandlertaskasynchelper (BIT.LY/1M1SN4O).
As of press time, the ASP. NET team is developing a new project called ASP. VNext. In VNext, the entire pipeline is asynchronous by default. Currently, the program incorporates MVC and Web APIs into a single framework that fully supports ASYNC/AWAIT, including asynchronous filters and asynchronous view components. Other asynchronous-ready frameworks, such as SignalR, will find a natural home in VNext. Of course, the future is the world of async.
Respect safety nets
Several new "security nets" have been introduced in ASP. NET 4.5 to help you capture asynchronous problems in your application. These are present by default and should be retained.
When the synchronization handler tries to perform asynchronous work, your InvalidOperationException receives a message that "the asynchronous operation cannot begin at this time." There are two main causes of this exception. First, the Web Forms page has an asynchronous event handler, but ignores the setting of Page.async to True. Second, the synchronous code calls the Async void method. This is also another reason to avoid async void.
Another safety net is for asynchronous handlers: When the asynchronous handler finishes the request, but ASP. NET detects that the asynchronous work has not yet completed, your InvalidOperationException receives the message, "the Async module or handler is complete, However, the asynchronous operation is still in a pending state. This is usually caused by asynchronous code calling async void methods, but it may also be due to improper use of the event-based Asynchronous Pattern (EAP) component (BIT.LY/19VDUWU).
You can also use an option to turn off both safety nets: httpcontext.allowasyncduringsyncstages (It can also be set in Web. config). Some pages on the Internet suggest that you make such a setting when you see these exceptions. I totally disagree. Seriously, I don't know how this works. Disabling the safety net is a horrible idea. The only possible reason I can think of is that your code might have done some very advanced asynchronous processing (far beyond the range I've tried), you're a genius for multithreading. So, if you've read the whole article and you're yawning and thinking, "Come on, I'm not a rookie," then I think you could consider disabling the safety net. For the rest of us, this is a very dangerous option and should not be set unless you fully know the consequences.
Start using
Finally, finally! Are you ready to start using async and await? I appreciate your patience.
First, check out the "Async code is not a Panacea" section of this article to make sure that async/await is beneficial to your architecture. Next, update your application to ASP. NET 4.5 and turn off quirks mode (you can run it only to make sure that no interruption occurs). At this point, you can start to really sync/wait for work.
Start with "leaf". Think about how your request is handled and identifies any I/O-based operations, especially network-based operations. Common examples are database queries and commands, and calls to other WEB services and APIs. Choose one to start, and do some research to find the best choice to do this with async/await. There are many built-in BCL types in the. NET Framework 4.5 that are now asynchronously ready; For example, SmtpClient has a Sendmailasync method. Some types can provide asynchronous ready replacements, for example, HttpWebRequest and WEB clients can be replaced with HttpClient. If necessary, upgrade your library version; For example, the Entity Framework in EF6 has an asynchronous compatibility method.
However, avoid "false async" in the library. False Async is a phenomenon in which a component has an asynchronous ready API that is implemented only by encapsulating the synchronization API within the thread pool threads. This is counterproductive for achieving scalability on ASP. A typical example of false asynchrony is Newtonsoft json.net, a library that is otherwise excellent. It is best not to call (fake) the asynchronous version to perform JSON serialization; Just call the synchronous version. A tricky example of false async is the BCL file stream. When a file stream is opened, it must be opened explicitly for asynchronous access, otherwise it uses false async to synchronize blocking of thread pool threads in file read and write operations.
After you select a leaf, you can begin using the method in your code that calls the API to make it an asynchronous method that waits for the asynchronous ready API to be called. If you call an API that supports CancellationToken, your method should take cancellationtoken and pass it to the API method.
Whenever a method is marked as asynchronous, it should change its return type: void to "Task" and non-void type T to "task<t>". You will find that all callers of the method need to become asynchronous so that they can wait for the task, and so on. Additionally, Async is appended to the name of your method to follow the task-based Asynchronous Pattern conventions (BIT.LY/1UBKGKR).
Allows the async/await mode to extend your call stack to "Trunk". In the Trunk, your code will be connected to the ASP. NET Framework (MVC, Web Forms, Web API). Integrate your asynchronous code with the framework by reading the related tutorials in the "status of asynchronous Support" section earlier in this article.
By the way, find out the state of any local thread. Because asynchronous requests can be rerouted, local thread state (such as ThreadStaticAttribute, threadlocal<t>, thread data slots, and Callcontext.getdata/setdata) will not be available. If possible, replace with httpcontext.items, or you can store the immutable data in Callcontext.logicalgetdata/logicalsetdata.
Here are some helpful tips I've found: you can (temporarily) copy your code to create a vertical partition. With this technique, you do not have to change the synchronization method to asynchronous; You can copy the entire synchronization method and then change the copy to asynchronous. You can then keep most applications using the synchronous method, creating only one small vertical slice of the async. This is a great idea if you want to explore async as proof of concept or just perform load tests on a part of your application to experience how to scale your system. You can have a fully asynchronous request (or page), while the rest of the application remains synchronized. Of course, you don't want to keep a copy of each method, and eventually all the I/O binding code will be asynchronous and you can delete the synchronous copy.
Summarize
I hope this article will help you understand the underlying concepts of asynchronous requests on ASP. Using async and await, you can make it easier than ever to write WEB applications, services, and APIs that maximize the use of their server resources. Async is awesome!
Introduction to Async/await on ASP.