在线时间:8:00-16:00
迪恩网络APP
随时随地掌握行业动态
扫描二维码
关注迪恩网络微信公众号
最近我们的系统面临着严峻性能瓶颈问题,这是由于访问量增加,客户端在同一时间请求增加,这迫使我们要从两个方面解决这一问题,增加硬件和提高系统的性能。 1.1.1 摘要 1 <!-- Adds OutputCache directive --> 2 <%@ OutputCache Duration="23" VaryByParam="None" %> 它支持五个属性,其中两个属性Duration和VaryByParam是必填的
表1输出缓存属性
<!-- Sets client OutputCache --> <%@ OutputCache Duration="23" VaryByParam="None" Location="Client" %> 通过在OutputCache中添加Location属性,我们实现了客户端缓存,通过设置客户端缓存我们能够减少的客户端请求,也许有人会问:“每个用户第一次页面请求都需要服务器来完成,这不能很好的减少服务的压力”。的确是这样,相对于服务器缓存,客户端缓存并没有减少代码的执行和数据库的操作,但是当我们把包含个性化数据的页面缓存在服务器中,客户端请求页面时,由于不同的用户个性化数据不同,这将会导致请求出现错误,所以我们可以使用片段缓存把公用的部分缓存起来或客户端缓存把用户信息缓存起来。
图2报表程序
1 protected void Page_Load(object sender, EventArgs e) 2 { 3 if (!Page.IsPostBack) 4 { 5 // Gets product id from table Production.Product. 6 // Then binding data to drop down list control. 7 InitControl(GetProductId()); 8 } 9 } 10 /// <summary> 11 /// Handles the Click event of the btnSubmit control. 12 /// Redirects to relative product information page. 13 /// </summary> 14 protected void btnSubmit_Click(object sender, EventArgs e) 15 { 16 Response.Redirect(string.Format("Product.aspx?productname={0}", ddlProductName.SelectedValue)); 17 } 当用户点击Submit按钮后,跳转到Product页面并且在Url中传递查询参数——产品名称(ProducName)。 1 protected void Page_Load(object sender, EventArgs e) 2 { 3 // Get product name. 4 string productName = Request.QueryString["productname"]; 5 6 // Binding data to data grid view control. 7 InitControl(this.GetData(productName)); 8 } 9 10 /// <summary> 11 /// Inits the control. 12 /// </summary> 13 /// <param name="ds">The dataset.</param> 14 private void InitControl(DataSet ds) 15 { 16 dgvProduct.DataSource = ds; 17 dgvProduct.DataBind(); 18 } 19 20 /// <summary> 21 /// Gets the data. 22 /// </summary> 23 /// <param name="productName">Name of the product.</param> 24 /// <returns>Returns dataset</returns> 25 private DataSet GetData(string productName) 26 { 27 // The query sql base on product name. 28 string sql = 29 string.Format( 30 "SELECT Name, ProductNumber, SafetyStockLevel, ReorderPoint, StandardCost, DaysToManufacture " 31 + "FROM Production.Product WHERE ProductNumber='{0}'", 32 productName); 33 34 // Get data from table Production.Product. 35 using (var con = new SqlConnection(ConfigurationManager.ConnectionStrings["SQLCONN"].ToString())) 36 using (var com = new SqlCommand(sql, con)) 37 { 38 com.Connection.Open(); 39 ////gdvData.DataSource = com.ExecuteReader(); 40 ////gdvData.DataBind(); 41 var ada = new SqlDataAdapter(com); 42 var ds = new DataSet(); 43 ada.Fill(ds); 44 return ds; 45 } 46 } 前面示例,我们通过Request的属性QueryString获取ProductName的值,然后根据ProductName查询数据库,最后把获取数据绑定到Datagridview控件中(注:前面实例没有考虑SQL Injection问题)。
图3查询结果 <!-- Adds OutputCache directive --> <%@ OutputCache Duration="30" VaryByParam="None" %> 前面提到当输出缓存的属性VaryByParam=”None”时,ASP.NET程序在缓存有效期内只缓存一个页面副本;现在我们在缓存有效期内(30s)再发送请求。
图5查询结果
1 <!-- Sets VaryByParam property--> 2 <%@ OutputCache Duration="30" VaryByParam="productname" %> 自定义缓存控件 1 /// <summary> 2 /// Gets vary cache based on custom string value. 3 /// </summary> 4 /// <param name="context">Http context.</param> 5 /// <param name="custom">custom string</param> 6 /// <returns></returns> 7 public override string GetVaryByCustomString(HttpContext context, string custom) 8 { 9 if (string.Equals(custom, "UserHostName", StringComparison.OrdinalIgnoreCase)) 10 { 11 // Indicates that the cache should be vary on user host name. 12 return Context.Request.UserHostName; 13 } 14 return base.GetVaryByCustomString(context, custom); 15 } 前面我们重写了GetVaryByCustomString()方法,使得UserHostName值不同时,获取相应的缓存值。 <!-- set vary cache based on custom string value --> <%@ OutputCache Duration="30" VaryByParam="None" VaryByCustom="UserHostName" %> 我们通过自定义现在GetVaryByCustomString()方法,实现了Web程序根据UserHostName实施不同的缓存方式,其实,我们还可以实现更多种类缓存方案,例如:基于用户角色、时间和Url等等。 1 <caching> 2 <!-- Sets out put cache profile--> 3 <outputCacheSettings> 4 <outputCacheProfiles> 5 <add name="ProductCacheProfile" duration="30"/> 6 </outputCacheProfiles> 7 </outputCacheSettings> 8 </caching>现在,我们在页面中添加CacheProfile属性,并且设置为ProductCacheProfile,如下所示: <!-- set CacheProfile property --> <%@ OutputCache CacheProfile="ProductCacheProfile" VaryByParam="None" %> 数据缓存 <!-- The product number datasource START --> <asp:SqlDataSource ID="sourceProductName" runat="server" ProviderName="System.Data.SqlClient" EnableCaching="True" CacheDuration="3600" ConnectionString="<%$ ConnectionStrings:SQLCONN %>" SelectCommand="SELECT ProductNumber FROM Production.Product"></asp:SqlDataSource> <!-- The product number datasource END --> <!-- The product datasource START --> <asp:SqlDataSource ID="sourceProduct" runat="server" ProviderName="System.Data.SqlClient" EnableCaching="True" CacheDuration="3600" ConnectionString="<%$ ConnectionStrings:SQLCONN %>" SelectCommand="SELECT Name, ProductNumber, SafetyStockLevel, ReorderPoint, StandardCost, DaysToManufacture FROM Production.Product WHERE ProductNumber=@ProductNumber"> <SelectParameters> <asp:ControlParameter ControlID="ddlProductNumber" Name="ProductNumber" PropertyName="SelectedValue" /> </SelectParameters> </asp:SqlDataSource> <!-- The product number datasource END --> <!-- Binding the product number to gridview control --> <!-- NOTE: Due to search and result in the same page, so need to set AutoPostBack is True--> <asp:DropDownList ID="ddlProductNumber" AutoPostBack="True" DataSourceID="sourceProductName" DataTextField="ProductNumber" runat="server"> </asp:DropDownList> <!-- Binding the product datasource to gridview control --> <asp:GridView ID="gvProduct" runat="server" DataSourceID="sourceProduct" CssClass="Product"> </asp:GridView> 现在我们对报表程序进行查询,如果ProudctName之前没有被缓存起来就会创建相应的缓存,而已经缓存起来的将被重用,查询结果如下: // Creates cache object Key1. Cache["Key1"] = "Cache Item 1"; // Makes Cache["Key2"] dependent on Cache["Key1"]. string[] dependencyKey = new string[1]; dependencyKey[0] = "Key1"; // Creates a CacheDependency object. CacheDependency dependency = new CacheDependency(null, dependencyKey); // Establishs dependency between cache Key1 and Key2. Cache.Insert("Key2", "Cache Item 2", dependency); 现在,当Key1缓存项更新或从缓存中删除,Key2缓存项就会自动从缓存删除。
图7 请求流程
/// <summary> /// Caches the data into text file. /// </summary> /// <param name="context">The http context</param> private void CacheData(HttpContext context) { // Weibo API. string uri = context.Request.QueryString["api"] + "?" + "source=" + context.Request.QueryString["source"] + "&" + "count=" + context.Request.QueryString["count"]; HttpWebResponse response = this.GetWeibos(uri); if (null == response) { throw new ArgumentNullException("Response is null"); } string jsonData; // Writes the reponse data into text file. using (var reader = new StreamReader(response.GetResponseStream(), Encoding.GetEncoding(response.CharacterSet))) { jsonData = reader.ReadToEnd(); } string dataPath = context.Server.MapPath("weibo.json"); using (var writer = new StreamWriter(dataPath, false, Encoding.GetEncoding(response.CharacterSet))) { writer.Write(jsonData); } // Establishs dependency between cache weibo and text file. // Sets cache expires after 2 minuntes. HttpRuntime.Cache.Insert("weibo", jsonData, Dep, Cache.NoAbsoluteExpiration, TimeSpan.FromMinutes(2)); } 现在我们把数据保存到文本文件中并且建立了缓存weibo与数据文件的依赖关系,接下来我们要把JSON格式数据返回给客户端。 /// <summary> /// Responses the weibo data. /// </summary> /// <param name="context">The http contex.</param> private void ResponseWeibo(HttpContext context) { // Gets the weibo cache data. byte[] buf = Encoding.UTF8.GetBytes(HttpRuntime.Cache["weibo"].ToString()); // Writes the data into output stream. context.Response.OutputStream.Write(buf, 0, buf.Length); context.Response.OutputStream.Flush(); ////context.Response.Close(); } 上面我们把JSON格式字符串转换为Byte数值,然后写入到OutputStream中,最后把数据返回给客户端。 // The function to get weibo data. loadWeibo: function() { $.ajax({ // Weibo API. url: "WeiboHandler.ashx", type: "GET", // NOTE: We get the data from same domain, // dataType is json. dataType: "json", data: { source: JQWeibo.appKey, count: JQWeibo.numWeibo }, // When the requet completed, then invokes success function. success: function(data, textStatus, xhr) { // Sets html structure. var html = '<div class="weibo">' + '<a href="http://weibo.com/DOMAIN" target="_blank">USER</a>' + ':WEIBO_TEXT<div class="time">AGO</div>'; // Appends weibos into html page. for (var i = 0; i < data.length; i++) { $(JQWeibo.appendTo).append( html.replace('WEIBO_TEXT', JQWeibo.ify.clean(data[i].text)) // Uses regex and declare DOMAIN as global, if found replace all. .replace(/DOMAIN/g, data[i].user.domain) .replace(/USER/g, data[i].user.screen_name) .replace('AGO', JQWeibo.timeAgo(data[i].created_at)) ); } } }) } 图8请求结果 |
请发表评论