写在前面:Flask框架是通过多线程/多进程+阻塞的socket实现非阻塞,其本质是基于python的源库socketserver实现的

  1. 前言
  2. 认识WSGI协议
  3. 认识Werkzeug
  4. flask是如何实现非阻塞的

本文使用的flask框架为最新的1.1.1版本,所有代码基于python3运行

一:前言

使用过flask或者其他web框架的人应该对web框架这种东西并不陌生,它是通过对一系列包括请求处理、路由分发、session管理甚至网络攻防等各个模块的封装,让使用者可以快速搭建起一个web应用,让开发者可以将更多的精力放在业务逻辑代码的coding上,而不用过多的去对偏底层的部分进行开发。这次,我试着通过《flask框架是如何实现非阻塞并发的》这篇文章加上对flask源码的分析尝试解答以下几个问题:

  • flask框架的大体架构是怎样的
  • 什么是WSGI协议
  • Werkzeug和flask是什么关系,和WSGI又是什么关系
  • 非阻塞并发的功能是在flask哪个部分实现的,又是如何实现的

首先我们看一下一个基本的flask应用程序:

import 

当我们在浏览器种访问http://127.0.0.1:8088/index的时候,系统会被time.sleep(15)阻塞15秒才能够得到返回,而当在这15秒内我们再次访问http://127.0.0.1:8088访问另外一个路由的时候,"hello world"会立刻返回,说明该请求未被前一个请求阻塞。现在,我们尝试对app.run()之后发生的事情做一个分析。

run()函数位于flask/app.py中的Flask类下,核心代码如下:

def 

在run函数内,函数设置了服务启动的address,port等信息,最后引入了一个werkzeug库的run_simple方法,另外还传入了一个options参数。要特别注意的是,options.setdefault("threaded",True)这行代码在最初的flask 0.1系列版本种是不存在的

以上是flask 0.12版本种的代码,这也就也为这通过传入threaded=false的方式可以使得flask以单进程单线程的阻塞方式运行,这样当前一个请求被阻塞,之后的请求也将被阻塞。

我们再进入到werkzeug中去看看run_simple()中发生了什么事情,关键代码如下:

def 

该文件位于werkzeug库中的server.py文件中,从这里不难看出,flask框架是通过调用Werkzeug这个第三方库来创建web服务,监听用户的请求,要了解Werkzeug首先我们需要去知道python的WSGI协议。

二:认识WSGI协议

那什么是WSGI协议呢?看上去似乎很高大上,其实,这种协议或者称作为“约定”的东西,用几行代码就可以解释清楚:

def 

以上是对WSGI最基本的解释,WSGI 对于 application 对象有如下三点要求

必须是一个可调用的对象
接收两个必选参数environ、start_response。
返回值必须是可迭代对象,用来表示http body。

WSGI是Web Server Gateway Interface的缩写,它是Python应用程序或者框架(如Flask)和web服务器之间的一种接口。

它是一种协议,一种规范,其是在 PEP 333提出的,并在PEP 3333进行补充(主要是为了支持 Python3.x)。这个协议旨在解决众多 web 框架和web server软件的兼容问题。有了WSGI,你不用再因为你使用的web 框架而去选择特定的 web server软件。

明白了么?通俗来说,就是只这种规范格式约定:web服务器比如Werkzeug调用应用提供的某个函数application()向这个函数传入environ,start_reponse这两个参数,最终该应用的这个函数返回一个可迭代对象给web服务器。这也可以用来表明flask基本的架构。

Flask框架的运作图

所以,WSGI不过是一种协议,一种约定罢了,它规定了server和app之间的调用关系,你要提供给我怎样的参数,我要怎么调用你,就是这么简单,就好比手机商和数据线厂商约定我们都通过type-c格式进行连接一样,这样,不过任何一家手机生产商和任何一家数据线生产商都可以生产自己的产品,这家厂商的手机可以用多个厂商的数据线,这样就把这样的功能给分开了。同样,只要实现了WSGI协议的任何web server都可以作为flask app的服务器,既不适用Flask 框架携带的Werkzeug作为默认的web server,比如uWSGI, Gunicorn,mod_wsgi等等,同样,同一个实现了WSGI协议的server也可以用在其他实现了WSGI协议的web框架上,比如:Django, Bottle ,flask。以下列举了实现该协议的框架和Server分别有哪些。

Frameworks that run on WSGI​wsgi.readthedocs.ioServers which support WSGI​wsgi.readthedocs.io

