Category: 技术


ob_gzhandler是可以获得content-length的

之前以为PHP的ob函数无法在gzip的时候获得内容长度,简单地改了Twip的代码,使之不报告Content-Length。今天偶然搜索Gravity的相关内容,看到@empyreaner君提供的获取Content-Length方法:
Ob_Start();
Ob_Start(‘ob_gzhandler’);
echo $content;
Ob_End_Flush();
header(“Content-Length: “.ob_get_length());
Ob_End_Flush();

此法甚为巧妙,PHP手册也有一句:Output buffers are stackable, that is, you may call ob_start() while another ob_start() is active.只恨自己阅读太不仔细,遂写此文以志之。

网络维护日

明天是一年一度的网络维护日,主机将进行维护,暂停除rek.tw外的全部服务。维护时间一天,后天将恢复服务。届时一批软件将升级到新版本,为了应对OAuth时代的来临。
最近开始尝试研究网络服务器软件的编写,参考了书上的几个网络服务器例子,然后又了解了Apache和lighttpd的设计架构,整理总结了几种常见的服务器设计思路把它们记录下来。网络服务器设计的关键在于解决并发问题,因为服务器都是对大量不同客户端执行相同的业务逻辑。优秀的并发设计能够提高服务器的请求承载量,保障数据安全,提供崩溃恢复能力,让服务器宕机损失最小化。

单客户端的服务器流程

首先考虑只有一个客户端请求服务的情况,服务器需要先开启一个Socket端口监听连接请求,然后接受一个客户连接,接着读取网络数据处理并发回结果,最后还要关闭网络连接。用传统的Socket函数描述,整个网络服务过程经历了bind、listen、accept、receive、send、close几个步骤,其中receive和send经历多次循环。由于receive、send等函数都是阻塞式的,没有完成接收(发送)就会等待而不往下执行,所以无法实现同时向多个客户端提供服务。
单一客户端循环

单一客户端循环

fork方式实现并行

既然单个客户端会在收发数据的时候阻塞整个程序的执行,一个简单的实现并行处理的方法很容易想到——创建多个进程(或者线程),分别服务不同的客户端。这就是fork方式的基本思路:主进程被创建之后,开启和监听端口,每每有请求的连接建立,都fork一个子进程来负责处理,主进程可以继续等待下一个客户端请求。对于每个被创建出来的子进程,都可以独立地接收和发送数据,执行服务器逻辑,完成操作关闭连接之后,子进程被销毁,资源被操作系统回收。
通过fork实现多客户端

通过fork实现多客户端

用一个比喻阐述fork方式的服务器就是:(主进程)只负责接受生产订单,然后雇人(fork子进程)执行生产任务(服务器逻辑),交付产品之后将其解雇(销毁子进程)。
fork方式是最简单的网络服务器实现方式,它有着显著的缺点:fork模式大量地创建和销毁系统进程会造成巨大的系统开销。操作系统在创建进程的时候需要分配资源,复制父进程内存数据,而处理完毕后销毁进程时又需要回收内存释放资源资源,再加上大量进程存在时,系统的进程调度也会造成不小的额外开销。当然它也有显著的优点:所有客户端服务进程相互隔离互不干扰,一个进程的意外崩溃不会影响到其他进程的正常工作,对于像Web服务这样要求高度稳定、互不干扰的情况十分适合。
著名的Apache服务器至今仍在使用这种方式实现服务,就是在Apache中被称作prefork的工作方式。当然,实际用于生产环境的服务器稍微复杂一些,Apache的prefork还实现了初始进程数量控制,延缓销毁进程等技术以提升并发访问承载力。

worker方式

fork方式中进程提供的服务是一次性的,和客户端连接断开后就会销毁,这样整个服务器会同时有大量的进程需要创建和销毁。是否可以回收完成服务的进程,并让他们对新的客户连接提供服务呢?这就是worker方式。统计数据证明worker方式是相当值得使用的:一般工作的网络服务器,并发请求中大约有60%到80%是处于连接发起和等待响应状态,只有很少的部分是已经建立连接,正在服务的;而且并发访问量在短时间内是相对稳定的,很难想象服务器会遇到这一秒连接数量50,下一秒连接数量5000的情况,据此可以认为,只要有少量长期提供服务的进程或者线程就足以应对大量的并发访问。
worker方式首先创建一个独立的worker管理进程来负责创建和管理worker进程,称作worker管理器。然后主进程监听和接受请求,并把任务交给worker管理器负责。worker管理器接到任务之后,会在它维护的多个worker进程中找到一个空闲的来处理这个任务。worker进程负责具体的数据收发,服务器逻辑的执行。完成任务关闭连接之后,worker进程不是被销毁,而是通知worker管理器已经完成工作,处于空闲状态。worker管理器会考虑将下一个任务交给它。
多客户端worker方式

