互联网今天已经广泛存在于人们的生活中,人们的衣食住行等方方面面早已离不开互联网的支撑,这其中离不开Web技术的发展。
Web是一种典型的分布式应用架构。Web应用中的每一次信息交换都要涉及到客户端和服务端两个层面。因此,Web开发技术大体上也可以被分为客户端技术和服务端技术两大类。
本文将会讲述Web服务端技术的萌芽和演进过程,旨在使读者能更清晰地掌握Web服务端技术的发展脉络。
Web服务端技术的发展与客户端技术的进步是相辅相成的,本文虽是讨论Web服务端,在讲述过程中却不可避免地会提及一些Web客户端的有关内容,但不会过多深入。对此感兴趣的读者,可以自行阅读最下方的参考链接。
同样的,不谈具体代码,只聊历史故事。
广义上的WebServer(Web服务器),包含硬件和软件两方面。今天我们只谈及其中的软件部分,即能向客户端提供Web服务的程序。
现在大家耳熟能详的Apache、IIS、Tomcat、Nginx等等,都属于WebServer。
让我们先带着这些疑问,回到HTTP协议尚未诞生的时代。
1960年,TheodorHolmNelson在哈佛计算机编程的选修课程上,使用了当时哈佛大学唯一可用的计算机——IBM7090。在临近课程结束的时候,TheodorHolmNelson决定使用机器语言编写一个计算机程序,让他能够将自己的笔记和手稿存储在计算机中,可以以各种方式修改和编辑草稿,并生成可打印的最终版本。在他的项目进行到第4万行左右的代码时,他开始意识到,他对这项任务的完成难度最初估计得过于乐观。
1963年,已经从哈佛大学毕业的TheodorHolmNelson决定将自己大学时的想法继续进行下去。他首次提出了名为“HyperText”(超文本)的概念,并找到了一些志同道合、痴迷计算机的朋友,成立了ProjectXanadu,试图制订规范,并应用到实际的计算机程序中。
制订SGML的基本思想是把文档的内容与样式分开。在SGML中,标记分两种:一种用来描述文档显示的样式,称为程序标记;另一种用来描述文档中语句的用途,称为描述标记。一个SGML文件通常分三个层次:结构、内容和样式。结构为组织文档的元素提供框架,内容是信息本身,样式控制内容的显示。
不过,由于GML/SGML过于庞大且复杂,虽然有不少大公司在内部使用,但始终没能得到广泛的应用。
暂且按下TheodorHolmNelson和CharlesF.Goldfarb这边不表,让我们来到1989年。
此时已是不惑之年的TimBerners-Lee,负责在CERN(欧洲粒子物理实验室)做IT支持工作。由于粒子物理在当时是前沿研究领域,需要全世界的物理学家合作参与,那么如何与世界各地的物理研究所保持通信,就是一件十分重要也棘手的事情。
起初,CERN使用传真机来传输文件,但物理传输速度极慢,且会耗费大量纸张与油墨,对于信息检索工作而言也十分不便。
后来,因ARPANET网络在美国军方和多所大学内成功使用,CERN也开始采用这种使用计算机网络进行通信的方式来传输数据。
这一年年底,TimBerners-Lee向其上级提出了一项名为InformationManagement:AProposal(《关于信息化管理的建议》)的提议:使来自世界各地的远程站点的研究人员能够组织和汇集信息,在个人计算机上访问大量的科研文献,并建议在文档中链接其他文档。
在参考了TheodorHolmNelson有关超文本的规范、并参考了CharlesF.Goldfarb的GML/SGML实现后,TimBerners-Lee于1990年发明了HypertextMarkupLanguage(简称为HTML,超文本标记语言)和HypertextTransferProtocol(简称为HTTP,超文本传输协议)。
1990年,TimBerners-Lee创建了一款最原始的GUI的HTML浏览器(同时也是编辑器),和第一个HTTP服务器。
1991年,TimBerners-Lee作为布道者开始广泛推广Web的理念,提出了WorldWideWeb(万维网)的概念。
值得一提的是,在这一时期,Web领域还有其他诸如NNTP、Gopher等传输协议。但它们都因为种种原因,没能像HTTP一样流行起来,最终消失在了历史长河之中。
1993年,NCSA(美国国家超算应用中心)对此表现出了浓厚的兴趣,并开发了名为Mosaic的浏览器,于当年4月发布。
1994年,第一届国际万维网大会于4月在瑞士日内瓦召开,WorldWideWebConsortium(简称为W3C,万维网联盟)组织正式成立,并从IETF(互联网工程任务组)接管了HTML的标准制订工作。
同年6月,IETF非正式地指出了之前在“民间”流传的URL(统一资源定位符)与URN(统一资源名称)这两种叫法的存在,并进一步地定义了一个名为UniformResourceIdentifier(简称为URI,统一资源标识符)的规范文法。
同年10月,CERN的另一位IT员工HkonWiumLie吸收了当时已有的一些Web样式的实践经验,提出并发明了CascadingStyleSheets(简称为CSS,层叠样式表)。
同年11月,Mosaic浏览器的开发人员创立了Netscape(网景)公司,并发布了MosaicNetscape浏览器1.0版本,后改名为NetscapeNavigator(网景导航者)。
1995年,W3C制订了HTML2.0标准。
同年5月,Netscape公司的工程师BrendanEich发明了一门名为LiveScript的脚本语言,可用在Web服务器和浏览器。在之后与NetscapeNavigator2.0一同发布时,被改名为JavaScript。
同年8月,Microsoft(微软)旗下的InternetExplorer(简称为IE)1.0版本正式发布。
1996年,IETF将HTTP列为互联网标准之一,并制订了HTTP/1.0协议标准。
同年12月,W3C将CSS纳入工作范围,并在随后个几个月里制订了CSS1标准。
1997-1999年,HTML3.0、HTTP/1.1、HTML4.0、CSS2、ECMAScript3等标准先后被发布,并统治了今后二十余年的互联网。
终于,Web时代降临。
HTTPd,即HTTPdaemon的缩写。
今天我们谈到这个名词,大部分人会把它认为是Apache的代名词。但这其实只是个误解(原因下文会提到)。
在类Unix的操作系统中,一个在后端周期性地执行某种任务或等待处理理某些事件的进程,往往被称为“DaemonProcess”(守护/幽灵进程)。HTTPd即取此意,意思就是在后台处理HTTP请求的程序。
因此实际上来说,HTTPd应该是近似等同于WebServer。在HTTP协议尚未出现的时代,WebServer一般指FTP服务器。但HTTP协议出现后,WebServer就立刻变成了指代HTTP服务器。今天的WebServer一定会、但不仅仅只会支持HTTP协议及其衍生协议,还可能支持诸如FTP、SMTP、MQTT甚至是更底层的TCP、UDP协议。
1990年年底,TimBerners-Lee在一台运行着NeXTSTEP系统的NeXTComputer上编写了首个HTTPd程序,起名为CommonLibrary。这是一个由C语言编写的组件,只能处理HTTP请求中的GET谓词,并不是一个独立且完整的程序。因其属于CERN项目的一部分,所以也被称为CERNHTTPd。
1993年,TimBerners-Lee将CommonLibrary从CERN项目中独立出来,更名为libwww并开源。
同年,NCSA在此基础之上扩展并开发出了NCSAHTTPd。
1994年,libwww的开发维护工作转交给了W3C,在此阶段,libwww新增了很多特性,诸如兼容HTTP/1.0、支持CGI、虚拟主机等。此时它也被称为W3CHTTPd。
1996年,W3C的工作重心已经不在libwww上,迟迟没有新版本发布,并最终于2003年宣告项目中止。
libwww提供了基础的HTTP协议的解析与包装方式,既可用于服务端,也可用于服务端,被广泛地使用在包括Mosaic、Lynx、Arena、MacWWW在内的诸多早期Web程序中。
上一小节提到,1996年时W3C的工作重心已经不在libwww上,因为他们已经另有其他重点工作。
由于libwww只能被编译到类Unix的操作系统中,且只支持静态网页。随着Web技术的不断发展,以及Windows系统的广泛流行,W3C亟需一种可以跨平台的的Web服务器。因此,W3C将目光放在了横空出世、发展迅猛的一种跨平台编程语言——Java。
W3C联合当时的拥有Java的Sun(升阳)公司,开发了一个名为Jigsaw的程序。
它由Java编写,起初只作为JDK1.2版本的一个模块发布,意图让开发者能快速搭建一个跨平台Web服务器项目。它采用了多线程的处理方式,兼容HTTP/1.1,支持SSL/TLS、WebDAV等新特性,同时也是首个支持Servlet和JSP的服务器程序。由于Java的跨平台特性,它可以运行在BeOS、AS-400、AIX、Unix、Solaris2、OS/2、MacOS、Linux、Windows95、WindowsNT等操作系统上。
虽然Jigsaw命运早夭,但因为它是第一个由Java编写的WebServer,起到了很多纲领性的指导作用,为后续Java技术在Web领域的扩展打下了坚实的基础。
值得一提的是,JDK9中新引入了与Jigsaw同名的模块化方案,但与JigsawHTTPd并没有什么关联。
最早的Web服务器只是简单地响应浏览器发来的HTTP请求,并将存储在服务器上的HTML文件返回给浏览器。可以近似理解为拥有文档预览功能的FTP。文档内容在文件未修改前就是不变的,所有访问Web的用户看到的内容都是相同的。
这也就是前文提到的所谓的“静态网页”,这显然满足不了人们对信息丰富性和多样性的强烈需求。
由此,Web技术的发展出现了两条分支路线。一条是尝试向客户端、即浏览器引入动态交互,例如Sun公司的JavaApplet、Netscape公司的JavaScript、Microsoft公司的JScript、VBScript、Adobe公司的Flash、ActionScript等等。另一条是试图从服务端、即WebServer入手,想在返回给客户端时就输出动态的内容。这两条路线都在未来有了十分迅猛的发展,我们今天按下客户端不表,只谈服务端这面。
1991年,NCSA首次提出了ServerSideIncludes(简称为SSI,服务端嵌入)的概念,并在之后发布的NCSAHTTPd中实现这一技术。
1993年,在NCSA发布NCSAHTTPd的同时,NCSA又提出了CommonGatewayInterface(简称为CGI,通用网关接口)这一概念,并在未来几年内先后制订了CGI1.0、CGI1.1、CGI1.2等标准。
CGI本质上来说,就是接受一个输入、并返回一个输出的程序。CGI独立于WebServer存在,在收到特定请求后(这些请求通常以/cgi-bin/路径开头),WebServer将HTTP请求的内容作为参数传递给CGI程序,并将CGI程序的返回值作为HTTP响应返回给客户端。所以CGI程序不能独立运行,需要配合WebServer才能工作。
早期通常是在WebServer接受到一个请求后,开启一个新的进程来执行CGI程序,这种方式在请求量稍微大一些时,就会严重拖累服务器的性能。
所以,随后又诞生了FastCGI(简称为FCGI)技术。简单来说,就是一个常驻内存的进程池技术,可以复用进程,使得CGI的工作负载性能大大提升。
在今天,由于CGI编写的复杂难度过大,已经很少有人再直接应用这种技术(间接的还有很多)。但它的出现,给其他编程语言带来了启发,诸如FCGI、SCGI、WSGI、Servlet乃至后来的动态脚本语言等技术不断涌现,它们都滥觞于CGI。
1995年,在随着NCSAHTTPd1.3版本的发布,NCSA就逐渐放缓了对NCSAHTTPd版本的开发工作。但为了满足日益丰富的Web服务端技术的需要,NCSAHTTPd的社区成员在BrianBehlendorf的领导下,决定在NCSAHTTPd1.3版本的基础上创建一个新的分支,并取名为Apache。
为什么取名为Apache?其中一个流传最广的解释是,Apache是在NCSAHTTPd的基础上修改的,因此是一个“修补过的”(apatchy)WebServer。
但后来在Apache的2.0版本里,Apache社区已将NCSAHTTPd的源代码全部移除,二者在今天已经没有了直接关系。
Apache在前人的基础上,支持了很多新的Web特性。例如:多种身份认证方案、支持CGI、支持SSL/TLS、支持IPv6、反向代理、URL重写、定制化日志文件等等。与此同时,在其2.0版本中还加入了对非Unix操作系统的跨平台支持。
Apache的设计理念,影响了很多后来的WebServer,是开源世界和Web历史中不能不提的一环。
值得一提的是,Apache社区在1999年成立了ApacheSoftwareFoundation(Apache软件基金会)组织,致力于支持开源软件事业。我们今天谈及Apache,即指的是最初的ApacheHTTPServer,也指Apache软件基金会。
正如前文提到的那样,虽然被称为ApacheHTTPServer,但它不仅仅支持HTTP协议及其衍生协议,还可以通过插件的形式支持其他协议。
1995年5月,在令世界为之疯狂的Windows95上市的前三个月,WindowsNT3.51发布,这是WindowsNT3.X系列中的最后一个版本,也是第一个支持全中文的Windows操作系统。
随着这一版本的发布,一个名为InternetInformationServices(简称为IIS,互联网信息服务)的系统可选组件悄然到来。
由于IIS是在Windows操作系统平台下开发的,这也限制了它只能在Windows下运行,但它是首个支持以GUI方式配置的WebServer。
与Apache一样,IIS也支持HTTP协议及其衍生协议、FTP协议、SMTP协议等。
随着Windows的流行,IIS也不断进行版本迭代,它曾一度接近Apache的市场份额,现在也是主流的WebServer之一。
CGI程序一般由C、C++、Pascal等语言编写,并在目标平台上编译成二进制可执行程序,这给开发维护工作带来了很多麻烦。
为了简化CGI程序的修改、编译和发布流程,人们开始探寻用无需编译的脚本语言来实现CGI应用的道路。
1994年,丹麦裔加拿大人RasmusLerdorf用Perl编写了一个简单的程序,用于统计他的个人主页的访问者。后来,RasmusLerdorf用C语言重新编写了一遍这个程序,并在1995年以PersonalHomePageTools(简称为PHPTools,个人主页工具)的名义开源了PHP1.0版本。
在这早期的版本中,提供了访客留言本、访客计数器等简单的功能。以后越来越多的网站使用了PHP,并且强烈要求增加如循环语句、数组变量等新特性,在新的社区成员加入开发行列后,1995年,PHP2.0版本顺利发布。在这个版本中,PHP添加了对MySQL数据库的支持,从此建立了其在动态网页开发上的地位。
PHP最早使用CGI的工作方式(即php-cgi),后因为这种方式的性能损耗很大,所以又开发了基于FastCGI的版本(即php-fpm,PHPFastCGIProcessManager的缩写)。
但与早期CGI不同的是,PHP首次将HTML代码和PHP指令合成为完整的服务端文档,Web应用的开发者可以用一种更加简便、快捷的方式实现动态Web网页功能。
1996年,Microsoft公司在借鉴了PHP的思想后,在其产品IIS3.0版本中引入了名为ActiveServerPages(简称为ASP,动态服务器网页)的技术。
ASP使用的脚本语言是JScript和VBScript。借助MicrosotOfficeFrontPage、MicrosoftVisualStudio等开发工具在市场上的成功,ASP迅速成为了Windows系统下Web服务端的主流开发技术。
需要说明的,Microsoft在之后的.NETFramework和.NETCore体系中,还分别引入的名为ASP.NET和ASP.NETCore的技术。如果说后两者还师出同门,只不过一个只在Windows上运行、一个能跨平台运行;而ASP则和后两者只有名字上得到了传承,实际上已经没什么关系了。
当然,以Sun公司为首的Java阵营也不会示弱。1997年,Servlet技术问世。1998年,JavaServerPages(简称为JSP,Java服务器页面)技术诞生。
其中Servlet类似于CGI/FastCGI处理;JSP则类似于PHP的HTML模版。前者对于拼接HTML不是很擅长,后者对于运算和逻辑写起来又很繁琐,那么有没有可以把二者优势相结合的办法呢?
答案是肯定的,这也就是著名的MVC(Model-View-Controller)架构。虽然MVC架构早在1978年就在Smalltalk上提出,在GUI领域上也有Microsoft推出的MicrosoftFoundationClasses(简称为MFC,微软基础类库)丰富实践,但这还是首次在Web领域得到应用。
这种Servlet+JSP组合的方式,后来也反过来影响了之前出现的PHP和ASP,二者最终在后续版本中引入了类似的功能。
至此,扩展到Web领域的语言(如Perl、Python),以及专为Web而生的语言(如PHP、ASP、JSP),这些主流的脚本语言已全部出现,它们最终引领了Web下一个时代的前进方向。
上文提到,无论Apache也好、IIS也罢,本身并不直接生成动态页面,而是需要以CGI/FastCGI的方式将HTTP请求转发给相应的处理程序,才能返回动态页面。
PHP、ASP、JSP等脚本语言的出现,虽然已经不需要Web开发人员手工编写复杂的CGI/FastCGI程序来解析、封装HTTP报文,而是专注于业务逻辑本身即可。但这种方式其实质还是WebServer+CGI/FastCGI二者独立运行的方式。
那么有没有直接能生成动态HTML内容、无需CGI/FastCGI配合的WebServer呢?
1999年,Tomcat应运而生。Tomcat既是HTTPWebServer,也是Java执行容器,它由CatalinaServlet容器、Coyote连接器、JasperJSP引擎等组件组成,可以直接输出动态HTML文档。
由于Tomcat也是Apache软件基金会的顶级项目之一,所以也被称为ApacheTomcat。
早期的Tomcat由于性能不佳(尤其是针对纯静态内容),通常还是要与ApacheHTTPServer或者其他WebServer一起工作,除了用于开发过程中的调试以及那些对速度要求很低的开发者,很少会将Tomcat单独作为WebServer。
这也给很多人造成了误解,以为Tomcat是那些基于CGI/FastCGI技术的脚本语言类似,是专门运行Servlet、JSP的程序。其实这也是一种误解,无论是Servelet还是JSP,它们都比Tomcat面世的要早;而Tomcat完全可以脱离ApacheHTTPServer独立运行,充当WebServer。
但随着Tomcat版本的不断迭代,以及WebServer集群技术的广泛使用,正有越来越多的开发者将其单独作为WebServer。
为了和早期那种只支持静态网页的WebServer加以区分,我们把这类WebServer也称之为ApplicationServer,即应用服务器。
Tomcat这种WebServer+执行容器的双重身份的方式,后来也有越来越多的Java开源产品采用,诸如Jetty、Netty、Underow等等。
值得一提的是,2014年,Microsoft发布了ASP.NETvNext首个预览版,也就是后来的ASP.NETCore,从这一版本开始,Microsoft也实现了类似的产品,名为KestrelServer。
网络通信,本质上就是对网卡或网络虚拟设备进行I/O操作。
早期的操作系统,基本都是阻塞I/O(即BIO),这种方式在面对大量并发时,会显得力不从心。上文提到的各种WebServer都是基于这种实现方式。
在这一时期,很多WebServer都会遇到著名的“C10K”问题,即:当请求的并发数量达到一万后,WebServer的性能会随之急剧下降。
2000年,libevent问世。这是一个由C语言编写的、轻量级的开源高性能事件驱动编程库。起初它只兼容类Unix操作系统,在其他系统上性能并不高,后来在社区的推动下才慢慢支持Windows等操作系统的IOCP模型。不过因为它历史悠久,社区活跃,很多出生较早的项目基本都会选择它作为网络编程库。
目前使用libevent的知名项目有:Memcached、GoogleChrome、ntpd、Tor(洋葱路由)等等。
2007年,为解决libevent多线程全局变量不安全、组件质量参差不齐等问题,MarcLehmann决定精简libevent,去掉多余的组件(如HTTP和DNS),只专注于事件驱动,并最终形成了libev。可以理解为libev是libevent的一个分支版本。目前这一分支作者已停止维护,而且libevent与libuv却在社区推动下飞速发展,所以最后很多项目都不再使用libev。
目前libev使用它的知名项目有ShdwScks(河蟹拼法)、Node.js早期版本。
2011年,在使用了libev作为内置WebServer仅仅两年后,Node.js社区意识到了一些问题。一是前面提到项目维护问题;二是因为Node.js的日益流行,迫切需要跨平台支持。因此,由Node.js之父RyanDahl主导的libuv诞生。它也是由C语言编写,提供对基于事件循环的异步I/O的跨平台支持。最终,在Node.js0.9版本中,libuv完全取代了libev。
目前使用libuv的知名项目有:Node.js、ASP.NETCore、CMake、Julia等等。
事件驱动编程的流行,给WebServer开发带来来新的活力,很多编程语言都加入了对它们的封装引用,可以很方便、快捷地搭建出一个简单的WebServer。但通常来说,都是用于快速搭建开发测试环境,目前还有没有一款基于此的、独立的WebServer产品出现。
2004年,俄罗斯人IgorVladimirovichSysoev在经过了两年的开发后,发布了名为Nginx的WebServer。
Nginx是EngineX的缩写,即“超级引擎”之意。在设计之初,Nginx就被赋予了一个明确的目标:全面超越ApacheHTTPServer的性能。
Nginx同时支持NIO、AIO两种I/O模型,在能支持大量并发连接的情况下,降低了内存占用,并提高了系统稳定性,完美地解决了C10K问题。
虽然Nginx在Windows系统上不如Apache表现稳定,更遑论Microsoft的亲儿子IIS了。但它的可扩展性和高性能,仍然吸引着大量开发者使用。
不过随着云平台的兴起,Nginx又成为了很多云厂商的首选。例如:
截止目前为止,Nginx已占据了36%以上的WebServer市场份额,正逐渐蚕食着Apache与IIS的市场份额。
2011年,在从RedHad(红帽)公司独立出来并开源后,Netty,这个脱胎于JBoss的项目,在被RedHat收购之后,才终于迎来了它的高速发展期。
由于诞生日期很晚,在吸收了早期其他WebServer的经验教训后,Netty直接采用了NIO的I/O模型,实现了其更高的并发性能。
和Tomcat一样,Netty也是一个Java实现的WebServer。这里要指出的是,后来Tomcat也支持了NIO,还新引入了APR技术,所以目前Netty带来的性能优势已经不是很明显。
但与Tomcat是支持七层的HTTP等协议不同的是,而Netty是从四层开始支持TCP、UDP等协议,除了充作HTTPWebServer外,还可以实现自己的高性能私有协议(如RPC协议)WebServer。
本文着重介绍了早期的、和一些现阶段流行的WebServer。
实际上,WebServer领域曾经有无数的优秀作用,也正兴起着更多的、功能更强大的产品。
其实无论是WebServer,还是可用作动态网页的编程语言,都远不止提到的这些,但其他的都没有这些流行,这里也就不费笔墨。