当前位置: 首页 > 编程日记 > 正文

Web性能优化实践——应用层性能优化

随着公司项目的进一步推广,用户数量的增加,已经面临着单台服务器不能负载的问题。

这次的优化由于时间关系主要分两步走,首先优化应用层代码以提高单台服务器的负载和吞吐率。之后再进行分表,引入队列、MemCached等分布式应用。

项目背景:这是一个在线竞赛的项目(http://race.gwy.591up.com),在竞赛的时间段内数据库的写入压力很大。

当前问题:1、服务器带宽压力。2、数据库压力。

下图是Web服务器CPU使用率报表。

总体上应用层服务器的CPU使用率不高。

下图是Web服务器带宽报表。

从这个报表可以看到,每块竞赛带宽的占用都会出现一个很高的峰值。

我们再看数据库服务器的带宽报表。

同样的,数据库服务器同样的在竞赛的时间点流量超大,很明显这种情况是不正常的,查询肯定是有问题的。

面对这样的问题,确定了第一期主要做以下的优化。

1、用flash storage做用户做题断点记录。(做题断点:类似程序断点,用户做到第N题时退出做答,下次进入时依然定位到第N题。)这里原来是用数据库存储的,但 用户每做一题都会执行一条UPDATE语句,而数据库是MySQL的MyISAM引擎,更新操作经常被堵塞。

2、更改系统交卷行为。原来系统在用户做完提交竞赛后,会执行一条UPDATE语句更新用户提交试卷的时间点。同样的这个UPDATE也是在同一时间段内执行,和产品经理沟通后,确认在最后一分钟的时候可以不用再执行这个更新,允许用户的作答时间有1分钟的误差。

3、调整数据库的更新语句为插入语句,这个优化点因为时间问题,推迟到第二个优化阶段再处理。

4、调整应用服务器以支持LVS集群。对当前系统进行分析后,暂时可以不用调整代码直接部署集群,问题是在多台服务器内都会存在相同的进程内缓存,这种情况暂时是可以接受的,后期需要改到MemCached集中管理缓存。

5、等待成绩页面同一时间跳转的压力问题。在线竞赛的提交时间点很集中,用户做答完题目后,会统一跳转到一个页面等待答案(这时后台的 Windows 服务在进行竞赛统计),这里服务器的并发、带宽压力都非常大。因此,优化这里不进行跳转,而是在当前的页面等待,并且会自动给不同的用户分配不同的等待时 间,以避免占满服务器的带宽。

6、每场完整的公务员考试试卷,题目资源有150K-200K,因为作答和查看解析是在不同的页面,之前的实现会造成题目的多次加载,严重的浪费了 带宽资源。于是这里优化成Handler输入静态资源加载,从服务器加载一次之后,后面所有的地方调用到题目都可以从浏览器的本地缓存中加载带省服务器带 宽。同时,服务器上只对静态资源服务器开启了GZip压缩,对动态文件进行压缩会浪费服务器的CPU资源,而只对Handler输出的题目进行GZip压 缩,一方面节省了CPU,另一方面把150K-200K的题目资源压缩到了50K左右。

7、数据库性能优化。调整了代码中查询的各个条件的位置,使查询语句能够更多的使用到索引。同时把原来每次一条的插入操作修改为一次插入多条等一些数据库查询优化。

任何一个优化都要针对已经存在的问题,从服务器监控的报表可以看到我们这个网站应用服务器带宽压力、数据库服务器带宽压力都很大,应用服务器的CPU使用率不高,因此,主要的优化是对应用服务器带宽和数据库服务器的写入压力做的优化,因为目的很明确,效果也是比较明显的。

文中提到了用Handler来输出静态资源让浏览器缓存,附上这个代码,其它的优化针对性很高,就不再啰嗦了,主要的还是记录下这次优化的工作方式和工作方法。

Handler输出的静态资源使用了.NET流压缩,于是我们声明一个压缩器接口。

using System.IO;namespace ND.Race.Compressor
{/// <summary>/// 压缩器接口/// </summary>public interface ICompressor{string EncodingName { get; }bool CanHandle(string acceptEncoding);void Compress(string content, Stream targetStream);}
}GZip压缩器实现这个接口。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29using System.IO;
using System.IO.Compression;
using System.Text;namespace ND.Race.Compressor
{public sealed class GZipCompressor : ICompressor{public string EncodingName{get { return "gzip"; }}public bool CanHandle(string acceptEncoding){return !string.IsNullOrEmpty(acceptEncoding) &&acceptEncoding.Contains("gzip");}public void Compress(string content, Stream targetStream){using (var writer = new GZipStream(targetStream, CompressionMode.Compress)){var bytes = Encoding.UTF8.GetBytes(content);writer.Write(bytes, 0, bytes.Length);}}}
}

同样的Deflate压缩器也实现这个接口。

using System.IO;
using System.IO.Compression;
using System.Text;namespace ND.Race.Compressor
{public sealed class DeflateCompressor : ICompressor{public string EncodingName{get { return "deflate"; }}public bool CanHandle(string acceptEncoding){return !string.IsNullOrEmpty(acceptEncoding) &&acceptEncoding.Contains("deflate");}public void Compress(string content, Stream targetStream){using (var writer = new DeflateStream(targetStream, CompressionMode.Compress)){var bytes = Encoding.UTF8.GetBytes(content);writer.Write(bytes, 0, bytes.Length);}}}
}

如果浏览器不支持流压缩,那我们只能直接输出内容了,因此我们还需要一个不进行压缩的处理类。

using System.IO;
using System.Text;namespace ND.Race.Compressor
{public sealed class NullCompressor : ICompressor{public string EncodingName{get { return "utf-8"; }}public bool CanHandle(string acceptEncoding){return true;}public void Compress(string content, Stream targetStream){using (targetStream){var bytes = Encoding.UTF8.GetBytes(content);targetStream.Write(bytes, 0, bytes.Length);}}}
}

现在我们就可以开始编码我们的Handler了。

public class QuestionCacheHandler : IHttpHandler
{#region 静态变量/// <summary>/// 资源过期时间/// </summary>private static readonly int durationInDays = 30;/// <summary>/// 流压缩接口/// </summary>private static readonly ICompressor[] Compressors = {new GZipCompressor(),new DeflateCompressor(),new NullCompressor()};#endregion#region 私有变量/// <summary>/// 内存流压缩类/// </summary>private ICompressor compressor;/// <summary>/// ETAG/// </summary>private string eTagCacheKey;/// <summary>/// 竞赛场次Id/// </summary>private long raceId;#endregionpublic void ProcessRequest(HttpContext context){if (context == null) return;long.TryParse(context.Request.QueryString["raceId"], out raceId);if (raceId == 0) return;       var acceptEncoding = context.Request.Headers["Accept-Encoding"];compressor = Compressors.First(o => o.CanHandle(acceptEncoding));eTagCacheKey = string.Concat(raceId, "/etag");if (IsInBrowserCache(context, eTagCacheKey)) return;SendOutputToClient(context, true, eTagCacheKey);}/// <summary>/// 发送内容到客户端/// </summary>/// <param name="context"></param>/// <param name="insertCacheHeaders"></param>/// <param name="etag"></param>private void SendOutputToClient(HttpContext context, bool insertCacheHeaders, string etag){string content = "";MemoryStream memoryStream = new MemoryStream();compressor.Compress(content, memoryStream);byte[] bytes = memoryStream.ToArray();HttpResponse response = context.Response;if (insertCacheHeaders){HttpCachePolicy cache = context.Response.Cache;cache.SetETag(etag);cache.SetOmitVaryStar(true);cache.SetMaxAge(TimeSpan.FromDays(durationInDays));cache.SetLastModified(DateTime.Now);cache.SetExpires(DateTime.Now.AddDays(durationInDays)); // HTTP 1.0 的浏览器使用过期时间cache.SetValidUntilExpires(true);cache.SetCacheability(HttpCacheability.Public);cache.SetRevalidation(HttpCacheRevalidation.AllCaches);cache.VaryByHeaders["Accept-Encoding"] = true;}response.AppendHeader("Content-Length", bytes.Length.ToString(System.Globalization.CultureInfo.InvariantCulture));response.ContentType = "text/plain";response.ContentType = "application/x-javascript";response.AppendHeader("Content-Encoding", compressor.EncodingName);if (bytes.Length > 0)response.OutputStream.Write(bytes, 0, bytes.Length);if (response.IsClientConnected)response.Flush();}/// <summary>/// 是否浏览器已经缓存/// </summary>/// <param name="context"></param>/// <param name="etag"></param>/// <returns></returns>private bool IsInBrowserCache(HttpContext context, string etag){string incomingEtag = context.Request.Headers["If-None-Match"];if (String.Equals(incomingEtag, etag, StringComparison.Ordinal)){context.Response.Cache.SetETag(etag);context.Response.AppendHeader("Content-Length", "0");context.Response.StatusCode = (int)System.Net.HttpStatusCode.NotModified;context.Response.End();return true;}return false;}public bool IsReusable{get{return false;}}
}

服务器端代码通过Http请求Header的Accept-Encoding来判断是否支持流压缩,再通过Header的etag来判断浏览器中是否已经有缓存副本。

转自:http://blog.moozi.net/archives/web-performance-optimization-practice-application-optimization.html

转载于:https://www.cnblogs.com/xiaopohou/archive/2011/09/20/2182811.html

相关文章:

技术图文:Python魔法方法之属性访问详解

背景 今天在B站学习“零基础入门学习 Python”中的第45节“魔法方法&#xff1a;属性访问”&#xff0c;这也是我们组织的 Python基础刻意练习活动 的学习任务&#xff0c;其中有这样的一个题目。 练习要求&#xff1a; 写一个矩形类&#xff0c;默认有宽和高两个属性。如果…

chmod权限设置

drwxr-xr-x. 7 root root 4096 Sep 26 20:16 sysconfig-rw-r--r--. 1 root root 1150 Aug 31 18:46 sysctl.conflrwxrwxrwx. 1 root root 14 Aug 31 17:21 system-release -> centos-release例如&#xff1a;-rw-r--r--第一个代表文件类型:-普通文件&#xff1a;…

【Python培训基础】一篇文件教你py文件打包成exe

场景: 如果要将我们编写好的代码给别人使用,如果要他们直接使用我们的代码,就需要安装各种编译软件以及第三方模块,还要对软件操作,编程有一定的了解,这对使用者的要求比较高,不是很方便,为了解决这一问题,我们可以选择将我们编写的代码,编译成一个可执行文件,这样,就可以实现跨…

刻意练习:Python基础 -- Task06. 字典与集合

背景 我们准备利用17天时间&#xff0c;将 “Python基础的刻意练习” 分为如下任务&#xff1a; Task01&#xff1a;变量、运算符与数据类型&#xff08;1day&#xff09;Task02&#xff1a;条件与循环&#xff08;1day&#xff09;Task03&#xff1a;列表与元组&#xff08;…

WCF - Session 剖析

WCF中的Session WCF是MS基于SOA建立的一套在分布式环境中各个相对独立系统进行通信的构架&#xff0c;实现了最新的基于WS-*规范。按照SOA的原则&#xff0c;相对独自的业务逻辑以service的形式封装&#xff0c;调用者通过Messaging的方式调用Service。对于承载着某个业务功能的…

mui 微信支付 与springMVC服务器交互

昨天搞定了微信支付,没有想象中的难,主要是官方的demo不全好多东西要自己琢磨,mui端的就不写了支付宝的有了一模一样.上java端的首先是jar包 一个是用来解析xml文件 一个是用来解析json 当然可以替代 然后是工具类当然并不是全都用的到. public class ConfigUtil { /** * 服务…

Python零基础自学会有哪些弊端

Python在人工智能领域的发展前景非常好&#xff0c;很多人都想要学习Python技术&#xff0c;有一些小伙伴会选择通过自学来学习&#xff0c;但是如果是零基础&#xff0c;自学的话一定要注意这些弊端&#xff0c;下面就为大家详细的介绍一下Python零基础自学会有哪些弊端? Pyt…

技术图文:如何利用 Turtle 绘制一棵漂亮的樱花树

背景 最近看到很多机构在推动“青少年编程能力等级标准”的制定以及相关考试的测评&#xff0c;看样子今年年底这个事情就能够确定&#xff0c;明天上半年在一些大中城市就会全面铺开。 《青少年编程能力等级》标准发布&#xff0c;年底前将在部分地区落地青少年编程能力等级…

Python 是否是下一个 PHP?为什么?

前几天和一个看好 Python 的 Rails 开发者聊天&#xff0c;他看好 Python 的原因就是 PHP 统治今天的网络应用开发。而 Python 很像下一个 PHP 。 『下一个 PHP』如何定义&#xff1f;是指流行程度么&#xff1f;如果是的话&#xff0c;我觉得 Python 不会像 PHP 那样流行。根本…

正确使用STL-MAP中Erase函数

一切尽在代码中。 #include <iostream> #include <map> #include <string> using namespace std ;int main(void) { map<int, string> m ;m.insert(pair<int, string>(1, "abc")) ;m.insert(pair<int, string>(2, "def&qu…

学完UI设计可以从事哪些工作

最近有很多同学都会问到一个问题&#xff0c;就是学完UI设计可以从事哪些工作?对于正在学习UI设计的同学和已经学完UI设计的同学们&#xff0c;可以来看看下面文章的详细介绍就知道了。 学完UI设计可以从事哪些工作? 一、交互设计师。 学习UI设计之后就可以做交互设计师了&am…

刻意练习:Python基础 -- Task08. 异常处理

背景 我们准备利用17天时间&#xff0c;将 “Python基础的刻意练习” 分为如下任务&#xff1a; Task01&#xff1a;变量、运算符与数据类型&#xff08;1day&#xff09;Task02&#xff1a;条件与循环&#xff08;1day&#xff09;Task03&#xff1a;列表与元组&#xff08;…

Winform 控件自适应 JSP 入门登录案例

明儿在放&#xff0c;先睡转载于:https://www.cnblogs.com/javabin/archive/2011/09/26/2192402.html

MyEclipse对Struts2配置文件较检异常 Invalid result location value/parameter

有时在编写struts.xml时会报错&#xff0c;但是找不出有什么她方有问题。也能正常运行 MyEclipse有地方去struts的xml进行了验证&#xff0c;经查找把这里 的build去掉就可以了 本文转自lpxxn博客园博客&#xff0c;原文链接&#xff1a;http://www.cnblogs.com/li-peng/p/3791…

学Python有哪些优势

Python在人工智能领域应用是比较广泛的&#xff0c;近几年&#xff0c;越来越多的人对Python技术比较感兴趣&#xff0c;想要学习&#xff0c;那么具体学Python有哪些优势呢?我们来看看下面的详细介绍就知道了。 学Python有哪些优势? 1.Python很受欢迎 流行程度似乎不是衡量价…

MongoDB 正则表达式

阅读目录 示例不区分大小写数组使用正则表达式正则中包含变量回到顶部示例 MongoDB 使用 $regex 操作符来设置匹配字符串的正则表达式。 > db.col.find() { "_id" : ObjectId("56c6bbef64799370c0ef358a"), "x" : "hello world", &…

刻意练习:Python基础 -- Task09. else 与 with 语句

背景 我们准备利用17天时间&#xff0c;将 “Python基础的刻意练习” 分为如下任务&#xff1a; Task01&#xff1a;变量、运算符与数据类型&#xff08;1day&#xff09;Task02&#xff1a;条件与循环&#xff08;1day&#xff09;Task03&#xff1a;列表与元组&#xff08;…

Java学习必不可少的网站,快收藏起来

java技术在IT互联网行业的发展前景一直在提升&#xff0c;越来越多的人都在学习java技术&#xff0c;今天小编来给大家提供一些学习Java的网站集合&#xff0c;希望能够帮助到正在学习java技术的同学。 Java学习必不可少的网站&#xff0c;快收藏起来! 1. Stackoverflow Stacko…

刻意练习:Python基础 -- Task11. 魔法方法

背景 我们准备利用17天时间&#xff0c;将 “Python基础的刻意练习” 分为如下任务&#xff1a; Task01&#xff1a;变量、运算符与数据类型&#xff08;1day&#xff09;Task02&#xff1a;条件与循环&#xff08;1day&#xff09;Task03&#xff1a;列表与元组&#xff08;…

Oracle中的MERGE语句

转自http://blog.chinaunix.net/space.php?uid16981447&doblog&cuid430716做了简单的格式整理&#xff0c;加入了一点点原创的东西。Oracle9i引入了MERGE命令,你能够在一个SQL语句中对一个表同时执行inserts和updates操作. MERGE命令从一个或多个数据源中选择行来upda…

C#从数据库导出数据[excel]

using System;using System.Collections.Generic;using System.Linq;using System.Text;using System.Data;using MySql.Data.MySqlClient;using Microsoft.Office.Interop.Excel;using Excel Microsoft.Office.Interop.Excel; //使用命名空间别名using System.Reflection; …

UI设计培训中的扁平化理念

本文是为正在学习UI设计的同学们整理的一份资料&#xff0c;主要讲的是UI设计培训中的扁平化理念&#xff0c;扁平化的设计是抛弃一切装饰的设计&#xff0c;扁平化设计使得用户操作起来更加简洁、高效和舒适。简洁大方的交互界面设计自然能够引导用户&#xff0c;并且在短时间…

刻意练习:Python基础 -- Task12. 模块

背景 我们准备利用17天时间&#xff0c;将 “Python基础的刻意练习” 分为如下任务&#xff1a; Task01&#xff1a;变量、运算符与数据类型&#xff08;1day&#xff09;Task02&#xff1a;条件与循环&#xff08;1day&#xff09;Task03&#xff1a;列表与元组&#xff08;…

Linux JSP连接MySQL数据库

Linux&#xff08;Ubuntu平台&#xff09;JSP通过JDBC连接MySQL数据库&#xff0c;与Windows平台类似&#xff0c;步骤如下&#xff1a; 下载 jdbc&#xff1a; mysql-connector-java-5.1.18.tar.gz 解压 jdbc&#xff1a; tar -zxvf mysql-connector-java-5.1.18.tar.gz 配置 …

h5 getUserMedia error PermissionDeniedError

HTML5 在使用非 localhost 地址访问时打开摄像头失败 。报getUserMedia error PermissionDeniedError&#xff0c;火狐下是可以正常调取的。 需要https&#xff1a; 火狐&#xff1a; 转载于:https://www.cnblogs.com/cosyer/p/7646672.html

女生零基础学软件测试难不难

软件测试属于一门IT技术编程语言&#xff0c;很多人都觉得IT技术都是男性比较多&#xff0c;按照目前的行业数据来看&#xff0c;确实是男性居多&#xff0c;但最近几年&#xff0c;女性程序猿也越来越多&#xff0c;其中就有软件测试这个岗位&#xff0c;下面具体来看看女生零…

技术图文:NumPy 的简单入门教程

背景 这段时间&#xff0c;LSGO软件技术团队正在组织 “机器学习实战刻意练习”活动&#xff0c;这个活动是“Python基础刻意练习”活动的升级&#xff0c;是对学员们技术的更深层次的打磨。在用 Python 写各类机器学习算法时&#xff0c;我们经常会用到 NumPy库&#xff0c;故…

Android常见错误

1、Unable to resolve target android-2 安装低版本的api&#xff0c;再default.properties 这个文件中把targetandroid-2 改成 targetandroid-7终于就没有问题了。 2、Invalid start tag LinearLayout main.xml放错文件夹了&#xff0c;应该在\res\layout下。 3、INSTALL_FAIL…

【开发】简易教程

本文档将带你一步步创建完成一个微信小程序&#xff0c;并可以在手机上体验该小程序的实际效果。这个小程序的首页将会显示欢迎语以及当前用户的微信头像&#xff0c;点击头像&#xff0c;可以在新开的页面中查看当前小程序的启动日志。下载源码 1. 获取微信小程序的 AppID 登录…

Python未来的发展趋势怎么样

Python未来的发展趋势怎么样?最近很多人都在学习Python技术&#xff0c;但是在学习的过程中&#xff0c;还是比较担心Python是否有发展前景这个问题&#xff0c;我们来看看下面的详细解析。 Python未来的发展趋势怎么样? 一、从事Python的待遇高。 由于Python语言的应用领域很…