如果你有兴趣,甚至我们可以自己去写一个简单的实现了WSGI协议的server。

首先我们新建一个flask_app.py的文件:

from 

再建立一个wsgi_server.py文件:

import 

当我们运行"python wsgi_server.py flask_app:flask_app"命令的时候,这个简易的server将会启动并且监听4000端口,于是我们在浏览器访问http://127.0.0.1:4000/user/5的时候,浏览器将显示Get /user/5 has been processed in flask app,说明这个简易的WSGI服务器已经完成,注意以下几点:

这里直接引用Flask的原因是Flask本身已经实现了WSGI协议,Flask框架本身是一个可被调用的对象,这一特点可以在Flask源码中的以下位置说明:

关键代码:flask/app.py

class 

可以很明显的看出,在类Flask中的wsgi_app()这个函数实现了WSGI协议。

关于WSGI协议更多的内容可以查看以下一些文章:

王炳明:花了两个星期,我终于把 WSGI 整明白了​zhuanlan.zhihu.com

WSGI 为什么很重要?​www.zhihu.com

kevinbai:说说我对 WSGI 的理解​zhuanlan.zhihu.com

相信看了上面几篇文章以后,大家会对WSGI有更加明确的认识。

三:认识Werkzeug

通过前面的内容我们知道,Flask框架通过引用第三方库Werkzeug作为自带的web服务器用于接收用户的请求并且完成响应任务。Werkzeug其实是一个WSGI工具包,它可以作为一些web框架的底层库,这里稍微说明以下,werkzeug不是一个web服务器,也不是一个web框架,而是一个工具包,官方的介绍说是一个 WSGI 工具包,它可以作为一个 Web 框架的底层库,因为它封装好了很多 Web 框架的东西,例如 Request,Response 等等。其实也就是说,我们完全可以只用Werkzeug创建一个简单的web应用,而完全不用flask相关内容。

接下来,我们利用Werkzeug创建一个简单的web服务器shortly.py:

import 

很相似,不是么?这和我们刚才在flask框架源码中看到的内容是一样的,在这种情况下,我们可以传入threaded=True这个参数,此时该server是多线程的,如果传递threaded=false,那么该server将会以单进程单线程的阻塞方式运行,如果此时访问/index路由后,再访问其他路由请求将会被阻塞。

于是乎,当进行到这里的时候,这篇文章想要探究的问题,就变成了Werkzeug如何实现非阻塞并发了,我们可以进一步探究它的源码。

进入run_simple()内部:

def 

make_server()和server_forerver()成为了关键

当我们进入到make_server()函数中:

def 

我们发现它可以根据传入的threaded和process两个值确定是使用多线程还是多进程亦或者是单线程的方式运行server,根据flask默认传递的threaded=True,我么进入到ThreadedWSGIServer这个类中看它是如何实现的。

class 

这个类的实现很简单,通过继承ThreadingMixIn和BaseWSGIServer两个类实现多线程的socket,通过分析,我们可以发现,这两个类最终对应的源头是python基本库中的socketserver库的ThreadingMixIn和TCPServer两个类,与似乎,当前的问题又被转换位对socketserver的研究了。

四:flask是如何实现非阻塞的

当问题进行到了这一步,我们也渐渐明确了核心要点是要明确,socketserver中如何通过ThreadingMixIn和TCPServer这两个类的组合实现server的多线程非阻塞。这里我们可以再写一个例子来证明我们的这种观点:

先建立一个threaded_socket_server.py文件

import 

再建立一个socket_client.py文件:

import 

当我们运行server的时候,该服务会监听9999端口,然后我们分别运行两次socket_client.py文件,模拟两次对该server的请求:

python socket_client.py test1
python socket_client.py test2

此时,server将会输出:

Thread-1收到数据b'test1n':模拟阻塞
Thread-2收到数据b'test2n':模拟阻塞
Thread-1模拟阻塞完成
Thread-2模拟阻塞完成

可见,这里的socket确实是非阻塞的了。另一个请求的阻塞不会影响其他请求的响应。那么

class 

这行代码是如何实现的呢?

通过观察,我们可以看到socketserver.py文件中

class 

这个类重写了类BaseServer中的process_request()方法,而BaseServer是类TCPServer的父类,这个重写也就相当于是重写了TCPServer中的该方法,当server接受到请求,如果threaded被设为True,则会执行ThreadingMixIn中的process_request(),该方法内创建了一个新的线程来处理请求,于是,真相就水落石出了。

class 

总之:Flask框架是通过多线程/多进程+阻塞的socket实现非阻塞,其本质是基于python的源库socketserver实现的

