Archive for October, 2007

高性能应用程序开发

Monday, October 22nd, 2007

高性能应用程序开发

Author: gashero
Date: 2007-10-22

目录

1 高性能的时代

当你面对汹涌而来的用户时,该怎么做?

产品策划的成功、推广的成功带来了汹涌而来的用户,随之问题开始凸显,曾经运行的很好的服务器,开始极度缓慢;运营经理哭丧着脸看着你显示出他回天无力一般的无奈。在一次成功的推广之后,突然访问量暴增了200%,然后,就在那个能够给你带来划时代推广意义的时候,服务器宕机了。

2 WEB应用的基本结构

一般的WEB应用包含几个基本结构:

  1. WEB应用
  2. 应用服务器
  3. 数据库

这似乎是无可挑剔的简洁,但是问题是当访问量猛增的时候,链条上任何一个结点都可能会成为瓶颈,并且让你欲死不能。

2.1 WEB应用

先进可以做WEB应用的技术很多,比较通用的有PHP、JSP、ASP.net等。一般来说,WEB应用技术本身的性能是无法构成瓶颈的,真正的瓶颈在于这些优雅的框架上编写出的低效的程序。那些经常 select count(*) from xxx 的程序员才是你的恶梦。当然,有一点不得不说,如果一个框架足够难学,那么意味着能够到达这个层次的程序员往往水平也很高,在数据结构、性能优化方面也往往经验丰富,这时候,他所能做的高性能的事情已经不再是一种框架可以制约的了。

PHP本身有着很方便的部署,PHP本身也不太难学,但是一个特点是PHP只有在Linux下才足够地道,导致了相当的学习成本。所以,国内的PHP程序员往往都是经久考验的。

JSP/Servlet本身是优秀的,但是在Java相关技术火热的发展起来之后,程序员队伍的总体素质就堪忧了。其实真正可以从J2SE一路学过来的也倒没什么大碍,关键是怕那些Java WEB速成派。当然从这里也侧面的想到那些IT培训学校所倡导的几个学速成加就业包票的讲法,实在是没的说。也许这个世界上从来都不曾有真正的窍门存在。

至于ASP.net,好用的工具还是要看归什么人用,ASP.net学习成本的低廉造就了一批水平也是相应的程序员。当然,不排除用ASP.net的高手,但是问题是平均水平。

2.2 应用服务器

应用服务器实际上对整个系统性能也有很大的影响,比如resin就比Tomcat性能要高,其他等等不一而足。同时应用服务器的配置也是个艺术。

2.3 数据库

数据库对整体性能影响也是比较大的,同时对于一个不断成长的系统,必须要考虑到未来扩展的可能。

3 高性能WEB应用的一般结构

从前端到后端依次是:

  1. 负载均衡Load Balance
  2. squid
  3. 应用服务器
  4. 中间件/memcache
  5. 数据库服务器

3.1 负载均衡

面对大规模的用户请求,负载均衡是不可或缺的。当然这个东东市场上卖的很贵,可以按照自己的需求来买。最近一段时间流行的第四层交换也可以考虑,产品多多。如果做的不是非常之专业的话,用squid来做目的代理服务器也有相同的功能。

一些软硬件防火墙一般也带有负载均衡的功能,可以利用起来,当然,某种产品中附带的某某功能,肯定是不如专业的好。

负载均衡妙处很多,可以自己慢慢发现,包括好偶面的squid到应用服务器这一层实际上也可以加上负载均衡来确保各个服务器稳定一点。

3.2 squid

用来缓存动态生成的页面,降低服务器的负载。不同的URL有时候需要不同的缓存超时时间。

当然squid在这里的主要用途是降低服务器负载,另外一种技术是网站静态化,达到类似的效果。总的来说加入这样一个缓存层次之后网站的总体负载就可以达到可控的地步了。另外,也可以对squid服务器层进行平行扩展以达到一定范围之内的网站扩容了。

3.3 应用服务器

同上所说,不过既然是在重负载的环境,也就需要好好配置了。

3.4 中间件/memcache

随着网站规模的不同可以选用中间件或memcache,简单一点的网站把数据库的中间查询结果缓存到memcache中即可。不过总体来说,即便是有中间件memcache也是很值得推荐的。

3.5 数据库服务器

巨无霸的Oracle未必就是最好的,多学一些数据库系统原理吧,适应你的需求哪怕是你自己写出来的sqlite集群也会比那些所谓成熟的解决方案好的多。

程序员作为一个以思考来吃饭的职业,如果不去思考,而把一切都寄希望于别人做好的东西可就不好了。不用怀疑,如果上面的那些技术你都用到了,那么说明这个网站已经开始遇到一些前人没有解决的问题了。想办法用你的聪明智慧来解决吧,因为这个时候你已经没法指望别人了。

4 一些基本原理

4.1 数据库连接

数据库的连接计算非常耗时,尽可能避免,如果不可避免那就尽可能减少连接的层次。甚至,你可以为了性能而不遵守各类范式。比如不去做连接,而是把被连接的数据串行化以后存到字段里面。

4.2 数据库统计

数据库的各类统计都是恶梦级别的,包括COUNT()、SUM()等函数调用,和排序操作。有如上面说的,为了性能你可以用尽各类方法,包括不遵守数据库的范式。一些函数的所得数据如果确实很必要,那么就把他们缓存起来。比如一个用户的某个资源数量统计,就可以存入这个用户的一个字段中,并且在每次资源增减时更新这个字段的值。

至于排序,很多时候其实是为了取得某个参数最高的几个,这时大可以在每次更新时把排序字段最高的几个缓存起来。

SUN的某牛曾说,计算机技术历史上最伟大的思想是缓存,用好缓存吧。

