个人空间和个人档案的优化(2008-09)

     最近个人空间的性能令人担忧,一天中不时会有大量的IO,或者w3wp.exe的内存占有不断增长直到应用程序池回收,这时如果应用程序池回收了就会导致大量的IO,针对这样的情况,必须先着手解决当前的性能问题。
1、分离用户头像到独立服务器中,并且取消通过程序读取和显示头像。
在以往的实现中,是通过DisplayPicture.aspx页面的 Response.WriteFile(fullname);
读取用户的头像然后显示出来的,前且还通过URL重写,提供友好的URL,例如http://profile.csdn.net/billok/picture/1.jpg
在这里面会有一定的性能和资源损耗,例如URL重写和通过asp.net读图片到内存然后又回收,这样的损耗其实没有太大的必要。所以在这次的性能优化中就直接显示为真实的地址,例如: http://avatar.profile.csdn.net/1/c/5/1_billok.jpg
其中
"/1/c/5/"是MD的散列的一部分,目的是为了文件在磁盘能够更加均匀的分布。
通过这样的修改就会取消ASP.NET的托管,提高影响的能力和减少不必要的性能损耗。同时,把avatar站点迁移到另外一台负荷更少一点的服务器上,就能大大减少IO读取量,当然上传头像和其它服务调用也要一并迁移了。

2、改良文件缓存组件。
系统中最重要的缓存方式是文件缓存,所以文件缓存的调用率是十分具大的,只要有一点的改善所带来的好处就是很大的了。
缓存组件是一个通用的缓存组件,可能通过配置来切换不同的缓存类型(如:文件、MemCached、ASP.NET等),不过这里主要用到的就是文件形式,因为有点复杂这里就先不拿出来讲了,不过在下篇文章里我会把 文件缓存的核心提取出一个简化版本以便大家纠一下错。这里只介绍本次优化中做了些什么事。
#资源的释放
在检查代码的过程中发现计算MD5后竟后没有清理资源,在计算后须要调用一下Clear()操作。
  1.             using (MD5 md5 = MD5CryptoServiceProvider.Create())
  2.             {
  3.                 byte[] buf = md5.ComputeHash(System.Text.Encoding.UTF8.GetBytes(k));
  4.                 x = BitConverter.ToString(buf).Replace("-"string.Empty);
  5.                 md5.Clear();
  6.             }
还发现原来XML序列化的时候竟然没有正确关闭,这量修正了这样的错误:
  1.         private static string ConvertToString(object objectToConvert)
  2.         {
  3.             string str = null;
  4.             using (MemoryStream stream = new MemoryStream())
  5.             {
  6.                 using (XmlTextWriter xmlWriter = new XmlTextWriter(stream, Encoding.UTF8))
  7.                 {
  8.                     XmlSerializer serializer = new XmlSerializer(objectToConvert.GetType());
  9.                     serializer.Serialize(xmlWriter, objectToConvert);
  10.                     byte[] buffer = new byte[stream.Length];
  11.                     stream.Position = 0;
  12.                     stream.Read(buffer, 0, (int)stream.Length);
  13.                     str = Encoding.UTF8.GetString(buffer);
  14.                     xmlWriter.Close();
  15.                 }
  16.                 stream.Close();
  17.             }
  18.             return str;
  19.         }
  20.         private static object ConvertToObject(string xml, Type objectType)
  21.         {
  22.             if (objectType == null)
  23.                 return null;
  24.             object obj = null;
  25.             try
  26.             {
  27.                 byte[] buffer = Encoding.UTF8.GetBytes(xml);
  28.                 using (MemoryStream stream = new MemoryStream(buffer, 0, buffer.Length))
  29.                 {
  30.                     stream.Position = 0;
  31.                     using (XmlTextReader reader = new XmlTextReader(stream))
  32.                     {
  33.                         XmlSerializer serializer = new XmlSerializer(objectType);
  34.                         obj = serializer.Deserialize(reader);
  35.                         reader.Close();
  36.                     }
  37.                     stream.Close();
  38.                 }
  39.             }
  40.             catch { }
  41.             return obj;
  42.         }
