看问题要看到本质:从Web服务器说起

这是个很长的故事, 让我们从Web服务器来开始。

Web服务器是个挺简单的东西,工作很简单,在80端口上监听,解析客户端发过来的HTTP的请求, 然后把相对应的HTML文件、Image等返回给客户端就可以了。 像这样:

640?wx_fmt=gif

640?wx_fmt=png

这就是一个静态内容服务器,所谓静态内容,就是服务器端的内容如HTML不会变化,每次请求都是一样的。除非人们手工改了它。

实现这样一个“玩具Web服务器”并不难,只要能了解服务器端Socket编程就可以了, 主要工作是编程处理HTTP协议的细节。

动态内容

但是如果想再往前走一步,让Web服务器能产生动态内容,那就难了。

比如说来了一个HTTP请求,在其中携带者用户名和密码,要求你去数据库做一个查询,看看用户是否存在。

POST  /login

user=xxxx&pwd=xxx

这个静态的Web服务器就搞不定了,它根本,也不应该去查询数据库。

怎么办呢?你可以用某种语言(比如C语言)写个程序, 来查询数据库,假设这个程序的名字叫db-query。

可是你将面对非常棘手的问题:  Web服务器是个进程,db-query也是个进程,这俩货之间怎么通信呢?

(友情提示,下面内容略显枯燥,可跳过)

首先是参数的传递,一种办法是这样:对于每个动态请求,Web服务器进程创建一个db-query的子进程,然后通过环境变量把参数传递过去。

web服务器:

setenv("QUERY_STRING","user=xxxx&pwd=xxx")

db-query子进程 :

param = getenv("QUERY_STRING")。

下一个问题:db-query这个子进程获得了用户名和密码,查询了数据库,怎么把查询结果返回给浏览器?

有个很巧妙的办法!

每个程序都有所谓的标准输出(STDOUT),db-query只要调用printf这个函数,数据就会输出到STDOUT,我们就可以在黑乎乎的控制台上看到了数据输出了。

但是输出到控制台是万万不行的,我们得输出到socket才可以发回浏览器。

每个浏览器和服务器的连接都是一个Socket, 每个socket都有一个文件描述符fd, 如果把查询数据库程序db-query的STDOUT重定向到那个fd,会发生什么?

没错!db-query的所有输出都直接发送的客户端的socket了,Web服务器可以撒手不管了!

当然,如果浏览器要看到的是HTML页面, 那db-query这个程序就需要输出HTML了。

640?wx_fmt=png

640?wx_fmt=gif

这种方式就就是大名鼎鼎的CGI,当你看到网址中有cgi-bin字样的时候,很有可能就是用CGI实现的。  只要遵循CGI协议, 可以用任何语言来实现动态的网站。

这是人类迈出的一大步,有了这一步,才能在网上购物,办公,社交,聊天......  你才能看到我这篇文章(嗯,也许腾讯把微信公众号的文章都静态化了, 请了解详情的同学告知)

但是,CGI是非常复杂和笨拙的, 主要体现在:

第一,对每个请求,都得创建一个子进程去执行,这是个非常大的开销。

第二,对程序员来说,编程极为痛苦,要操作环境变量,还需要直接在编程语言中输出HTML!

640?wx_fmt=gif

640?wx_fmt=png

麻烦不麻烦,难受不难受,上个世纪的程序员苦逼不苦逼?

Servlet

怎么才能跳出苦海?必须得做到关注点的分离!

程序员的关注点是:拿到Http 请求中的数据,执行业务, 然后输出Http 响应。 别的什么环境变量,重定向,别来烦我!

那就简单了,让程序员写个类,里边是业务逻辑, 然后我们想办法构建一个HttpRequest对象和HttpResponse对象,传递给程序员的类让他使用不就行了?

谁来创建这个HttpRequest和Response 对象,  然后调用程序员写的类?

静态Web服务器表示我不愿意,我就想管好我这一亩三分地,把静态内容给大家服务好。

Tomcat已经迫不及待地要上场了,我来我来。码农朋友们,我送给你们一个规范,叫Servlet, 你们按照Servlet的规范来写程序,放到我这里运行,别的什么都不用管了。

640?wx_fmt=gif

640?wx_fmt=png

程序员很高兴,只需要写简单的Servlet就行了,HttpRequest和HttpResponse对象由Tomcat来创建,可以从HttpRequest中获得Header, Cookie, QueryString 等信息, 从HttpResponse中获得输出流,直接向浏览器输出结果, 简单又直接。

Tomcat还郑重向大家声明:对于每个请求,我只会用一个线程来出来,线程的开销可比进程小多了。

对于那个在代码中混杂HTML的问题怎么处理?

 Tomcat也有办法, 可以在HTML混杂代码!这就是JSP。执行期其实会被编译成Servlet。

(码农翻身注:请移步《JSP:一个装配工的没落》)

你看,责任分离了,每个人只要办好自己的事情就好。

(注:实际上,我们不会在Servlet中写业务逻辑, Servlet现在通常是一个通往框架的入口。)

WSGI

CGI表示不服:遵循我的协议,任何语言都可以来实现动态网站,你Servlet只是Java规范,不管别的语言了?

Servlet规范确实没法跨语言实现,那要是Python也想做动态Web网站,该怎么办?

既然已经认识到动态网站的本质了, 可以采用类似的思想来处理嘛! 我们为Python也定义一个规范,叫做WSGI (Web Service Gateway Interface)。

让程序员写个类或者函数(称为wsgi application),在其中实现逻辑。让某个动态服务器(称为wsgi server)把Http Request和Response传递给它,就可以执行了。

但是Python表示:我不喜欢你们Java 那一套啰里啰嗦的类,HttpRequest 不就是一些key value吗?放到我钟爱的dict中多好 !我把它叫做enviroment, HttpResponse也没必要,直接用函数的返回值(确切说是一个可迭代对象)就好。

640?wx_fmt=gif

640?wx_fmt=png

看看,是不是和Java 的Servlet 很像?(当然,忽略了很多细节。)

从本质上来说,都是为了关注点的分离:

1. 用一个动态内容服务器(wsgi server,Tomcat等)来接受并且封装HTTP 请求,降低程序员的负担。

2. 程序员只需要遵循约定(servlet,wsgi)就可以轻松实现自己的业务,不用关注系统的处理细节。

如果你先学的Java,通过Servlet理解了动态内容网站的本质和解决问题思路,再看到Python的wsgi,一眼就能看透,学起来飞快,反过来也是如此。

Web服务器的例子还比较简单,但是也体现出了这个道理:遇到问题要深度思考,努力看到本质,这样才能举一反三。


640?wx_fmt=gif


码农翻身公众号开放投稿,可能是全网最高片酬:

用故事讲技术 ,稿费1000

技术/职场/感悟/面试等,稿费700

翻译类文章,每千字200

详情猛戳: 可能是全网最高片酬,速来!

640?wx_fmt=jpeg

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值