1. Trang chủ
  2. » Công Nghệ Thông Tin

ASP.NET 4.0 in Practice phần 10 potx

55 443 0

Đang tải... (xem toàn văn)

Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống

THÔNG TIN TÀI LIỆU

Thông tin cơ bản

Định dạng
Số trang 55
Dung lượng 15,38 MB

Nội dung

425TECHNIQUE 96 Building an HTTPHandler to minify JavaScript </httpHandlers> </system.web> </configuration> IsReusable is generally set to true to reuse the same instance of the class across differ- ent requests. To achieve scalability, items are saved in the cache B C E . The content type and the content are sent to the browser, after the replacements are performed D . By associating this content with .css files, we’re modifying the output of these files. You can apply the same techniques to similar content, when you need to perform the same replacements. DISCUSSION Typical CSS files aren’t big in size, so caching them in memory is acceptable and—of course—fast. Just like the previous technique, this one uses string manipulation, so the same caveats apply. The results of this simple handler are pretty amazing: you can considerably reduce the size of a CSS file. Given the nature of style sheets, which are composed of lots of line feeds, spaces, and tabs, minification can greatly improve the speed of your application. Building an HTTPHandler to minify JavaScript This example is similar to the one we presented in technique 95. The real difference is in the algorithm associated with the minifying. In this scenario, to minify JavaScript exter- nal files, we’ll also remove comments from code, both inline ( /*…*/ ) and per line ( //… ). PROBLEM JavaScript files are full of tabs, comments, and spaces. These characters are useful while you’re in the throes of developing, but they’re less useful to browsers (the same considerations for the example in technique 95 apply here, too). We want to build a system that minifies JavaScript content on the fly, without modifying files before deployment. As always, we’re also keeping great performance in mind. SOLUTION The solution is similar to the one we used in technique 95—the only real difference is in the content type response header and the regular expression that does the magic to strip out comments. You have to map the resulting HttpHandler to the .js extension in IIS (if you aren’t using IIS 7.x integrated mode) and register it in web.config. As we’ve said before, you can reuse this code in a bridge page if you need to use it in a hosting scenario where you can’t map custom extensions. The core piece of this class is the regular expression used to catch comments in the following code: C#: fileContent = Regex.Replace(fileContent, @"(?mi)(/\*[\d\D]*?\*/)|(//.*$)", string.Empty); VB: fileContent = Regex.Replace(fileContent, "(?mi)(/\*[\d\D]*?\*/)|(//.*$)", string.Empty) TECHNIQUE 96 426 CHAPTER 16 Performance and optimizations The rest of the code is similar to listing 16.3; the only difference is that we first need to remove comments and then remove line feeds and tabs. This solution isn’t the only option available to you. Let’s look at another possibility. Another option: Microsoft Ajax Minifier To carr y out our solution, you could also use a new tool called the Microsoft Ajax Minifier, available at http://ajaxmin.codeplex.com/. The Ajax Minifier consists of a command-line tool, an MSBuild task to minify JavaScript files at compile time, and a library to do it at runtime, just like we’re doing in this example. You might be able to further compress JavaScript that’s already been compressed by using the Minifier’s hypercrunching mode, which renames variables to reduce file size. For real-world scenarios, the Minifier is the best choice. We’ve provided the last two examples so that you can understand different approaches to the same problem if you need to provide your own rules in terms of personalizations. DISCUSSION Even though the outcome isn’t quite perfect, the solutions offered here will work for most situations. Keep in mind that complex comments sequences might modify this script output, so, as always, you’ll need to test this technique with your files before going into production. Performance increases are guaranteed by caching and com- pressing content, and size reductions of 70% and more are possible. Similarly, multithreading techniques can impact your server performance because you can span the work across multiple threads. The next section of this chapter will expose you to that option, starting with a technique not entirely new to ASP.NET 4.0, and continuing with ParallelFX, a new technology that uses the parallelism that was introduced in .NET Framework 4.0. 16.2 Reducing computing time with multithreading With so many multicore CPUs on the market, multithreading and parallel execution are becoming more popular topics among developers. Both multithreading and paral- lel execution aim to reduce computing time, which improves performance. Let’s define our terms: ■ Multithreading is the ability to execute multiple tasks at the same time, using different threads. Figure 16.3 shows an example of a multithread architecture. ■ Parallel execution is the ability to span a single task across multiple CPUs to use the power of all of them to execute a computing task in the fastest possible way. StartWork() Engine1 Enginen Completed Figure 16.3 In the multithreading architec- ture shown here, the StartWork method will instantiate differ- ent engines at the same time to execute multiple requests at one time. 427TECHNIQUE 97 Increasing performance with multithreading When a piece of code is executed, the thread is blocked and waits for the response. If you have a single thread responding to your code execution needs, the problem is simple: you’ll have a waiting list for code to be executed. For most applications, this method isn’t going to work. Let’s imagine that while you’re in a production program like the ones in Microsoft Office, you have to wait for every single operation you do to complete before you can move on to another one. Under these circumstances, it would be impossible to have a background spell checker or to start printing while you’re still editing a document. This example highlights the importance of multithreading. ASP.NET supports mul- tiple threads, as we discussed in chapter 1. Using multithreads, one request doesn’t stop the others, and multiple requests can be served at the same time. More important is the ability to create new threads and assign a specific code to them, so that part of the work can be executed in a different thread. To be clear, we’re talking about gener- ating multiple threads from a single request to increase response time. When you need to make calls to external resources, like databases or web services, you’ll find this approach to be quite useful. Vigorous debate is going on about whether generating multiple threads in a web application is a best practice. Remember, the working threads are shared by all the requests. In this kind of situation, if you can afford better application componentiza- tion, you can achieve the same results by simply moving thread generation to a differ- ent layer and using the application as a controller and method of display only. Even though the jury is still out, the technique shown in the next example should be useful to you in scenarios where you don’t need this kind of componentization or it’s just not possible. Increasing performance with multithreading Applying multithreading techniques is a good idea when you have to deal with multi- ple requests and you don’t want to slow the process while you’re waiting for results. A typical example is a system made of many requests to different web services. If you need to implement something similar, you’ll probably end up using a simple for iter- ation and calling each web service in this block. That technique might work with a few requests, but to speed up execution, you need to use multithreading. Process, thread, and execution When a program is executed, the operating system creates a particular object called a process and gives isolated memory space to it. A process contains threads, which are used to execute the code. A process doesn’t have the ability to execute anything. A process contains at least one thread (the primary one). When the primary thread is terminated, the process itself is terminated and the memory is unloaded. From a performance point of view, creating a thread is easier than creating a process because you aren’t required to allocate memory. TECHNIQUE 97 428 CHAPTER 16 Performance and optimizations PROBLEM Let’s suppose you have to gather some data using different web services and then dis- play the results on the page, just like a flight comparison engine does. You want to avoid latency and provide a better experience for users while they’re waiting for differ- ent external services to respond to their requests. Usually, if you opt to execute a sin- gle task at a time, the total time will be the sum of the entire operation. You can dramatically improve speed by executing the tasks in parallel, and you’ll gain in total response time. SOLUTION In heavy-load scenarios where you need to execute different tasks at the same time, you might be able to use a worker thread to reduce the total execution time. A worker thread is a secondary thread created by the primary one to accomplish a specific task. The . NET Framework has a specific namespace, called System.Threading , to sup- port threads, and a specific class, named Thread , to represent the concept of a thread in managed code. Thread has a special constructor that receives the code to be exe- cuted and a Start method to begin execution. When the thread is created, there’s a fork in the execution flow: the primary thread continues its normal execution, and the secondary starts its work. To provide a true multithreading experience, we’re going to execute every request on a separate thread. Using this approach, the total time for the complete request to be executed isn’t the amount of time it takes to execute all the different requests, but the longest amount of time that it takes to execute any one of them (plus the over- head of creating, destroying, and joining threads). Even if it’s possible, it’s not a good idea to directly instantiate threads; for this spe- cific scenario, a specialized class called ThreadPool exists. This class represents a pool of threads managed by the CLR itself, and can be used to coordinate them. When you’re using a technique like this one, you need thread synchronization: each call to the QueueUserWorkItem method immediately returns, so you need a way to notify your class that each thread has completed its work and that the results are ready to show. To accomplish this task, you need to use a WaitHandle class manually, as shown in figure 16.4. StartWork() Engine 1 Engine n T1.Join() Tn.Join() Completed Figure 16.4 Thread generation and synchronization need to be handled manually to work correctly with threads. When completed, a single thread will notify the ThreadPool. 429TECHNIQUE 97 Increasing performance with multithreading The problem at this point is that while accessing the List<T> to add our results, there’s no guarantee that there won’t be collisions resulting from different threads trying to modify the same collection at the same time. List<T> isn’t thread-safe, so we need to synchronize the modifications using the lock keyword (in C#) or the Monitor class. All the code is shown in the following listing. C#: public class PriceEngine { public PriceEngine(string flightNumber) { FlightNumber = flightNumber; FlightResults = new List<FlightPriceResult>(); } public void GetFlightPrices() { StartTime = DateTime.Now; try { List<WaitHandle> handles = new List<WaitHandle>(); foreach (IFlightPriceProvider provider in GetProviders()) { ManualResetEvent handle = new ManualResetEvent(false); handles.Add(handle); Tuple<IFlightPriceProvider, ManualResetEvent> currentState = new Tuple<IFlightPriceProvider, ManualResetEvent> (provider, handle); ThreadPool.QueueUserWorkItem( delegate(object state) { // Engine implementation in listing 16.5 }, currentState ); } WaitHandle.WaitAll(handles.ToArray()); } finally { EndTime = DateTime.Now; Completed = true; } } } VB: Public Class PriceEngine Public Sub New(ByVal number As String) FlightNumber = number FlightResults = New List(Of FlightPriceResult)() Listing 16.4 The engine for initializing the multithreading requests WaitHandle tasks Retrieve providers list Create and register handle Set completed flags 430 CHAPTER 16 Performance and optimizations End Sub Public Sub GetFlightPrices() StartTime = DateTime.Now Try Dim handleList As New List(Of WaitHandle)() For Each provider As IFlightPriceProvider In GetProviders() Dim handle As New ManualResetEvent(False) handleList.Add(handle) Dim currentState As New Tuple(Of IFlightPriceProvider, ManualResetEvent) (provider, handle) ThreadPool.QueueUserWorkItem(AddressOf ExecuteProvider, currentState) Next WaitHandle.WaitAll(handleList.ToArray()) Finally EndTime = DateTime.Now Completed = True End Try End Sub End Class In this listing, you can see the general structure of the engine, but its inner workings are contained in the code in the following listing. Each result is retrieved from the dif- ferent providers and added to the results collection using a thread-safe approach. C#: delegate(object state) { Tuple<IFlightPriceProvider, ManualResetEvent> invokeState = (Tuple<IFlightPriceProvider, ManualResetEvent>)state; FlightPriceResult result = null; IFlightPriceProvider currentProvider = invokeState.Item1; result = currentProvider.GetFlightPrice(FlightNumber); bool lockTaken = false; Monitor.Enter(Sync, ref lockTaken); try { FlightResults.Add(result); } finally { if (lockTaken) Monitor.Exit(Sync); } ManualResetEvent resetHandle = invokeState.Item2; resetHandle.Set(); } VB: Listing 16.5 The engine implementation WaitHandle tasks Retrieve providers list Create and register handle Set completed flags Using lock to be thread-safe Rejoining the thread 431TECHNIQUE 97 Increasing performance with multithreading Private Sub ExecuteProvider(ByVal state As Object) Dim invokeState As Tuple(Of IFlightPriceProvider, ManualResetEvent) = DirectCast(state, Tuple(Of IFlightPriceProvider, ManualResetEvent)) Dim result As FlightPriceResult = Nothing Dim currentProvider As IFlightPriceProvider = invokeState.Item1 result = currentProvider.GetFlightPrice(FlightNumber) Dim lockTaken As Boolean = False Monitor.Enter(Sync, lockTaken) Try FlightResults.Add(result) Finally IF lockTaken Then Monitor.Exit(Sync) End If End Try Dim resetHandle As ManualResetEvent = invokeState.Item2 resetHandle.Set() End Sub End Class The IFlightPriceProvider interface guarantees that every provider has the Get- FlightPrice method to load results. It’s part of our design strategy, often referred to as the Provider Model. The providers attached to this example are just for testing pur- poses, and in order to simulate latency, they have a call to Thread.Sleep to freeze exe- cution for a couple of seconds. A simple implementation is shown in the following listing. C#: public class SecondProvider: IFlightPriceProvider { public FlightPriceResult GetFlightPrice(string FlightNumber) { Thread.Sleep(3000); return new FlightPriceResult() { FlightNumber = FlightNumber, Price = 260 }; } } VB: Public Class SecondProvider Implements IFlightPriceProvider Public Function GetFlightPrice( ByVal FlightNumber As String) As FlightPriceResult Thread.Sleep(3000) Dim result as New FlightPriceResult() result.FlightNumber = FlightNumber result.Price = 260 Return result End Function End Class Listing 16.6 A simple provider implementation Using lock to be thread-safe Rejoining the thread Simulate latency Return fixed value Simulate latency Return fixed value 432 CHAPTER 16 Performance and optimizations In real-life scenarios, you’ll insert your code in this method and populate a new instance of the FlightPriceResult class to return the corresponding flight price. To effectively start the work, we need to create a page with a Textbox in which to enter the flight number and a Button to execute the code, as shown in the following listing. C#: protected void StartWork_Click(object sender, EventArgs e) { PriceEngine engine = new PriceEngine(FlightNumber.Text); Session["engine"] = engine; ThreadPool.QueueUserWorkItem( delegate(object state) { PriceEngine priceEngine = (PriceEngine)state; priceEngine.GetFlightPrices(); }, engine); Response.Redirect("results.aspx"); } VB: Protected Sub StartWork_Click(ByVal sender As Object, ByVal e As EventArgs) Dim engine As New PriceEngine(FlightNumber.Text) Session("engine") = engine ThreadPool.QueueUserWorkItem(AddressOf Execute, engine) Response.Redirect("results.aspx") End Sub Protected Sub Execute(ByVal state As Object) Dim priceEngine As PriceEngine = DirectCast(state, PriceEngine) priceEngine.GetFlightPrices() End Sub The code in this listing is simple: the engine will start, saving its instance in Session so that we can access it later. The results.aspx page includes code that checks in Session for the instance of the PriceEngine class that originates the threads, checking at intervals for the execution to be completed. By using a simple reload of the page, as shown in the following listing, we can check the state of the job, and, if it’s done, display only the results to the user. Markup: <asp:PlaceHolder ID="WaitingPanel" runat="server" Visible="false"> Listing 16.7 The page containing the code to start the work Listing 16.8 The results.aspx page contains both the waiting panel and the results Start new instance Execute next statement Redirect on waiting page Start new instance Execute next statement Redirect on waiting page Show waiting panel 433TECHNIQUE 97 Increasing performance with multithreading Please wait </asp:PlaceHolder> <asp:PlaceHolder ID="ResultsPanel" runat="server" Visible="false"> <h1>Results</h1> <asp:literal ID="Feedback" runat="server" /> <asp:GridView ID="ResultList" runat="server" /> </asp:PlaceHolder> C#: protected void Page_Load(object sender, EventArgs e) { PriceEngine engine = Session["engine"] as PriceEngine; if (engine == null) Response.Redirect("./"); if (engine.Completed) { ResultsPanel.Visible = true; ResultList.DataSource = engine.FlightResults; ResultList.DataBind(); Feedback.Text = string.Format("Elapsed time: {0}", engine.EndTime.Subtract(engine.StartTime)); } else { WaitingPanel.Visible = true; Header.Controls.Add(new HtmlMeta() { HttpEquiv = "refresh", Content = "2" }); } } VB: Protected Sub Page_Load(ByVal sender As Object, ByVal e As EventArgs) Dim engine As PriceEngine = TryCast(Session("engine"), PriceEngine) If engine Is Nothing Then Response.Redirect("./") End If If engine.Completed Then ResultsPanel.Visible = True ResultList.DataSource = engine.FlightResults ResultList.DataBind() Feedback.Text = String.Format("Elapsed time: {0}", engine.EndTime.Subtract(engine.StartTime)) Else WaitingPanel.Visible = True ' programmatically add a refresh meta tag Show results panel Get engine from session Complete work Refresh page and check again Get engine from session Complete work 434 CHAPTER 16 Performance and optimizations Dim meta as New HtmlMeta() meta.HttpEquiv = "refresh" meta.Content = "2" Header.Controls.Add(meta) End If End Sub he sequence of the entire workflow is shown in figure 16.5. It’s similar to what flight comparison engines do to speed up their execution while you’re waiting for the results to display in the web page. Dealing with multithreading isn’t simple because, as we’ve discussed, you have to take care of details and be sure to use thread-safe collections. But if you use a solution like this one, the performance boost you can achieve by letting different threads simultaneously execute different tasks will be obvious to both you and your customers. DISCUSSION Multithreading can boost specific applications, like the one in this scenario, by lever- aging the ability to span the work on different threads. It’s not intended to be used by every application because you do have to consider some drawbacks. ASP.NET uses a thread pool, which we talked about in chapter 1. The pool size includes both ASP.NET-generated threads and the ones generated in your code. Remem- ber: if you use this technique in high-traffic applications, the entire pool size can be con- sumed, and you’ll need to increase it accordingly if you notice that its size is low. TIP You can monitor current threads in the Control Panel, under Perfor- mance Monitor. Even with that limitation, using threads to execute multiple tasks separately is a good design decision that increases performance dramatically and leverages ASP.NET’s mul- tithread nature. Refresh page and check again Thread 2 Thread 1 GetFlightPrice() GetFlightPrice() GetFlightPrices() Results.aspx Session Completed = true FlightResults.Add() Figure 16.5 The sequence of our multithreading system: first, the providers are initialized and their work is executed. When all the providers are done, the engine is notified so the results can be displayed. [...]... applications In fact, you can host WCF services natively using another binding protocol, just like direct TCP support lets you do Starting with this version, IIS can use ASP.NET in Windows Server Core, a specific version of Windows Server, using versions 2.0, 3.0 (WCF), 3.5 SP 1, and, of course, 4.0 Now you can finally host applications with different ASP.NET versions (like 2.0 and 4.0) in the same... multithreading access to collections, handle exceptions in an easier way, and increase performance, thanks to the minimal thread switching that it provides DISCUSSION ParallelFX is a new feature introduced in NET Framework 4.0 that you probably won’t use directly in an ASP.NET page, as we did in our example More likely, you’ll wrap it in a middle tier or something similar ParallelFX can certainly help... data Finally, we have to close the connection and return the objects to the UI Sounds easy, doesn’t it? Connecting to the database is just a matter of instantiating the SqlConnection class located in the System.Data.SqlClient namespace, passing in the connection string and invoking the Open method NOTE The connection string contains information about the database location plus other additional information... In fact, invoking a stored procedure is just a matter of using its name instead of the full SQL statement and setting the CommandType property of the SqlCommand class The following listing shows the necessary code Listing B.4 Invoking a stored procedure C#: string sql = "GetOrders"; using (var comm = new SqlCommand(sql, conn)) { comm.CommandType = CommandType.StoredProcedure; } VB: Dim sql As String... a way to eliminate lots of lines of code Now you know why Entity Framework greatly simplifies development B.2 Reading and writing XML In this section, you’ll learn how to create and read an XML file using LINQ to XML We decided not to use the System.Xml APIs because they’re obsolete and are maintained in NET Framework 4.0 for compatibility reasons only TECHNIQUE 105 Writing XML When LINQ to XML was... with images in the current directory 444 APPENDIX A ASP.NET and IIS 7.x Our module is a simple class that implements the IHttpModule interface, so in the Init method we registered the EndRequest event of the HttpApplication class The code is shown in the following listing Listing A.2 Our custom HttpModule code C#: using System; using System.Web; using System.IO; public class DirectoryListingModuleManaged... ParallelFX can certainly help your routines perform faster If you’ve had trouble in the past using ThreadPool, it’s a giant step forward in accessing the operating system’s multicore inner features 16.3 Optimize your web.config ASP.NET 4.0 introduces a new web.config version, which contains less markup than in previous versions If you look at a web.config file from an ASP.NET 3.5 application, you’ll notice... lifetime DISCUSSION At this point, it must be clear that you can modify every single aspect of the server using the IIS 7.x integrated pipeline, and we’re not just talking about the ones related to ASP.NET itself You still need to create a classic HttpModule, but it will be used by the entire pipeline Remember that when you’re using integrated pipeline mode, you need to remove ASP.NET HttpModules and register... (configuration\system.webServer\modules) ASP.NET and IIS 7.x are so tightly integrated that when you run IIS modules, you’re leveraging the ASP.NET HttpModule infrastructure and controlling the request and response for all resources, not only ASP.NET ones TECHNIQUE 101 Configuring application warm-up in IIS 7.5 IIS 7.5 includes a unique feature called application warm-up ASP.NET application compilation and... new integrated pipeline in detail Native handler IHttpHandler static file WAS *.aspx Integrated pipeline Native end Managed auth events auth log begin session http.sys Figure A.1 IIS 7.x integrated pipeline mode enables a useful integration between ASP.NET and the web server You can write managed modules to intercept events and provide the same results as you can with unmanaged ones TECHNIQUE 100 . starting with a technique not entirely new to ASP. NET 4. 0, and continuing with ParallelFX, a new technology that uses the parallelism that was introduced in .NET Framework 4. 0. 16.2 Reducing. experience! 44 1 appendix A: ASP. NET and IIS 7.x Starting with IIS 7 .0, which is included in Windows Server 200 8 and Windows Vista, .NET Framework became part of the web server, using a new architecture. use ASP. NET in Windows Server Core, a spe- cific version of Windows Server, using versions 2 .0, 3 .0 (WCF), 3.5 SP 1, and, of course, 4. 0. Now you can finally host applications with different ASP. NET

Ngày đăng: 12/08/2014, 15:23

TỪ KHÓA LIÊN QUAN