#锁的问题是这样的,原来组件中是没有锁的,这样会导致资源争用的问题,后来我就加上了ReaderWriterLock这样的读写锁,这样资源争用的问题就基本解决了,在这次的优化中查看了一下FX3.5出来了一个新的读写锁 ReaderWriterLockSlim,性能明显优于前者,这当然是优化的首选之一了,因为任何读写都会与锁发生关系的,所以调用量惊人的大,只要有一点性能提高,放大后都是巨大的。ReaderWriterLockSlim要修改的地方比较多,这里的就不列举了,可以查看前面的链接,或者 文件缓存简易版
#改善Get方法的逻辑,减少IO读取次数。
改动的地方不大,但是每次读缓存时减少了一次IO,这样对IO的改善就很大了。这里讲一下一个细节就是在判断缓存有没有过期的时候需要知道文件的最后更新 时间,我们可以通过File.GetLastWriteTime(fileName)方法获取到,但是如果fileName不存在的话就会返 回"1601/1/1 8:00:00"这样的时间值,所以可以通过if (dt == null || dt.Value.Year == 1601)来判断缓存是否已经存在了,这样就减少了一次的IO读取。

3、缓存默认头像。
头像服务中原来是通过 Response. WriteFile(Server.MapPath(fileName)) 方法从文件系统中读取头像然后显示出来。但是目前的情况是有头像的用户实际上是少数的,大部分的用户都是没有上传头像的,而且默认头像也就只有5个,所以 如果每次访问默认头像都要读取一个IO就实在有点浪费。所以改用Response.BinaryWrite方法来从byte[]中读取显示头像,并且对头 像的数据进行内存缓存,这样IO读取量就会大大减少了。下面是修改后的代码:
  1.         protected void Page_Load(object sender, EventArgs e)
  2.         {
  3.             string url = Request.Url.ToString();
  4.             string mime = "image/x-icon";
  5.             string fileName = "noimg_default.ico";
  6.             if (!url.EndsWith(".ico"))
  7.             {
  8.                 mime = "image/jpg";
  9.                 fileName = string.Format("noimg_default_{0}.jpg", url.Substring(url.LastIndexOf('/') + 1, 1));
  10.             }
  11.             //Response.Buffer = true;
  12.             //Response.Clear();
  13.             Response.BufferOutput = true;//将服务器创建的响应进行缓存
  14.             Response.ClearHeaders();
  15.             Response.AddHeader("Content-Type", mime);
  16.             string filePath = Server.MapPath(fileName);
  17.             if (filePath.IndexOf("noimg_default.ico") > -1 ||
  18.                 filePath.IndexOf("noimg_default_1.jpg") > -1 ||
  19.                 filePath.IndexOf("noimg_default_2.jpg") > -1 ||
  20.                 filePath.IndexOf("noimg_default_3.jpg") > -1 ||
  21.                 filePath.IndexOf("noimg_default_4.jpg") > -1)
  22.             {
  23.                 //Response.WriteFile(Server.MapPath(fileName));
  24.                 string cacheKey = filePath;
  25.                 byte[] bytes = Cache.Get(cacheKey) as byte[];
  26.                 if (bytes == null)
  27.                 {
  28.                     bytes = GetImageBinary(filePath);
  29.                     Cache.Insert(cacheKey, bytes, null, DateTime.MaxValue, TimeSpan.Zero,
  30.                                        CacheItemPriority.High, null);
  31.                 }
  32.                 Response.BinaryWrite(bytes);
  33.             }
  34.             //Response.Flush();
  35.             Response.End();
  36.         }
  37.         private byte[] GetImageBinary(string filePath)
  38.         {
  39.             byte[] bytes = null;
  40.             using (Stream stream = new FileStream(filePath, FileMode.Open,
  41.                                                                   FileAccess.Read, FileShare.ReadWrite))
  42.             {
  43.                 using (BinaryReader br = new BinaryReader(stream))
  44.                 {
  45.                     for (Int64 x = 0; x < (br.BaseStream.Length / 10000 + 1); x++)
  46.                     {
  47.                         bytes = br.ReadBytes(10000);
  48.                     }
  49.                     br.Close();
  50.                 }
  51.                 stream.Close();
  52.             }
  53.             return bytes;
  54.         }

注意:
# 这里不配置Response.Flush();否则可能会出现错误"0x80072746"

#提高Web园的数目到4个,以提高该应用程序池处理请求的性能。

4、排除异常捕捉组件捕获的异常。

5、垃圾收集器的类型有两种,即工作站和服务器。选择不同的垃圾引集器类型对性能有一定的影响,默认选项为工作站形式,通过处改配置文件可以指定不同的收集器类型,对于多核服务器来说,需要修改成以服务器垃圾收集器的形式进行工作,最大限度的提高垃圾收集的效率。

  1. <runtime>
  2.   <gcServer enabled="true"/>
  3. </runtime>
如何判断当前垃圾收集器是否为服务器类型,可以通过调用 GCSettings.IsServerGC来判断

后续:
#
HttpWebRequest潜在的问题。

     在用户展示页面中,同事lei用排除法发现一处使用HttpWebRquest调用内容服务的请求可能会存在问题。经过单独提取这段调用,然后使用多线程大量请求此 调用的页面后发现在内存会不断快速增长,但很难有大量的回落,结果最后达到应用程序池的内存阀值后导置回收。从现象来看,GC对于这样的调用所占用的资源 可能不能很好的进行垃圾回收,或者这样的调用存在非托管资源的占用,所以对此进行了优化,并且使用了超时设置,如下:
  1.             HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create("http://xxx.csdn.net/xxx.ashx");
  2.             request.Timeout = 1000;
  3.             string content = null;
  4.             try
  5.             {
  6.                 using (HttpWebResponse response = (HttpWebResponse)request.GetResponse())
  7.                 {
  8.                     if (response.StatusCode == HttpStatusCode.OK)
  9.                     {
  10.                         using (Stream stream = response.GetResponseStream())
  11.                         {
  12.                             using (StreamReader reader = new StreamReader(stream, Encoding.UTF8))
  13.                             {
  14.                                 content = reader.ReadToEnd();
  15.                                 reader.Close();
  16.                             }
  17.                             stream.Close();
  18.                         }
  19.                     }
  20.                     response.Close();
  21.                 }
  22.             }
  23.             catch (System.Net.WebException)
  24.             {
  25.             }
  26.             finally
  27.             {
  28.                 if (request != null)
  29.                 {
  30.                     request.Abort();
  31.                     request = null;
  32.                 }
  33.             }

这样的调用应该做到了足够的资源释放措施,但是事实上并没有很好的解决刚才提到的问题,资源还是会不断增长。
当然这样的增长可能也是正常的,不过过快地进行应用程序回收并不是一个很好方法,所以可以考虑把修改对内容服务的调用方式。

内容服务它提供一个公共展示内容(html或者模板)的获取点,但它的内容可以来源于不同的数据源, 调用方可以根据自已的需要获取自已页面所需要内容项,如果所请求的内容项在内容服务中不存在,则会自动根据配置文件从数据源(隐藏的)中自动获取并进行缓 存。内容服务的更新有两种方式,一种是拉的方式,就是刚才提到的根据配置文件向数据源接口自动提取;一种是推的方式,数据源方如果有数据更新会通过 MSMQ的形式传递给内容服务。通过这两种方式可以保正内容服务的内容的时效性,并且对于内容服务来说,它也是一个经过优化的统一的缓存服务,能分散IO 流量。具体内容服务的框架设计会在以后的文章中详细说明。

内容服务使用WCF(MSMQ+SVC)技术,但是在内容获取上为了兼容不同平台的调用需要使用了ashx页面返回JSON内容的方式,所以就出现了刚才使用HttpWebRequest来请求内容服务的问题,其实从调用性能角度来看完全可以把这样调用也通过WCF(TCP)来提供调用接口,这样既可以提高调用性能,也可能会解决内存增长过快的问题。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 26
    评论
