Detailed web application running problems: Pre-compilation improves website performance, tracking user habits, and solving Thread Synchronization

Source: Internet
Author: User
Tags website performance

In thisArticleI will share some of the issues and solutions encountered during the test run on the iopenworks.com website. These problems include: (1) if you use ASP. net MVC pre-compilation improves performance; (2) if the Zhidao website is running, the user's response speed, website exception information, and user operation habits; (3) solve the thread synchronization problem integrated with discuztoolkit.

 

1 ASP. net mvc 3 Pre-compilation support

To improve website performance, in addition to common compression, CDN, and cache, pre-compilation is also used. Whether it is ASP. NET webform or ASP. net mvc, these pages must be compiled before they are run on the website. Therefore, if you can compile the website before it runs, it will undoubtedly improve the website response speed. Therefore, we chose a razorgenerator for all ASP. in this way, you only need to copy the DLL file during deployment, instead of the cshtml file. The following describes how to use it for pre-compilation.

1.1 download and install razorgenerator

You can download razorgenerator.codeplex.com/at http://razorgenerator. this is an extension of vs 2010. After the download is complete, you can install it directly. Then you need to downloadSource codeThen compile and obtain the compiled razorgenerator. MVC. dll.ProgramSet.

 

1.2 change the View File Generation Method

Change buildaction of all views to none and customtool to razorgenerator. At this time, you can see an associated. generated. CS file, which is the pre-compiled source code file.

1.3 handle helper

For helpr files, the processing method is different. The Helper file is usually placed in the app_code folder. First, you need to add @ * Generator: mvchelper * @ In the first line of the Helper file to declare it, then change buildaction to none, and change customtool to razorgenerator.

Next, an additional step is required, which is very important. Otherwise, the compilation fails, that is, the buildaction of the. generated. CS file needs to be changed from content to compile.

 

1.4 register precompiledmvcengine

Next we will reference the razorgenerator. MVC. dll assembly in ASP. net mvc 3 Project, define a preapplicationstartcode, and register the preapplicationstartcode in the assemblyinfo. CS file. In this way, we have registered precompiledmvcengine.

(1) register with assemblyinfo. CS

 

[Assembly: preapplicationstartmethod (
Typeof (Uishell. iopenworks. preapplicationstartcode ), " Prestart " )]

 

(2) preapplicationstartcode Definition

 

Namespace Uishell. iopenworks
{
Public Class Preapplicationstartcode
{
Private Static Bool _ Isstarting;
Public Static Void Prestart ()
{
If (! _ Isstarting)
{
_ Isstarting = True ;
VaR Engine = New Precompiledmvcengine (
Typeof (Preapplicationstartcode). Assembly );
Viewengines. Engines. Add (engine );
Virtualpathfactorymanager. registervirtualpathfactory (engine );
}
}
}
}

 

 

1.5 deployer

In this case, you do not need to deploy the View File to deploy the website. You only need to copy the DLL file and website resources. Note that there are no. cshtml files and no app_code files under views, because they are all precompiled to the uishell. iopenworks. dll assembly. Next, you can test the website and enjoy the performance improvement brought about by pre-editing and translation.

 

2. Track website running status

The website encountered many problems during the internal test. However, at this time, the user has come in to test how you can promptly discover the user response speed, website exception information during user access, and how the user uses your website. Here, we use the log4net log component, which is used to record (1) pages accessed by the user; (2) what exceptions the user encounters while accessing the page; (3) the response speed of each page. Next, I will introduce how to record this information.

2.1 track the pages accessed by each user in global and record the user response speed

 