flask框架_Flask: flask框架是如何实现非阻塞并发的相关推荐

  1. 200行自定义异步非阻塞Web框架

    Python的Web框架中Tornado以异步非阻塞而闻名.本篇将使用200行代码完成一个微型异步非阻塞Web框架:Snow. 一.源码 本文基于非阻塞的Socket以及IO多路复用从而实现异步非阻塞 ...

  2. flask框架_Flask框架的入门:Hello world

    Flask是python的微框架.微框架中的"微"意味着Flask旨在使核心保持简单但可扩展.flask上手非常容易,可以很快的实现一个简单的网站.当然,网站看起来好不好,与htm ...

  3. Python常用框架:Flask

    Flask框架的诞生: Flask诞生于2010年, Armin Ronacher的一个愚人节玩笑.不过现在已经是一个用python语言基于Werkzeug工具箱编写的轻量级web开发框架,它主要面向 ...

  4. Flask框架(1.flask概述,Windows配置 virtualenv虚拟环境步骤和路由以及视图函数的定义)

    框架:      软件框架,指的是为了实现某个业界标准或者完成特别基本任务的软件组件规范,也指为了实现某个软件组建规范时,提供规范所要求之基础功能的软件产品.       框架的功能类似于基础设施,提 ...

  5. Python 框架之Flask初步了解

    Python 框架之Flask初步了解 前言 ​ 在了解python web 框架之前,我们需要先了解框架实现的基本原理.首先,需要了解WSGI(Web Server Gateway Interfac ...

  6. Django/Flask/Tornado三大web框架性能分析

    写在前面: 本文的数据涉及到之前遇到过的问题,大概一次 http 请求到收到响应需要多少时间.这个问题在实际工作中与框架有比较大的关系,因此特别就框架的性能做了一次分析. 这里使用之前的一个报告数据: ...

  7. Web框架——Flask系列之Flask简介(一)

    一.Web应用程序作用 Web(World Wide Web)诞生最初的目的,是为了利用互联网交流工作文档 二.关于Web框架 (一)什么是Web框架? 已经封装好了一段代码,协助程序快速开发,相当于 ...

  8. Flask——1.初识flask微框架

    官网:http://python.usyiyi.cn/translate/flask_011_ch/index.html 1. FLASK 概述 Flask是使用python语言编写的一个轻量级的we ...

  9. 【Python】flask框架学习 flask框架的基本使用

    flask框架是什么? Flask 是一个轻量级的 Web 框架,用于构建 Web 应用程序.它基于 Python 编程语言和 Werkzeug 工具包,提供了简单易用的 API,可以轻松地创建 RE ...

最新文章

  1. css实现 textarea 高度自适应
  2. 2.2版本发布!TensorFlow推出开发者技能证书
  3. 《Rhino3D 4.0产品造型设计学习手册》——导读
  4. GRE作文用AI打分,已经20周年了:AI给中国考生的分数,远高于人类打分
  5. Docker简介以及mysql和redis的部署
  6. mysql + keepalived高可用
  7. java http请求_如何设置Fiddler来拦截Java代码发送HTTP请求,进行各种问题排查
  8. Windows微秒级定时方法
  9. 怎么批量在数字里加入网页_手把手教你爬取天堂网1920*1080大图片(批量下载)——理论篇
  10. 苹果AirPods有望在年末推出新款产品 或将支持防水功能
  11. html手机验证码登录页面代码,htmlunit 模拟登录 数字验证码(示例代码)
  12. eda课程设计,求救!!!!!!!!
  13. 向量坐标相乘的计算算法
  14. 供应链金融及产业风控
  15. java实现mysql的导入导出_Java实现mysql导入导出Excel
  16. Python输出1000以内质数代码
  17. 【个人小程序和企业小程序的区别】
  18. Unity3D魔方游戏如何完成魔方的旋转
  19. 使用Android Studio 日常小问题
  20. matlab批量图片旋转处理

热门文章

  1. Android与iOS/WP8跨平台整合设计与开发_专栏
  2. head first java 03 ( 6 章 )
  3. 在.Net中json应用测试整理
  4. 打印1到最大的n位数
  5. pytorch 优化器调参
  6. 【Gamma】Scrum Meeting 6
  7. 一条汇编指令是如何在计算机的硬件中进行执行的
  8. redis 应用场景和数据类型
  9. 驱动中的C语言----指针与指针初使化
  10. 8 mv命令_Linux常用操作命令——文件和目录操作