好的,以下是一个使用 HTML 和 JavaScript 的个人空间网站示例。请注意,这只是一个基本的示例,您可以根据自己的需求进行更改和完善。 HTML 代码: ``` <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>个人空间</title> <link rel="stylesheet" href="style.css"> </head> <body> <header> <h1>欢迎来到我的个人空间</h1> </header> <nav> <ul> <li><a href="#" id="home-link">主页</a></li> <li><a href="#" id="blog-link">博客</a></li> <li><a href="#" id="gallery-link">相册</a></li> <li><a href="#" id="contact-link">联系我</a></li> </ul> </nav> <main> <section id="home"> <h2>个人简介</h2> <p>你好,我是 John,一名前端工程师。欢迎来到我的个人空间。这里是我分享我的生活、学习和工作的地方。</p> <p>在这里,你可以看到我的博客、相册,也可以通过联系我了解更多信息。</p> </section> <section id="blog"> <h2>博客</h2> <article> <h3>如何学习前端开发</h3> <p>学习前端开发需要具备一定的编程基础,包括 HTML、CSS 和 JavaScript。同时,还需要了解常用的前端框架和工具,如 React、Vue、Webpack 等。</p> <p>在学习过程中,可以通过阅读相关书籍、观看视频教程、参加线下培训等方式获取知识,并通过实践项目来巩固学习成果。</p> </article> <article> <h3>如何优化网站性能</h3> <p>优化网站性能可以从多个方面入手,包括减少 HTTP 请求、压缩资源、使用缓存等。同时,可以通过使用 CDN、使用 HTTP/2 等方式来进一步提升性能。</p> <p>在优化过程中,需要运用一些工具来辅助,如 Chrome DevTools、WebPageTest 等。</p> </article> </section> <section id="gallery"> <h2>相册</h2> <img src="image1.jpg" alt="图片1"> <img src="image2.jpg" alt="图片2"> <img src="image3.jpg" alt="图片3"> </section> <section id="contact"> <h2>联系我</h2> <form> <label for="name">姓名:</label> <input type="text" id="name" name="name"> <label for="email">邮箱:</label> <input type="email" id="email" name="email"> <label for="message">留言:</label> <textarea id="message" name="message"></textarea> <input type="submit" value="发送"> </form> </section> </main> <footer> <p>版权所有 © 2021 John</p> </footer> <script src="script.js"></script> </body> </html> ``` JavaScript 代码: ``` // 获取各个页面元素 const homeSection = document.getElementById('home'); const blogSection = document.getElementById('blog'); const gallerySection = document.getElementById('gallery'); const contactSection = document.getElementById('contact'); // 获取导航链接元素 const homeLink = document.getElementById('home-link'); const blogLink = document.getElementById('blog-link'); const galleryLink = document.getElementById('gallery-link'); const contactLink = document.getElementById('contact-link'); // 切换到主页 homeLink.addEventListener('click', function(event) { event.preventDefault(); homeSection.style.display = 'block'; blogSection.style.display = 'none'; gallerySection.style.display = 'none'; contactSection.style.display = 'none'; }); // 切换到博客页面 blogLink.addEventListener('click', function(event) { event.preventDefault(); homeSection.style.display = 'none'; blogSection.style.display = 'block'; gallerySection.style.display = 'none'; contactSection.style.display = 'none'; }); // 切换到相册页面 galleryLink.addEventListener('click', function(event) { event.preventDefault(); homeSection.style.display = 'none'; blogSection.style.display = 'none'; gallerySection.style.display = 'block'; contactSection.style.display = 'none'; }); // 切换到联系页面 contactLink.addEventListener('click', function(event) { event.preventDefault(); homeSection.style.display = 'none'; blogSection.style.display = 'none'; gallerySection.style.display = 'none'; contactSection.style.display = 'block'; }); ``` CSS 代码: ``` * { box-sizing: border-box; } body { margin: 0; font-family: Arial, Helvetica, sans-serif; } header { background-color: #333; color: #fff; padding: 20px; } nav { background-color: #f1f1f1; overflow: hidden; } nav ul { list-style-type: none; margin: 0; padding: 0; } nav li { float: left; } nav li a { display: block; color: #333; text-align: center; padding: 14px 16px; text-decoration: none; } nav li a:hover { background-color: #ddd; color: #333; } main { padding: 20px; } section { display: none; } section h2 { margin-top: 0; } section article { margin-bottom: 20px; } form { display: flex; flex-direction: column; max-width: 500px; } form label { margin-top: 10px; } form input, form textarea { margin-bottom: 10px; padding: 5px; } form input[type="submit"] { background-color: #333; color: #fff; border: none; padding: 10px; cursor: pointer; } footer { background-color: #333; color: #fff; padding: 20px; text-align: center; } ``` 您可以将以上代码保存为 HTML、CSS 和 JavaScript 文件,然后在浏览器中打开 HTML 文件,就可以看到一个简单的个人空间网站了。希望对您有所帮助!

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 26
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值