Python web世界观——web架构概览(适合传统程序员)


传统web server面临的问题

我们知道传统的web server,一个进程打开socket,监听,来了请求生成新的进程(或线程、或阻塞)进行响应,本身还在继续监听。这是看过unix网络编程的大部分人所接触到的网络模型。然而,unix实在太老,网络需求在近些年发生了巨大的变化,最重要的就是对并发性的要求。

并发性的要求的提高,让本机的web server的架构也发生了变化,并且对本机这个词语也发生了不同的需求。因为server的请求可能不是一台机器处理的来的,那么又需要解决的一个问题是多个server机器的协作同步问题了。

现代web server的要求

首先是本机的变化。本机的变化有两个:并发性增加和快速开发的需求增加。我们的例子都以python作为例子。因为由于对快速开发迭代的的要求,C++等虽然性能好,但是变化速度慢,因此目前大部分网络系统后端都是用python做的,等到稳定后再切到高性能的C++或C或go后台。可惜的是互联网一直不太稳定。。。

单机提高并发性

传统的web server的模型上面说过了,如果来上百万的访问,岂不是要生成上百万个进程?(linux下线程也是进程,占用pid),并且进程管理着实耗费系统资源,这就导致了:无论硬件再怎么强大,也迟早会被并发的损耗耗尽。为了解决这个问题,应用了纤程(协程)的概念。进程本身是操作系统为用户提供的调度实体,具体的调度由操作系统完成。而协程是应用程序自己在自己的单个进程中实现的伪进程,多个协程就是多个代码的执行路径,协程的调度由应用程序自己完成(目前的一般做法是各个协程本身自己让出执行权,例如使用yield关键字)。如此就在一个操作系统的进程内模拟出了多个独立的并行的执行单位,并且协程之间的通信又特别简单(因为本来是一个进程,协程在进程看来就相当于进程内部的函数跳动和调用)。这个机制可以极大的解决单机的并发性需求。

python语言中提供了实现协程的关键字:yield。但是这不是协程的完整实现,然而pytho的好处是缺什么可以用模块实现来补充。利用yield关键字,python实现了两个比较出名的协程模块greenlet和stackless,两个的主要区别是stackless是自动的进行协程间调度,而greenlet需要各个协程手动的让出执行权利。各有优势,但是显然在精细化控制上greenlet的简单设计与python的yield关键字不谋而合,给用户提供了更高的权利(但是也需要付出更多的代码)。

又有人在greenlet之上封装实现了gevent。是基于协程的网络库。由于是基于协程的,所以这个网络库的最大特点就是高度并发。什么是网络库呢?无非就是封装了socket使用和进程线程模型。例如你可以动态的开多个进程(线程),每个都跑greenlet协程,也可以直接做个线程池。greenlet定义了协程和切换的方法,但是没有规定怎么切换,在socket下,很容易阻塞,gevent就预定了切换的情况。也就是说,在协程使用上,greenlet是方法,gevent实现了策略。

加快web开发迭代速度

上面说过,为了开发速度,产业界逐渐切换到了python,每个人都期望有朝一日能切换回去更高效的语言,但是进度表永远不给你机会,所以你会发现,目前的大型网站很多都是直接使用python在工作的。 但python语言本身确实可以提高开发速度。但是在网络应用上,python通过将网络开发标准化进一步提高开发速度。他提出的框架叫做WSGI,这是一个web框架定义标准,这个标准定义了应用端、服务端和中间件接口。大部分的对这个标准的实现都是实现的中间件,在这些实现的后面是服务端。我们所理解的web server不就是一个打开socket监听,然后处理吗?为什么要有中间件这一层呢?因为,打开socket,解析http包(https),维护和跟踪session、cookie等这些操作对所有http server来说都是通用的,python 的wsgi标准就是把这些通用的操作独立出来,定义好接口。所有的后端服务器都直接使用WSGI定义(中间件暴漏)的接口进行编程,可以让服务器编程和服务器程序本身变得非常的简单。
实现这个WSGI标准的有很多python库,例如gunicorn。作为这个标准定义的服务端也有很多python库实现:bottle、django、flask、tornado等。所以,按照WSGI的逻辑,一般一台机器都要同时启动gunicorn作为中间件,某个server(例如bottle)作为实际的后端,后端的用户程序开发都是使用bottle这些库提供的接口实现的。这是一种什么样的编程体验呢?
  1. @route('/helloworld/:yourwords', methods=['GET', 'POST']) #url接口,注意参数书写格式,前面有个冒号表示是参数
  2. def hello(yourwords):
  3. return 'hello world. ' + yourwords 短短几行代码,就定义了如果是什么样的url就执行什么样的逻辑代码并返回(不全)。比起传统的网络后端cgi编程,是不是觉得非常清爽?完全干净的只处理业务。这就是WSGI标准带给我们的。 这里的gunicorn可以独立的启动,但是并行性不好,所以就可以用上了我们上面说的gevent高度并行的网络库。对于这两个来说gevent是方法,gunicorn又是策略了。我们可以看到,通过这一层层的封装和调用,让各个组件可以在不同场景下选择不同的实现方式,从而达到不同的目的。这种类似搭积木的灵活性,是传统web开发所没有的。

    跨机器的并发:云后台

    在一台机器上增加并发,提供和实现标准,虽然显著提高了单机的作战能力,但对于现代的网络要求还是不够。这就需要多台机器一起对外提供服务。使用多台机器就有了传统的云需求:什么样的请求在什么时候选择哪台PC来提供服务?这里面就有了负载均衡、网络代理、可用性、一致性等实现要求。 现有很多软件同时能满足多种服务,例如nginx可以在网络代理也可以做负载均衡。而做负载均衡还有F5、LVS等专用的软件或硬件。keepalive软件等负责可用性、例如raid等很多方案又可以提供一致性。这些在逻辑上是分离的,但是在实现上很可能有兼容并包的软件。目前流行的软件,在某一刻可能就过时了。但是这个需求是不会变的。这也就构成了现代的web服务器的后端像防火墙一样的标配了。


     

相关内容

    暂无相关文章

评论关闭