4.3 操作流程

每一种操作如果可以按照流程分步骤的话,就可以看清楚很多问题了。因为一个流程的耗时是各个流程耗时的和,这意味着如果可以减少流程,就会提高性能。

当然,与之相悖的是逻辑清晰度,减少划分的层次就会导致一些逻辑合并到了一起。这时如何取舍就是你自己的问题了。想想逻辑清晰的目的。曾经听过一句话:为了用户未必需要的未来的升级而预先做太多的提高灵活性、移植性等等的工作,实际上是违反软件工程师职业道德的体现。

这个事情上推荐去学习一下Tomcat的分层,作为反面典型。

4.4 数据库只是数据库

数据库只是用来存储数据的地方,不要过度依赖SQL语句带来的计算功能,也不要把计算的中间结果也存到数据库里面去。计算并不是数据库的强项,更何况你这样做毫无优化可言。所以当你看到一个很长的SQL语句时应该先仔细考虑他是否有优化的可能。再者,有些人喜欢把计算的每个中间结果都存到数据库里面去,并且把这些结果用于调试。这样做在网站稍微上一点规模的时候就会体现出严重的后果。

不过将数据库用于异构系统之间的数据通信倒是常事。

再就是别把数据库当文件系统,有意义的数据可以存入数据库,但是像用户的头像和上传的其他数据可就别往这东西里面放了。

SendPkt快速入门

Wednesday, October 3rd, 2007

SendPkt快速入门
作者:
gashero
电邮:
harry.python@gmail.com
原文地址:

http://gashero.yeax.com/?p=26

项目主页:

http://sendpkt.googlecode.com

日期:
2007-10-03
版本:
sendpkt 0.2
修订版:
1
目录
1 简介
2 使用SendPkt的流程
3 SendPkt发送数据包的简单例子
4 SendPkt发送ARP查询包的例子
5 参考
1 简介
摘自 SendPkt 项目wiki。
SendPkt是一套同时可以运行在Linux/Win32的发送链路层数据包的Python扩展库。
SendPkt在Linux和Win32下实现方式是不同的,但是提供对Python的相同接口,确保Python程序可以不经过修改就可以移植。
在Win32下SendPkt使用WinPcap?中的 pcap_sendpacket() 函数发送数据包。
在Linux下SendPkt使用libnet的 libnet_adv_write_link() 函数发送数据包。
同时SendPkt还提供了查找网络接口设备的功能,以供发送数据包时选择。在Linux下虽然可以使用libnet自动选择的网络接口设备,但是在Win32下,默认的网络设备总会导致发包失败。所以为了可移植性,你必须在发送数据包时指定使用哪个网络设备接口。
2 使用SendPkt的流程
选择合适的网络接口以发送数据包,因为Win32于Linux的不同,需要专门选择。
对于Win32,通过libpcap取得的第一个网络设备接口(网卡)是一个虚拟网卡,无法发送数据包,而一般选择第二个网口,可以通过os.name判断操作系统并选择合适的网口。
得到的网络设备接口列表,是一个字符串列表,最终提供给发送函数的网口标志也是字符串。
构造数据包。推荐使用dpkt。
dpkt是python界通用的数据包组包和解包库,在分层构造数据包时从高层(应用层)向底层依次构造数据包。并且把高层数据包的数据作为下一层数据包的data字段来使用。
对于高性能应用,手工构造数据包也未尝不可,因为哪种方式都需要对数据包格式的深入了解。
发送数据包,使用 sendpkt.sendpacket() 。
例子见下面的代码。调用方式为 sendpkt.sendpacket(packet,device) 。第一个参数为数据包,为字符串对象,以太网帧。第二个参数为网络设备名,通过第一步获得。
3 SendPkt发送数据包的简单例子
参见SendPkt项目的单元测试 test.py 。:
import sendpkt
#构造一个需要发送的数据包
packet=”a”*80
#查找所有可用网络设备接口
devlist=sendpkt.findalldevs()
#根据操作系统选择合适的网络设备
if devlist:
if os.name==”nt”:
device=devlist[-1] #\\NPF\\….
elif os.name==”posix”:
device=devlist[0] #eth0
else:
device=devlist[0]
else:
device=””
raise OSError,”You must run in root mode”
#发送数据包
sendpkt.sendpacket(packet,device)

4 SendPkt发送ARP查询包的例子
ARP查询包的op字段为1,表示查询一个IP地址对应的MAC地址。如下是查询过程。:
#! /usr/bin/env python
# -*- coding: UTF-8 -*-
# File: arpquery.py
# Date: 2007-09-21
# Author: gashero
# Copyright@1999-2007, Harry Gashero Liu.

“””
ARP查询的测试
“””

from socket import inet_aton,inet_ntoa

import dpkt
import sendpkt

LOCALMAC=”111111″
LOCALIP=inet_aton(“192.168.1.104″)
REMOTEMAC=””
REMOTEIP=inet_aton(“192.168.1.1″)

def main():
global LOCALMAC
global LOCALIP
device=sendpkt.findalldevs()[0] #for linux is eth0
arp=dpkt.arp.ARP(
hrd=1,
pro=2048,
hln=6,
pln=4,
op=1,
sha=LOCALMAC,
spa=LOCALIP,
tha=”\x00″*6,
tpa=inet_aton(“192.168.1.1″)
)
ether=dpkt.ethernet.Ethernet(
dst=”\xff”*6,
src=LOCALMAC,
type=0x0806,
data=arp
)
#print ether
#print repr(dpkt.ethernet.Ethernet(str(ether)))
sendpkt.sendpacket(str(ether),device)
return

if __name__==”__main__”:
main()

5 参考
详细问题请发邮件到 harry.python@gmail.com 询问。