【IT168 技术】2012年春运期间,备受关注的火车票有了新的购买方式-网络和电话,人们一度天真的以为再不用去寒风瑟瑟的冬天去忍饥挨饿、排队购票了,但是现实无情了给了人们当头一棒,页面迟迟显示不出、登录半小时都能难系统……本来是一件利国利民的好事,结果有点出乎意料,当然究其背后的原因,很复杂,为此IT168专门采访了HP资深架构师汪洋,请他从技术的角度帮我们分析下网站架构过程中应该注意哪些问题?
汪洋(Richard Wang),安捷雨希软件咨询创始人(http://so.agilesharp.com),资深架构师和.NET技术专家。
针对12306目前的情况,汪洋表示最大的短板在于查询操作,由于在购票过程中,需要不断的查询火车的车次、日期及座位的相关情况,从而引导客户买票,正因为是基于实时的查询,所以查询的性能问题可能会引起蝴蝶效应。
而对于很多乘客提出的购票过程中的登录难的问题,汪洋谈到可以采用读缓存的方式解决;但对于下单难问题,由于其涉及到大数据量并发写入的问题,确实有一定的挑战,但并不是没办法解决,另外还猜想12306为了保证数据库的一致性等,其数据库里面的锁机制可能很严格,而锁机制正是性能的一大硬伤。
谈到12306的具体架构,汪洋表示可以借鉴国内电商淘宝、京东等的模式,因为流程很类似,但是针对每个部分(从前端到后端的数据存储)都需要作出改进。
网站架设 客户端如何改进
首先,对于一个站点,首先要考虑的是如何使用户可以最快的看到网页的内容。而对于一个网页来说,内容无非就是数据,html,资源(图片,脚本,css),其中对于资源(图片,js,css)可以采用CDN进行,一方面不占用站点本身的网络带宽,另一方面可以使不同地区的用户快速查看资源内容;而对于html和数据,就需要服务器端尽快的生成内容。
当相关的页面达到了客户端浏览器之后,就需要考虑交互问题了。用户最终是要向服务端提交内容的,而在这个时候可以考虑:只向其提交必要的数据,而不是每次把整个页面都传输过去,所以,可以适当的采用ajax技术,达到只提交数据,而不提交html标记的目的。当请求到达服务端后,也只把数据发送给客户端。
而关于客户端如何展示内容,可以采用客户端代码javascript来进行,同时客户端也可以设计缓存架构,例如,不要每次需要的时候都去服务端请求,可以一次多请求一部分数据,因为请求服务端是要开tcp连接的,一旦用户过多,服务端会不堪重负,就像我们在网上购物一样,需要显示一些商品列表,现在很多电商的做法是分页,一页一页的去请求;其实,我们可以考虑一次请求5页,10页的内容,当然很多人会考虑这其中会不会有浪费的问题,这个时候,其实就需要比较数据传输和 tcp连接成本问题。
从这一点可以看出,一个好的网站绝对是精打细算出来的,对于每一点都需要优化、仔细估算。要实现这个一方面需要挖掘用户的习惯,考虑用户一般需要翻几页,很多用户,习惯性的只翻前几页,极少用户会翻到几十页,二就是通过多传输一部分数据,以减少服务端的压力,这其中可以算笔账:多给5页的数据,在此之间就减少5次对服务端的请求,一个用户减少5次,百万,千万呢?
传输过程如何考虑
客户端请求到达服务端后,我们需要保证:网页内容在网络上尽可能快的传递,这可以从两个方面考虑:1.从站点本身来考虑,2.从网络传输的过程考虑,前者是我们确确实实可以控制的,后者就不一定了,不同的用户网络配置、带宽不一样,所以每次在传递内容的时候,要尽可能的压缩,这也是最主要的建议,不过压缩是需要消耗CPU的,CPU的重要性相信大家都明白。
所以,好的站点,不是花钱砸出来的,而是“算计”出来的,从网络传输的过程考虑可以把一些改动比较不多的页面,并且没有用户特性的(不需要登录,验证就能看的)缓存在代理,这样用户请求这些页面时候,就可以直接从代理获取数据,减少对服务端的请求。
服务端配置考虑
服务端相关设计、配置,包括软件和硬件两个大的方面,首先说说服务端的第一个重要的软件-网站宿主或者说apache或者IIS、还有ngix等等,因为很多时候,宿主的配置也很重要,由于汪洋主要是从事的是Windows平台相关架构工作,所以重点介绍了IIS。
▲IIS处理流程
下面通过模拟一个请求,了解IIS是如何工作的,从这个图中,我们可以很清楚的看到请求是如何最后被IIS请求,然后响应发送到客户端的:1. 当请求到达服务器,首先进入内核模式,由内核组件http.sys接受,在这里,会进行验证Url是否合法等,另外根据请求Url,去内核缓存中,检测是否有对应的响应,2.如果有,直接返回;否则,进行一次用内核模式到用户模式的切换,将请求交给IIS的监听线程,IIS监听线程依然去检测IIS的缓存,如果没有,在进行线程切换,将请求给ASP.NET出来线程,后面的以此类推。
那么,在得到一个响应的过程中,可能会涉及到三次的线程切换,还有两次的模式切换,虽然在IIS7 7.5中,改进了这个过程,但是这其中代价依旧很大,最好的方式就是数据均保存在内核缓存中,然而这时又不得不面临另一个问题,大量数据存放于缓存中对性能的影响非常大,这时可以在IIS中设置一些缓存的条件,在默认的情况下,静态文件,例如html,图片,js,css是缓存在IIS内核中的,但是动态的页面和内容,是不在缓存中的,我们可以通过设置,让IIS缓存动态的内容,只不过缓存的时间短一些而已,站点的内容不可能时时刻刻更新,总有一段所谓的“数据静态时间(数据不变)”,几秒也行。
至于如何设置缓存,主要就是修改IIS中的配置文件,很简单,这里不赘述,至于缓存,这就是关键了,IIS缓存有限,要分析哪些页面是访问量最大的,把这几个页面置于缓存中;另外,就是压缩的问题了,在数据传输之前,IIS可以控制压缩,在IIS中 压缩分为9个级别,级别越高,压缩的就越厉害,但是消耗的CPU也就越厉害,这其中就需要折中,我们会发现,整个网站的架构中,很多方面都需要折中。
▲IIS组件安装-动态内容压缩
▲通过命令行 设置IIS的压缩级别
另外可以考虑定制IIS,开发IIS中间件,具体要考虑有没有这个必要了。
负载均衡如何选择
当请求到达宿主后,就需要考虑负载均衡的问题了,负载均衡可以是硬件层面的,也可以是软件层面的,走OSI模型第二层协议,或者第七层协议,关于window平台下面的负载均衡软件,首先,很多的linux的负载均衡产品,在window中都有对应的版本,另外 我建议的就是linux和window结合。
▲其中linux服务器只是负责转发请求
当然,在window中IIS也有比较成熟的负载均衡软件,从这个图中看到的就是IIS中比较成熟的软件负载均衡,和IIS直接集成,并且有很强的可视化界面。这其中可以设置很多的规则 选择不同的负载均衡算法。
▲Server Farm
这是一种,另外window服务器也自带了负载均衡的功能,可以采用在这样的方式:
▲Windows自带负载均衡功能实现
这是软件的实现方法,如果预算有限,就采购几台服务器,再加上软件负载均衡,比买硬件成本低很多,如果不差钱的话,就砸钱买设备吧,EMC的F5负载均衡就是不错的选择。
但是有一点要说明就是:固然负载均衡(不管是软还是硬的)可以解决一些问题,如果站点本身的架构设计和编码写的不好,再多的设备,也是宛然。另外,也可以考虑自己开发一个负载均衡软件,难度不大,就看有没有必要了。硬件上的负载均衡可以找专业公司搞定,不多赘述。
代码编写 优化
介绍过负载均衡,下面我们该谈谈请求被接受之后,处理的流程了,那么就谈到了站点本身的设计-编码问题了,这个问题相比较就很复杂了,可以说是步步惊心,就连写一句for循环,也是小心再小心,而现在很多技术人员在写代码的时候,考虑的并不多,这也会引起很多问题,代码写的烂,一下子耗掉N多内存,再多的内存条也是白搭。下面是代码编写、优化中需要注意的一些问题。
- 内存瓶颈分析
内存性能问题可以分为两个部分:内部内存压力,外部内存压力。其中内部内存压力主要是站点本身在运行的过程中消耗过多的内存,基本是可以从托管资源,非托管资源两方面分析;而外部内存压力指代站点所在的服务器上面的其他应用于站点本身之间进行资源的争夺,从而使得站点可以使用的内存太少。那么在ASP.NET企业级应用中,我们技术人员关注点可以放在内部内存压力。
首先看看托管资源的问题。
为什么要讨论托管资源?因为托管资源(就是分配中在托管堆上面的对象),分配在托管堆上,而托管堆在内存中,所有托管资源的合理的分配和回收,会对内存产生影响。
ASP.NET应用的功能就是由很多的对象组合完成的,所以讨论托管资源很有必要。
在.NET中,托管堆分为两类:大对象托管堆,小对象托管堆。一般而言,如果对象所占的空间小于85K,就分配在小堆上面,反之,分配在大堆上面。如图的小堆图:
在对上面,对象被划分为三代:0,1,2代。“代”数越大,被回收的可能性就越小。基于这个理论,就要避免原本只要是低代的对象变为高代的对象,例如某个对象用完之后就销毁的,原本是0代的,现在存活到了2代,那么它所占的内存就浪费了。虽然,垃圾回收机制没有在一定程度上回收,但是如果我们总是“霸占”着对象,垃圾回收也没有办法。
下面,举个例子,形象的说明一下。对于一个大型的ASP.NET应用而言,里面会包含很多的对象,假设10000个,其中每次请求都要产生的100个临时对象。如果这些临时对象,被不合理的分配,成为了大代的对象,我们来算一算。
如果这个100个对象每个占1k的空间,那么100个,我们约等于0.1M,如果站点访问量是10w,那么如果这些0.1乘以10w,如果访问量是100w,1000w,结果如何?大家已经清楚了。很多,需要将问题放大来看,结果就很清晰了。
对于对象的使用,有一点要记住:尽可能迟的分配,尽可能早的释放。
下面,我们可以谈谈,如何找出内存问题。
可以采用工具加分析的方式。可以采用System Counter,CLR Profiler,ANTS Memory Profiler(Red Gate)等。其中System Counter,CLR Profiler分析的比较的粗糙,ANTS Memory Profiler(Red Gate)可以指出是哪段代码有问题,方便解决问题。如图
另外,我们可以看看一些常见的性能问题,例如字符串相关问题,Session,缓存,对象池。
- 字符串
字符串相关问题应该是个老生常谈的问题了,主要涉及拼接与比较的问题。对于拼接,如果大于7个字符串拼接,最好是先顶一个缓冲区,例如StringBuilder,再拼接。对于比较,不要用“==”,而要用String.Compare函数,并且带入文化信息(culture)进行比较,效率更高。
Session
下面,我们来看看与Session相关的话题。在考虑Session的时候,需要从多方面考虑:是否使用Session?Session里面保存什么数据?Session的过期时间是多少?站点的并发用户是多少?
我们来举一个例子,通过一些数字,让朋友们有一个比较清晰的了解。
在ASP.NET中,Session的过期时间默认是20分钟,我们就以默认的过期时间为例。
1. 假设,每个用户在站点驻留的时间为10分钟,那么用户的Session的过期时间就延长为了30分钟。也就说,在这30分钟内,用户的Session都是有效的。
2. 假设站点每秒钟接受100个请求,每个用户在站点中每秒发送5个请求,那么,站点每秒就相当于有20个并发用户访问。
3. 假设我们从用户的第一个请求开始,为他们保存Session信息,那么在1分钟内,站点上面的Session个数为:(30分钟—Session的存活时间)*(60秒)*(20—每秒钟并发访问的20个用户)=36000个Session。
4. 如何每个Session保存10K的数据,那么1分钟内,站点所有Session占用的内存就约等于360M。
相信通过上面这些数据,朋友们应该很清楚如何回答之前提出的问题了。
另外,在使用Session的时候,我们还需要考虑站点以后的发展。如果站点在以后采用了负载均衡,那么我们要在多个服务器之间进行Session的同步,否则,就可能会出现,用户登录的时候,Session保存在服务器A上,第二次请求的时候,请求被定向到服务器B上面,此时B上面没有Session,用户还需要重新登录。此时要考虑负载均衡产品是否支持Session同步,或者我们自己从技术上面解决,例如采用分布式Session,其原理和分布式缓存类似。
- CPU瓶颈分析
对于CPU性能瓶颈的分析,我们依然可以采用系统性能计数器或ANTS Profiler(Red Gate)中的CPU套件来寻找。另外VS2010也自带了性能检测工具,用法和ANTS Profiler类似,朋友们可以上MSDN去自行学习。
当发现服务器的CPU使用过高,首先不应该立刻去诊断CPU,因为很多的其他性能问题会直接反映在CPU上面,例如内存不足。当出现了内存不足的问题的时候,CPU会将原来需要保存在内存中的数据保存在磁盘上面,这样,就加大了CPU的调度,从而使得CPU产生压力。所以,当CPU出现问题的时候,首先去检测是否内存瓶颈,如果内存是没有问题的,那么在回过来分析CPU的问题。
在分析CPU的问题的时候,我们就要关注在整个站点中,有哪些操作是很消耗CPU的。一般而言,可以从以下几个方面入手:加密、解密,垃圾回收,解压缩,算术运算,过度编译。
- 解压缩
正如上面所说的,压缩是一个很消耗CPU的过程,解压也是。并且解缩还是非常消耗内存的,因为很多的解缩操作都会先将内容放进内存,等整个文件解压完之后,然后将之从内存写到磁盘。
采访嘉宾介绍: