上一篇我们聊了操作系统为高效利用网络IO,提供的几种解决方案。今天,我们继续聊一下Web和应用服务器的设计。
先说一下Web服务器和应用服务器啥区别。早期互联网时代,信息大部分就是静态的Html,这时候对服务器来说,最大的问题就是解决大量网络请求的接入。后期随着互联网深入到我们生活的方方面面,信息已经开始动态化和个性化,这需要我们经过业务逻辑的处理后再返回动态信息。
业务处理逻辑和网络处理逻辑怎么整合到一起呢。显然,我们是想让服务器能作为中间件独立出来的,这就需要为业务逻辑模块抽象出一个统一的接口,这就是CGI(通用网关接口)的由来。
业务代码只要实现CGI的要求,就可以被Web服务器掉起。业务代码是逻辑比较复杂的,这也催生了各种开发语言的诞生,从C到php、.Net、Java,其实本质上是提供了更多的数据结构和算法支撑,提供更深的抽象层次。让业务代码编写和扩展更容易。
有了CGI,我们再具体看Web服务器怎么接入这些不同的语言代码。像php/.Net/Java这些语言都需要一些解析器或者运行时来支撑,最简单的方式就是将这个运行时作为一个module直接和Web服务器编译在一起。但是这样也有问题,如果业务代码出错,可能整个Web服务器也会出问题,影响其他业务请求的处理。
再有一种方式,就是把解析器或者运行时作为一个程序单独运行,当Web服务器有请求到达的时候,直接调起解析器进程,把请求参数传递过去。但是这样做也有问题,想象下有1000个并发的话,就需要1000个进程来处理,能同时处理的并发不会太高。另外,每次请求都需要启动一个进程,性能也会有问题。
我们在第二种方式基础上再优化一下,每次请求完成后,该进程不退出,继续运行着,当有新的请求来的时候,继续处理。这样就避免了每次开启进程的开销。
我们可以看到,这几种方式都是直接启动进程来处理的,优点是,每次请求是独立的,如果出现了故障,也不会影响整个服务器;缺点是启动进程的代价有点大,性能不高,而且高并发支持并不好。我们可以用线程这种方式来解决,进程和线程互相配合,每个进程有一个线程池并行处理,当请求过多的时候,再启动新的进程。这就是FastCGI的方式。
说到这里,我们看到Web服务器已经不仅仅只处理网络请求了,也有了处理复杂业务的能力。随着Java等技术的发展,逐渐出现了Tomcat等JavaEE的解决方案,他把Web请求和复杂业务的处理能力结合在了一起,提供了完整的解决方案。这种服务器我们叫做应用服务器。
其实,无论是Web服务器,还是应用服务器,还是单独的网络解决方案包,解决的问题一样,方案也有类似的地方。单独的服务器代码比较复杂,大家可以去看看网络包的代码,如ACE、Java的Netty等等,可以继续深入研究。
总结一下,互联网从只提供静态信息,到提供个性化、动态化的信息和服务,极大的改变了我们生活的方方面面。技术永远为问题而生,这也催生了Web服务器逐渐发展成为应用服务器。