本文并不适合高度依赖很复杂的现有框架之场景。

 

很多年来,web服务以及web服务器的性能一直提升的很有限。

最近无聊,思考这个事情并且做了一些小尝试,算是一个构想吧。

就语言而言,用的最多的大概是PHP,其次估计是java/.Net,这些需要预热、占资源很多还慢吞吞,于是人们前端整起各种缓存,各种什么负载均衡什么的。这也是无可奈何的事,因为要保持“动态网站”这个前提,所以很多请求都需要由web服务动态处理,而各种框架、库什么的搞起来,一个进程占内存几百M也就成了常态。这中间还贯穿着各种反向代理、负载均衡、各种cgi和类似协议、各种运行环境、各种虚拟机和类似产物等等等等。想起这么多东西,貌似不慢是没道理的……

我突发奇想,既然这些脚本类的语言诸多不痛快,那何不试试搞个exe程序来进行web请求的处理?

首先要把web服务器和web服务分开,总没必要自己搞个服务器程序吧,那就太扯了……

然后要把网站的前后台分开,后台慢点没什么的,因为不对外,负载也很低,于是需要处理的就是前台请求了。

而多数时候,前台的处理逻辑都是固定的,比如从数据库提取数据生成页面、生成列表这类,要么就是更新一些数据,比如点击量什么的。抽丝剥茧之后,我们会发现首先数据库是少不了的,然后无非就是根据数据来填充模板生成html,最后把内容发送出去。

既然逻辑很简单,那exe的优势应该能完全发挥。相比而言,exe的优势是性能足够高,在不考虑数据库的情况下,除了操作系统和部分系统级api,几乎可以视为直通硬件了,一般没什么与外部程序打交道的地方;然后资源回收很方便,用完的对象可以直接销毁;最重要的是,适合我这种喜欢搞控制台/桌面程序而不太擅长写web的人,呵呵。

做了个小实验,环境是这样的:

 

底层物理机配置很渣的,至少是10年前的低端配置。

然后在此物理机上开了两个虚拟机,一个Debian+Nginx+uwsgi+python+flask,一个windows+exe,同一IPV4的子网,中间通信走的应该是虚拟网桥,通信库用的zeromq

这里说明一下,nginx内嵌了lua,但我不熟悉这玩意,那个zmq的扩展库安装总是出问题,所以用python,这部分性能损失应该不少,不过作为测试,无妨了。

python收到请求以后,将请求转发给exe,然后exe返回内容,经过多次测试,在“发送前”到“接收后”这个流程的耗时在0.0015秒到0.002秒之间,因为这个过程产生了与其他节点的网络通信,这部分的性能损失应该也不少,再算上lua和python的性能差,我估计如果把exe做到相同机器,则这部分开销可以压缩到千分之一秒之内。

python代码如下

@app.route("/test")
def testzmq():
    context = zmq.Context()
    socket = context.socket(zmq.REQ)
    socket.connect("tcp://172.16.0.2:5555")
    time_start=time.time()
    socket.send_string(str(request.url))
    message = socket.recv()
    time_end=time.time()
    return ("收到 [ %s ] , 执行时间 [ %f 秒] , 请求方法[%s] , 地址[%s] , [%s]" % ( message,time_end - time_start,request.method,request.url,request.args))+"<br />\r\n"

exe(delphi/pascal)代码如下

//工作线程

procedure dbworker(lcontext: TZMQContext);
var
  receiver: TZMQSocket;
  s: Utf8String;
  t: tstringlist;
begin
  receiver := lContext.Socket(stRep);
  receiver.connect('inproc://'+rpcpoint);
  t := TStringList.Create;
  t.Delimiter := '|';
  while True do
  begin
    receiver.recv(s);
   writeln(s);
    receiver.send('ok');
    s := '';
    sleep(1);
  end;
  t.Free;
  receiver.Free;
end;

 

工作线程是连接到exe内部的stRouter与stDealer桥接结构,这中间依然有开销。

 

之所以做成这个架构,是考虑实际场景中,单个exe+多线程可能会有瓶颈,所以采用桥接结构,这样也构成了负载均衡结构。exe占用内存不到3m,编译后的程序大小104k.

 

写到这里,我又按照实际大小测了一下,本站首页字符数量是13.5万(gzip压缩之前的原始文本),我做了同样大小的文件发送,延时提升到千分之3.5秒以内。

 

我相信,在10年前的机器上开了两个虚拟机所产生的千分之3秒的延迟,放在正经使用的服务器上顶多千分之2秒吧,再加上python自身比lua还是差一些,所以我推测在正常环境下这个延迟必然可以压缩到千分之二秒以内,甚至千分之一秒。

再往后运行,我认为exe比php、python的性能提升绝不止这0.00x秒了。

 

基本上,我的设想算是得到证实了,实际干起来的话,当然还可以做必要的优化,比如exe可以直接gzip以后再传输,这样的话我估计效率会提升不少。

在我看来,性能提升只是一部分;更重要的是,当摆脱了web脚本语言这个框框以后,exe可以做的事情是很多的,遇到很特殊的事情也可以丢个线程出去忙活半年,而不再局限于web环境的诸多限制;最重要的是,让我这种web渣渣可以用自己喜欢的方式来干这个事了,哈哈。

 

=====================

继续实践中,lua的扩展库弄好了,也做了一个独立的负载均衡器,目前整个结构为web(nginx+lua,位于美中)---负载均衡(位于美西)---业务处理(exe,在我电脑上),实际效果比想象的好。因为这一圈下来延时在220ms左右,可实际上依然做到瞬间返回结果到浏览器,当然实际运行的时候不可能人为构成200多的延时,应该保持在0或者1,现在环境是为了调试方便,懒得不停的传服务器。

浏览的传输路径为:

浏览器(湖北)--美中web服务器--美西负载均衡--业务处理(湖北),处理完以后则原路返回。

今天想测试的都完成了,超出预期,后面抽空继续。

作者 听涛

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注