多客户端worker方式

用比喻阐述worker方式的服务器就是:(主进程)只负责接受产品订单,请一个管家(worker管理器)负责打理生产事务,管家会长期雇佣若干工人(worker进程),你将生产任务交给管家,管家就找一个没事干的工人生产(执行服务器逻辑),工人完成任务后不会被解雇(销毁进程),管家会被下一个生产任务交给他。
worker方式实现服务器较之fork方式有不小的改进,虽然设计起来更加复杂,但和带来的收效相比是值得的。难点主要在于worker管理器的设计,为了有效的应对访问压力的变化,worker管理器需要能动态地创建和销毁worker进程,以便在访问高峰时提供更多的服务进程,访问低谷时占用更少的系统资源,并且还要能够发现并销毁出现错误僵死的worker。
worker方式的优点显而易见,缺点倒是也有不少。worker的软肋在于worker管理器进程,假如这个进程出现错误崩溃,那么整个服务器就宕机了。Apache从2.0版开始提供worker模式供选择,作为生产服务器,Apache同时使用多线程和多进程结合的方式。以worker模式启动之后,Apache会启动多个worker管理器进程,每个worker管理器会创建多个worker线程来负责具体请求。这样的设计一方面能利用更轻量级的线程机制降低worker的系统开销,方便worker管理器设计,另一方面通过增加worker管理器的冗余,提升抗风险能力——即使一个worker管理器崩溃,服务依然不会中断。

event方式

如果多个客户端交互信息量很大的话,服务器逻辑不得不大量使用同步锁定和进程间通信机制,大大增加了额外的系统开销,也造成许多死锁的风险。lighttpd服务器另辟蹊径,化整为零,利用Linux系统事件通知和异步网络IO操作的方式实现一个单线程的Web服务器。其具体实现和利弊将在下一篇文章中具体介绍和讨论。

Ubuntu 10.04来了

Ubuntu9.10带来了“云计算”,还把SCIM和Pidgin都改成了非默认组件,使用还没怎么习惯,现在Ubuntu10.04又来了。10.04是一个LTS版本,支持从8.04或者9.10直接升级。
和9.10相比,新的10.04版拥有许多新特性,首先一个就是升级了Gnome,使用最新版Gnome的Ubuntu桌面体验有不少改进。当然相信这对显示计算能力的要求也会大大增加。Firefox的默认搜索引擎由Google改成了Yahoo,看来Firefox和Chrome的浏览器竞争开始全面展开了。曾经有人说Google助力Mozilla是为了让Web标准化,然后自己发展Chrome是为了应用网络化,并推广Google的WebApp,现在看来是很有道理的。UbuntuOne新增MusicStore,看来云计算赚钱的日子也快来了。还有一堆BugFix和驱动升级也是每次新版本必然带有的。
和一切.04版本一样,10.04将会在今年4月底发布正式版,拭目以待。

让YoonoDesktop使用代理访问

今天下载了YoonoDesktop1.6来用,感觉还不错,就可惜Twitter、Facebook在墙外,Yoono.com的主机访问也很慢,让人难以忍受。然后启用VPN翻墙,体验了一把,各项功能都和Firefox插件版的相差无几,看来我可以放心的使用Chrome了。不过平时我多半用SSH+AutoProxy翻墙,因为VPN相对比较慢,尤其是访问国内的网站的时候,需要到台湾绕一圈才回来,太麻烦了。虽然可以用自定义路由表的方法让国内的IP直接连接,但国外的地址也并非全都需要翻墙,终究还是不如AutoProxy方便。
于是研究YoonoDesktop能否设置代理连接,结果根本没有找到相关的选项。看了Yoono的程序文件结构,确信是XUL Runner搭出来的无疑,于是Google找了好一会儿,都没能找到不通过界面,直接设置代理的方法。后来想到Firefox的配置文件应该可以拷过来用,grep搜了一下,果然找到~/.mozilla/firefox里有个prefs.js的文件,里面定义了代理设置。当下把有用的三行复制出来:

user_pref(『network.proxy.socks』, 『127.0.0.1″);
user_pref(『network.proxy.socks_port』, 7070);
user_pref(『network.proxy.type』, 1);

打开~/.yoono/yoono/zv1mj8ax.default/prefs.js文件,把上面三行粘贴进来。保存重启YoonoDesktop,代理生效,可以直接访问Twitter了。
Powered by WordPress. Theme: Motion by 85ideas.