[Threadstatic]
Private Static Stopwatch _ stopwatch;
Protected Void Application_beginrequest ()
{
_ Stopwatch = stopwatch. startnew (); // Start Time
If (Discuzhelper. isloggedin ()) // Record current user
{
Try
{
VaR User = discuzhelper. loggeduser ();
If (User! = Null )
{
Threadcontext. properties [ " User " ] = User. Username;
Return ;
}
}
Catch (Exception ex)
{
_ Logger. Error ( " Failed to get the user name though the user is logged in. " , Ex );
}
}

Threadcontext. properties [ " User " ] = String . Empty;
If (Request! = Null ) // Record the IP address of the current user
{
Threadcontext. properties [ " IPaddress " ] = Request. servervariables [ " Remote_addr " ];
}
Else
{
Threadcontext. properties [ " IPaddress " ] = String . Empty;
}
}
Protected Void Application_endrequest ()
{
If (Request! = Null & _ Stopwatch! = Null & _ Logger! = Null ) // When the timer ends, the user response time and access page are displayed.
{
_ Stopwatch. Stop ();
_ Logger. debug ( String . Format ( " Accessed page 'response time: {0} MS, URL: {1 }'. " , _ Stopwatch. elapsedmilliseconds, request. url ));
}
}

 

2.2 record system exceptions in global

 

Void Application_error (Object sender, eventargs EA)
{
If (Server! = Null )
{
Exception E;
For (E = server. getlasterror (); e! = Null ; E = E. innerexception)
{
_ Logger. Error ( " Unhandled server exception thrown. " , E );
}
}
}

 

 

2.3 key Handling Methods

Below, I also recorded the user's operation exception information and response speed in key methods. For example, I must record the following information: (1) response speed during user registration, exceptions during registration, response speed during user logon, and exceptions during user logon; (2) under what conditions does the user attempt to download the free iopenworkssdk plug-in framework? (3) when trying to download the framework, it will go to the registration page. At this time, the user will continue to register and download the framework, or give up.

Recording these key methods helps improve the ease of use of the application system. Through logs, we have fixed many problems of integration with discuz and improved the user response speed.

 

2.4 Log Analysis

Next, let's take a look at log analysis. Here we have customized it in an open-source logviewer. By analyzing logs, you can know the system exceptions, system performance, user operation habits, and key methods. Of course, you can also open the log file to view it directly, but it is difficult. By the way, we will never record the user's password here, which is too unprofessional. In addition, all passwords are encrypted to avoid "csdn "!

(1) view exception information

(2) view key method information: User Access habits, response performance, etc.

 

3 solve discuztoolkit Thread Synchronization

WebsiteCommunityIt is integrated with discuz, and we use discuztoolkit for integration. This is an officially released class library, but there are still a bunch of problems. The two most serious problems are caused by thread synchronization. We can see that the discuz team did not take the ASP. NET multithreading model seriously, or even did not notice thread security. The following describes the two thread security problems encountered.

(1) When registering a user, the following exception occurs: The call_id submitted by the current session is not greater than the previous call_id

 
Failed to get the user name though the user is logged in. discuz. toolkit. discuzexception: Code: 103, message: the call_id submitted by the current session is not greater than the previous call_id at discuz. toolkit. util. getresponse [T] (string method_name, discuzparam [] parameters) in E: \ work \ design \ core \ milestore 1 \ osgi \ M10 \ uishell. iopenworks \ discuztoolkit \ util. CS: Line 97 at discuz. toolkit. discuzsession. getuserinfo (int64 [] UIDs, string [] fields) in E: \ work \ design \ core \ milestore 1 \ osgi \ M10 \ uishell. iopenworks \ discuztoolkit \ discuzsession. CS: Line 224 at discuz. toolkit. discuzsession. getuserinfo (int64 UID) in E: \ work \ design \ core \ milestore 1 \ osgi \ M10 \ uishell. iopenworks \ discuztoolkit \ discuzsession. CS: Line 255

This problem is caused by the sign method of discuz. toolkit. util. Here, it generates a call_id for each API request.

 

List. Add (discuzparam. Create ( " Call_id " , Datetime. Now. ticks ));

 

 

If you call the API in the current thread too frequently, datetime. Now. ticks will generate the same value, causing an exception. Therefore, the official proposal is to sleep. Therefore, we need to change it to the following:

 

