werkzeug 详解
首先,先向大家介绍一下什么是 werkzeug,Werkzeug是一个WSGI工具包,他可以作为一个Web框架的底层库。这里稍微说一下, werkzeug 不是一个web服务器,也不是一个web框架,而是一个工具包,官方的介绍说是一个 WSGI 工具包,它可以作为一个 Web 框架的底层库,因为它封装好了很多 Web 框架的东西,例如 Request,Response 等等。
例如我最常用的 Flask 框架就是一 Werkzeug 为基础开发的,这也是我要解析一下 Werkzeug 底层的原因,因为我想知道 Flask 的实现逻辑以及底层控制。这篇文章没有涉及到 Flask 的相关内容,只是以 Werkzeug 创建一个简单的 Web 应用,然后以这个 Web 应用为例剖析请求的处理以及响应的产生过程。
下面我们以一个简短的例子开始,先看看怎么使用 werkzeug,然后再逐步刨析 werkzeug 的实现原理。
安装 werkzeug
我希望读者是在 virtualenv 环境中跟着我的步伐走得,如果你还不知道什么是 virtualenv,那么你可以在我的博客中搜索一下 virtualenv,然后先弄好,再继续,因为很可能因为一些库的冲突等问题导致你看不到本文中介绍的东西。
ok,下面开始安装 werkzeug,
1
|
pip install Werkzeug
|
这条命令下去,几秒钟之后你就可以使用 werkzeug 了。
一个简单地 web 服务器
接下来,我们就开始使用 werkzeug 来创建一个简单的 web 服务器,这个服务器就仅仅返回 “Hello Werkzeug”,没有其他内容。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
|
#!/usr/bin/env python
# encoding: utf-8
import os
from werkzeug.serving import run_simple
from werkzeug.wrappers import Request, Response
from werkzeug.wsgi import SharedDataMiddleware
class Shortly(object):
def dispatch_request(self, request):
return Response('Hello Werkzeug!')
def wsgi_app(self, environ, start_response):
request = Request(environ)
response = self.dispatch_request(request)
return response(environ, start_response)
def __call__(self, environ, start_response):
return self.wsgi_app(environ, start_response)
def create_app(with_static=True):
app = Shortly()
if with_static:
app.wsgi_app = SharedDataMiddleware(app.wsgi_app, {
'/static': os.path.join(os.path.dirname(__file__),'static')
})
return app
if __name__ == '__main__':
app = create_app()
run_simple('127.0.0.1', 6666, app, use_debugger=True,use_reloader=True)
|
这段代码就实现了我说的功能,那么我们就来看看这段代码是怎么运作的?
首先,一切都回到最开始的地方开始,从 main 开始看起,可以发现 main 是非常简单地,只有一个初始化函数,然后就调用了 werkzeug 的 run_simple 函数。okay,我们可以发现这个 app 其实是一个 Shortly 对象,这个类就只实现了 3 个方法,一个是 dispatch_request, wsig_app, call ,就这么简单了,那我们就知道了,关键的代码都不是这些,应该是 run_simple.
run_simple 解析
okay,我们这个系列博客的目的就是解析 werkzeug 源码,所以拿到 werkzeug 源码肯定是我们必须要做的。所以第一步我们就需要从 github 上将 werkzeug clone 下来:
1
|
git clone https://github.com/mitsuhiko/werkzeug.git
|
然后,我们就找 run_simple 的代码咯
1
|
vim werkzeug/serving.py
|
goto line 559
我们可以看到这个函数的定义,秉着关注重点的原则,我们就忽略条件判断,以一条最简单地路线来看代码,那么这里就假设:
1
2
3
|
use_debugger = False
static_files = False
use_reloader = False
|
OK, 那到这里其实 run_simple 调用的就是 inner 了,那么就来看看 inner 的代码:
1
2
3
4
5
6
7
8
9
10
11
|
646: try:
647: fd = int(os.environ['WERKZEUG_SERVER_FD'])
648: except (LookupError, ValueError):
649: fd = None
650: srv = make_server(hostname, port, application, threaded,
651: processes, request_handler,
652: passthrough_errors, ssl_context,
653: fd=fd)
654: if fd is None:
655: log_startup(srv.socket)
656: srv.serve_forever()
|
忽略 fd,那么剩下一点点了:
1
2
3
4
5
6
|
650: srv = make_server(hostname, port, application, threaded,
651: processes, request_handler,
652: passthrough_errors, ssl_context,
653: fd=fd)
656: srv.serve_forever()
|
好,你应该和我一样有兴致得想知道这个 make_server 里面是什么内容了,我也很期待,那就跟上去看看。
make_server 的代码我就不贴了,还是最简原则,忽略各种条件,那么这里就假设:
1
2
|
threaded = False
processes = 1
|
那么代码也很简单了,就剩下:
1
2
|
546: return BaseWSGIServer(host, port, app, request_handler,
547: passthrough_errors, ssl_context, fd=fd)
|
很好,好不容易跟踪到这,终于上关键了,那就是这个 BaseWSGIServer 了,我们就来看看这个类实现了什么功能。
先看这个类的定义:
1
|
443: class BaseWSGIServer(HTTPServer, object):
|
这个类是继承自 HTTPServer 的,那么我们就有点底了,这差不多到头了,已经和 Python 的 API 碰上了。好,既然是继承自 HTTPServer,那么就把他当做 HTTPServer,然后继续看 run_simple 的代码,我们一路跟踪下来,我们发现了 656 行有一个 srv.serve_forever()
,那么这不就是 HTTPServer 的用法吗? server.serve_forever() 。
okay,到这那么事情已经暂告一段落了,虽然很多事情都还没搞清楚,例如请求是怎么被封装的,响应又在哪里被处理了,例如URL路由之类的怎么操作的。但是,我们已经对 Werkzeug 有一个大概的印象了,知道他低层还是 HTTPServer 实现的,没有太多特殊的自定义协议。在下一章我们会逐步得进行进行更深层次的解密。欢迎继续关注。
werkzeug 详解的更多相关文章
- Flask-Login详解
Flask-Login详解 关于Flask登录认证的详细过程请参见拙作<<使用Flask实现用户登陆认证的详细过程>>一文,而本文则偏重于详细介绍Flask-Login的原理, ...
- Linq之旅:Linq入门详解(Linq to Objects)
示例代码下载:Linq之旅:Linq入门详解(Linq to Objects) 本博文详细介绍 .NET 3.5 中引入的重要功能:Language Integrated Query(LINQ,语言集 ...
- 架构设计:远程调用服务架构设计及zookeeper技术详解(下篇)
一.下篇开头的废话 终于开写下篇了,这也是我写远程调用框架的第三篇文章,前两篇都被博客园作为[编辑推荐]的文章,很兴奋哦,嘿嘿~~~~,本人是个很臭美的人,一定得要截图为证: 今天是2014年的第一天 ...
- EntityFramework Core 1.1 Add、Attach、Update、Remove方法如何高效使用详解
前言 我比较喜欢安静,大概和我喜欢研究和琢磨技术原因相关吧,刚好到了元旦节,这几天可以好好学习下EF Core,同时在项目当中用到EF Core,借此机会给予比较深入的理解,这里我们只讲解和EF 6. ...
- Java 字符串格式化详解
Java 字符串格式化详解 版权声明:本文为博主原创文章,未经博主允许不得转载. 微博:厉圣杰 文中如有纰漏,欢迎大家留言指出. 在 Java 的 String 类中,可以使用 format() 方法 ...
- Android Notification 详解(一)——基本操作
Android Notification 详解(一)--基本操作 版权声明:本文为博主原创文章,未经博主允许不得转载. 微博:厉圣杰 源码:AndroidDemo/Notification 文中如有纰 ...
- Android Notification 详解——基本操作
Android Notification 详解 版权声明:本文为博主原创文章,未经博主允许不得转载. 前几天项目中有用到 Android 通知相关的内容,索性把 Android Notificatio ...
- Git初探--笔记整理和Git命令详解
几个重要的概念 首先先明确几个概念: WorkPlace : 工作区 Index: 暂存区 Repository: 本地仓库/版本库 Remote: 远程仓库 当在Remote(如Github)上面c ...
- Drawable实战解析:Android XML shape 标签使用详解(apk瘦身,减少内存好帮手)
Android XML shape 标签使用详解 一个android开发者肯定懂得使用 xml 定义一个 Drawable,比如定义一个 rect 或者 circle 作为一个 View 的背景. ...
随机推荐
- nativeLibraryDirectories=[/data/app/com.lukouapp-1/lib/arm64, /vendor/lib64, /system/lib64]]] couldn't find "libxxxx.so
一开始我遇到的问题是,如果手机有SD卡槽,则不管有没有插卡,都会闪退,打日记后发现是找不到so文件.报错日记如下: nativeLibraryDirectories=[/data/app/com.lu ...
- 【(待重做)树状数组+dp+离散化】Counting Sequences
https://www.bnuoj.com/v3/contest_show.php?cid=9149#problem/G [题意] 给定一个数组a,问这个数组有多少个子序列,满足子序列中任意两个相邻数 ...
- codeforces 362A找规律
刚开始以为是搜索白忙活了原来是个简单的找规律,以后要多想啊 此题是两马同时跳 A. Two Semiknights Meet time limit per test 1 second memory l ...
- apple applessd.sys error
http://bbs.feng.com/read-htm-tid-10242622.html
- mybatis collection标签和association标签(一对多,一对一)转载
mybatis 一对一与一对多collection和association的使用 在mybatis如何进行一对一.一对多的多表查询呢?这里用一个简单的例子说明. 一.一对一 1.associati ...
- SGU 439 A Secret Book
解法: 对于第二个串,循环移动能得到的字典序最小的串,可以直接用最小表示法搞定. 然后用最小表示的第二个串和第一个串做两次扩展KMP,一次正常求,另外一次将两个串都反转一下,然后扫一遍ex[]数组 # ...
- 装机、做系统必备:秒懂MBR和GPT分区表
从Intel 6系列主板之后,就开始提供UEFI BIOS支持,正式支持GPT硬盘分区表,一举取代了此前的MBR分区表格式,不过为了保持对老平台的兼容,微软即使最新的Windows 10系统也继续提供 ...
- Ckeditor通过Ajax更新数据
之前在表单中对ckeditor的赋值就直接是 $("#theadEditor").val(result); 而如今我想通过点击不同选项来使用Ajax在后台訪问数据.对ckedito ...
- 用yarn替代npm
最近,从npm拉取vue-cli总失败,后来干脆直接用yarn 全局安装yarn(官网首推用系统包安装,更加安全) npm i yarn -g 用yarn添加全局vue-cli yarn global ...
- react 执行 yarn build 页面无法显示
资源文件路径问题 如果你使用create-react-app创建项目,执行命令 yarn build 后,直接以静态方式打开build文件夹内的index.html,会看到页面显示出现问题,打开con ...