List. Add (discuzparam. Create ( " Call_id " , Datetime. Now. ticks ));
// Avoid to generate same 'call _ id' and throws an exception on 'The call_id submitted by the current session is not greater than the previous call_id '.
Thread. Sleep ( 50 );

 

 

However, this still does not work. This exception is just a little more strange and leaves you with fewer opportunities. Do not forget ASP. NET applications are multi-threaded. When two threads access the same call_id at the same time, they may still get the same call_id. Therefore, after several times of this problem, I will use the following methods to fix it.

 

Lock (_ Syncroot)
{
List. Add (discuzparam. Create ( " Call_id " , Datetime. Now. ticks ));
// Avoid to generate same 'call _ id' and throws an exception on 'The call_id submitted by the current session is not greater than the previous call_id '.
Thread. Sleep ( 50 );
}

 

 

(2) An item with the same key has already been added.

[17:11:30, 818] [7] [Error] [accountcontroller] [49.72.46.135] []: system. argumentexception: An item with the same key has already been added. at system. throwhelper. throwargumentexception (exceptionresource Resource) at system. collections. generic. dictionary '2. insert (tkey key, tvalue value, Boolean add) at system. collections. generic. dictionary '2. add (tkey key, tvalue value) at discuz. toolkit. util. getserializer (type T) in E: \ work \ design \ core \ milestore 1 \ osgi \ M10 \ uishell. iopenworks \ discuztoolkit \ util. CS: line 157 at discuz. toolkit. util. getresponse [T] (string method_name, discuzparam [] parameters) in E: \ work \ design \ core \ milestore 1 \ osgi \ M10 \ uishell. iopenworks \ discuztoolkit \ util. CS: Line 88 at discuz. toolkit. discuzsession. getuserid (string username) in E: \ work \ design \ core \ milestore 1 \ osgi \ M10 \ uishell. iopenworks \ discuztoolkit \ discuzsession. CS: Line 243 at uishell. iopenworks. controllers. accountcontroller. register (discuznewuser newuser, string returnurl) in E: \ work \ design \ core \ milestore 1 \ osgi \ M10 \ uishell. iopenworks \ uishell. iopenworks \ controllers \ accountcontroller. CS: Line 53

 

Think about it. It would be so hot if the user fails to register successfully !! Therefore, I found that discuztoolkit did not lock the data when using static variables to save the data, so it took too much thread-safe to take the case. This exception also occurs in the util class,CodeSerializer_dict is a static global variable.

 

Serializer_dict.add (type_hash, New Xmlserializer (t ));

 

So I modified the following. In this way, the integration with discuz is completely solved.

 

Private Static Dictionary < Int , Xmlserializer> serializer_dict = New Dictionary < Int , Xmlserializer> ();
Private Static Readerwriterlock _ Lock = New Readerwriterlock ();
Public Static Xmlserializer getserializer (type T)
{
Int Type_hash = T. gethashcode ();
Const Int Timeout = 5000 ;
Try
{
_ Lock. acquirereaderlock (timeout );
If (! Serializer_dict.containskey (type_hash ))
{
_ Lock. upgradetowriterlock (timeout );
If (! Serializer_dict.containskey (type_hash ))
{
Serializer_dict.add (type_hash, New Xmlserializer (t ));
}
}
Return Serializer_dict [type_hash];
}
Catch (Applicationexception ex)
{
Throw New Exception ( " Accquire lock failed. " , Ex );
}
Finally
{
If (_ Lock. isreaderlockheld)
{
_ Lock. releasereaderlock ();
}
Else If (_ Lock. iswriterlockheld)
{
_ Lock. releasewriterlock ();
}
}
}

 

 

OK. The most important part of the website trial run is the description. Next we will introduce what iopenworks.com is. Iopenworks.com is a free plug-in repository designed to provide developers with completely standardized osgi for free.. net service-oriented plug-in framework and shared plug-in repository. In this way, you can use other plug-ins from the plug-in repository or share your own plug-ins for mutual benefit!

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.