连接数据库通过配置文件app.config
1. 小记
最近在学习Flask这个Web框架, 相比于Django, Flask算的上是微型的Web框架了,他只有路由和模板渲染两个功能, 想干别的事都需要使用插件. 好在目前的插件数量也不少, 也不乏一些十分好用的插件, 让Flask在企业Web应用开发中还是有一席之地的(我听说知乎就是用的Flask+tornado).
这一路学下来, 基本上就会写一些视图函数, 完成简单的业务逻辑, 对于框架执行流程其实知道得很少, 仅仅是对路由机制有一点点的了解.
Flask的路由机制主要依赖Werkzeug.routing模块, 主要是
Map
类,Rule
类,MapAdapter
类等提供的功能
于是一直想看看Flask的源码, 对这个框架进行进一步的研究, 但是无奈实在看不懂, 不知道从何看起是最大的问题.
于是我自己一直尝试寻找切入点, 好让我理清看源码的思路, 接下来我就主要记录一下自己的琢磨过程, 这个思考琢磨的过程对我一个新手开发者来说很重要.
我想:
- 一个Web应用都是从接受请求开始, 通过分析请求, 匹配对应的路由规则, 再去调用视图函数, 返回响应结果的, 那么Flask中接受请求的入口在哪里?
- 由于Flask是遵守WSGI协议的, WSGI协议是Python中的一种Web规范, 一定有大家共同遵守的规则, 那么WGSI协议中, 大家遵守的规则是什么?
- 通过大家都遵守的规则, 也许就能找到Flask中处理请求的入口在哪里了. 同时, 任何遵循这个协议框架的请求入口都能找到.
实际上这个思考过程并没有这么顺理成章, 我在学习Flask之前就看过相关WSGI的介绍, 无奈也不是特别能理解.
但是当先去了解了某些概念后, 以后再别的地方再次碰到这个问题, 首先就知道去哪里查相关资料了, 这个时候再问题为导向去学习新的知识的时候就会理解的快得多, 并且当理解之后就会融会贯通.
那第一步就是研究一下究竟什么是WSGI.
2. WSGI
WSGI全称Web Server Gateway Interface, 不要被名字唬住, 先看这么一句话一起体会一下WSGI是什么:
This document specifies a proposed standard interface between web servers and Python web applications or frameworks, to promote web application portability across a variety of web servers.
这句话来自PEP3333的摘要部分, 简单翻译一下就是
这份文档(指的就是PEP3333)详细说明了Web服务器和Web应用之间的标准接口, 旨在提升Web应用的可移植性
这里面的关键字就是:
- 可移植性: 这是WSGI存在的目的
- 标准接口: 这是WSGI定义的规则
如果知道了WSGI的目的, 理解后续的内容就轻松不少, 那我们就先讲一下WSGI的目的, 理解一下可移植性的含义吧
2.1 WSGI协议的作用
当我们访问某个网站的时候:
- 浏览器作为用户代理为我们发送了HTTP请求
- 这条请求经过长途的跋涉终于找到了能够接受它的服务器
- 服务器上的HTTP服务器软件会将请求交给Web应用处理该请求,得到用户想要的数据
- Web应用再将数据交给HTTP服务器软件, 再由HTTP服务器软件返回响应结果
- 浏览器接受到响应, 显示响应内容
基本上就是这么一回事:
这里面一共出现了三个角色, 分别是:
- 服务器: 硬件层面, 也就是机房里面的计算机(或者集群), 运行着操作系统+一些软件.
- HTTP服务器软件: 运行在服务器上的软件, 用于监听客户端请求,将接受到的请求交给Web应用, 再将Web应用的响应结果返回给客户端.
- Web应用: 接受服务器软件传过来的请求, 处理请求, 并返回处理后的结果给服务器软件.(我们的Flask, Django都是这个层面的)
由于服务器硬件咱们也没什么好说的, 因此接下来提到的服务器(包括HTTP服务器)指的都是HTTP服务器软件.
对于初学者来说服务器好像就是硬件嘛, 和软件没有关系, 但实际上, 服务器既可以指硬件, 也可以指软件. 维基百科.
那WSGI协议的作用在这里就是连接服务器软件和Web应用的桥梁, 这两方规定一系列协议, 要求两者之间传输的数据对方都能看得懂.
那这么做有什么好处呢?其实就是上面说的可移植性了, 如果对可移植性还是不太理解也没有关系, 下面让我们来看看这里面一个问题.
既然服务器软件和Web应用都可接受请求, 返回请求, 为什么搞这么复杂?非要搞个协议, 接受请求,处理请求和返回请求的都是一个人不可以么?我和我交流还需要协议么?
没错, 上一个人也是这么想的, 而且事实上, 我们确实可以这么做.接下来我们来看一下三种模型, 来更好的理解一下WSGI协议和可移植性.
2.1.1 第一种模型 - tornado
tornado是由F打头的404网站收购并且开源出来的Web框架, 他的第一个特点奏是将HTTP服务器和Web应用整合到了一起.
所以可以用tornado搭建出来的服务器模型如下
这就是我们刚才说的接受请求, 处理请求, 返回请求都是一个人, 感觉也不错.
但是请注意了, 虽然表面上看起来这好像是一个人处理的, 但是我们在编写处理业务逻辑的代码时, 肯定不会去碰服务器相关的代码, 只需要写好视图和路由就可以了, 因此对于tornado来说, 他的服务器部分和逻辑处理部分还是分开的. 这就相当于虽然是一个人, 但是他的手和大脑是两个部分.
实际上长这样
这里有两个问题.
- 如果我不想用tornado这个框架写逻辑部分, 那么我也一定不能使用它的服务器部分.
- 如果我就想用它的Web框架, 想换一个性能更加强大的服务器(软件), 似乎也做不到.
谁叫他们是一个人呢!
这就是所谓的没有可移植性
请注意:
实际上, tornado的服务器和Web框架是兼容WSGI协议的, 有兴趣的话可以自己搜索一下相关内容.举这个例子是因为在Python Web框架中, tornado实现了高性能的HTTP服务器仅此而已.如果百度过Tornado的也许知道他有一个很大的特点就是, 异步, 非阻塞, 高性能之类的. 其实, 这个特点说是他的HTTP服务器部分, tornado服务器擅长处理多个长连接, 可以用于在线聊天等业务场景.
2.1.2 第二种模型 - WSGI服务器+Web框架
终于轮到WSGI出场了.
对于一个遵守WSGI协议的服务器和Web应用来说, 它并不在意到底是谁传过来的数据, 只需要知道传过来的数据符合某种格式, 两边都能处理对方传入的数据.
打个比方, 你特别特别想吃水饺, 于是请了俩人, A专门擀面皮, B专门包饺子, 由于擀面皮的人动作比较快, A还要负责把包好的饺子拿过来下锅, 这样你才能吃到水饺.
A擀面皮的时候需要遵守饺子协议, 一定要把面皮擀成圆的(没错, 馄饨皮就是方的!), 并且皮还不能太大, 太厚, 这样B才能保证包出饺子来.
同样的, B也要遵守饺子协议, 再拿到饺子皮后使用灵巧的手法捏出造型各异的水饺, 但是他包的一定是饺子, 不能是一坨A看不懂的东西!
B把包好水饺拿给A去下饺子, 好在这二人都遵守了饺子协议, A一看, 不错, 这家伙包出的的确是饺子, 那我也保证我能够最后煮熟的东西是水饺了.
最后, 你吃到了水饺, 但是A个B始终也不认识对方, 如果在这个过程中你吧B换了另一个遵循饺子协议的C, 他与A还是能够紧密合作.
A只在乎自己擀出的是面皮, 拿到的是饺子
B只在乎拿到的面皮, 包出的是饺子
于是我们可以搭建像这样的模型:
目前被广泛应用的WSGI服务器(又称为WSGI容器), 主要有gunicorn
和uWSGI
.完整列表
而遵循WSGI协议的python web框架有Django
, Flask
, Pyramid
, web2py
, Bottle
等等等.完整列表
最终, 我们有:
随便怎么组合都可以, 怎么样, 是不是可移植性更强了?
2.1.3 第三种模型 - 最终形态
然而在真正的生产环境中, 以我们上面的模型是完全扛不住很多人一起访问的, 于是就有了服务器集群的概念, 使用一个性能更好的服务器打头阵, 然后它所做的事情就是把接收到的请求再分发给其他计算机去处理.
这就好像后来你开了个饺子馆, 为了能够接待更多的人, 你必须再雇对几个包饺子的组合. 当然, 还必须有一个收银人员.
这里拿NGINX来举例
在这个模型中, 我们的WSGI服务器起到了承上启下的作用, 它只处理NGINX丢给他的请求.
当然, 这也不意味着我们对WSGI服务器性能要求不高了, 因为真正去调用Web应用的还是WSGI服务器, 我们只不过使用NGINX去实现了负载均衡.
其实第三种形态远没有这么简单. 我了解的也不是很多, 但是到了这里, 我们应该已经完全理解了WSGI协议的目的了. 那接下来就来看一下WSGI协议到底指定了哪些规则吧!
了解过部署的朋友可能知道还有另一个高性能的服务器-Apache, 但是通过阿帕奇与WSGI应用的交互似乎是通过阿帕奇自带的模块去进行的.
2.2 WSGI协议简单分析
WSGI协议这么厉害!那一定很难实现吧!
其实WSGI协议内容没有这么神秘, 也特别容易实现, 它只是一系列简单的规则, 这个规则有多简单呢, 一会儿我们用3行代码就可以写完一个简单的WSGI应用, 而且不需要导入任何模块和包.
由于涉及服务器与Web应用交互, WSGI的规则分为两个部分, 分别对WSGI服务器端和WSGI应用端做了要求. 作为Web后端开发人员, 我们和框架(应用)打交道, 只需要了解WSGI协议对应用的要求.(我才不会说我也没有看服务器端的协议内容)
假设现在已经有了一个WSGI服务器, 现在需要编写一个WSGI应用, 那么我们的应用该如何和服务器进行交互呢?
换句话说, 我们的应用需要接受什么样的数据, 需要返回什么样的数据呢?
OK, 让我们把WSGI应用端规则罗列一下:
- Web应用必须是一个可调用对象, 它必须*接受*两个参数
- 其中第一个参数是
environ
(字典类型), 另一个参数是start_response
(函数) - 需要在返回响应之前调用
start_response
- 最终返回的一个可迭代对象
什么?你问我什么是可调用对象, 什么是可迭代对象?不如去问问他吧.
这两个参数的具体内容为:
environ
本次请求的所有内容, 我挑了几个看着眼熟的.
{ 'REQUEST_METHOD': 'GET',
'RAW_URI': '/',
'SERVER_PROTOCOL': 'HTTP/1.1',
'HTTP_HOST': '127.0.0.1:5000',
'HTTP_CONNECTION': 'keep-alive',
'HTTP_ACCEPT': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8',
'HTTP_ACCEPT_LANGUAGE': 'zh-CN,zh;q=0.9,en;q=0.8',
'REMOTE_ADDR': '127.0.0.1',
'REMOTE_PORT': '62445',
'PATH_INFO': '/',
...
}
start_response
函数, 返回数据之前需要调用, 用于设置状态码和响应头
- 注意, 调用
start_response
函数时, 第一个参数用于设置状态码, 是一个字符串 - 第二个参数用于设置响应头, 要注意该参数的格式(见下方代码).
根据这些个规则, 我们很容易写出一个遵循WSGI协议的Web应用:
# 1. 必须是一个**可调用对象**, 它必须接受**两个参数**
def demo_app(environ,start_response):
# 2. 其中第一个参数是`environ`(字典类型), 另一个参数是`start_response`(函数)
# print(environ)
# 3. 返回之前调用一次start_response 返回状态码和响应头, 注意参数格式, 不同的请求头信息用元组隔开
start_response("200 OK", [('Content-Type','text/html')])
# 4. 返回一个可迭代对象, 这里就是最终的响应体
return [b"<h1>Hello WSGI!</h1>"]
是不是很简单? 排除注释一共就只有3行代码.
至此, 我们就看完了WSGI协议应用端的部分. 那么现在, 来看一下Flask中的WSGI是怎么体现的吧
上面的函数是一个完全符合WSGI协议的函数, 因此他就可以作为一个WSGI应用, 你甚至可以将它跑起来!
如果你已经安装了Gunicorn, 将上述代码保存, 并命名为
my_app.py
, 你只需要将终端切换到该文件所在的路径下, 然后输入
gunicorn -b 127.0.0.1:5000 my_app:demo_app
就可以正常运行起来, 此时用浏览器访问127.0.0.1:5000, 就能看到返回的结果!
你也可以将print所在行注释掉, 看一下environ里面包含的完整信息注意: Gunicorn只支持类Unix系统! 不支持Windows!
2.3 Flask中的WSGI
我们知道了, 遵循WSGI协议的应用一定是一个可调用对象, 所以它既可以是一个函数, 也可以是一个实现了__call__()
方法的类.
话不多说, 上源码
class Flask(_PackageBoundObject):
.....
def __call__(self, environ, start_response):
return self.wsgi_app(environ, start_response)
...
我们轻松找到了Flask中处理请求的入口, 但是, 它又调用了另一个方法, 趁热打铁, 让我们来看一下这个方法.
class Flask(_PackageBoundObject):
...
def wsgi_app(self, environ, start_response):
ctx = self.request_context(environ)
error = None
try:
try:
ctx.push()
response = self.full_dispatch_request()
except Exception as e:
error = e
response = self.handle_exception(e)
except:
error = sys.exc_info()[1]
raise
return response(environ, start_response)
finally:
if self.should_ignore_error(error):
error = None
ctx.auto_pop(error)
...
这个就是Flask中真正请求的入口了, 在兜里一个圈子后, 最终将两个参数传给了wsgi_app()
这个方法, 并交由它来处理.
所有的请求就将会在这几行代码中处理完成, 并且最终返回. 只要搞懂了这几步, 就能知道Flask是怎么处理请求的啦!
看上去很轻松, 但实际上的步骤还是相当复杂的, 我会慢慢尝试去理解, 一旦有所收获也会第一时间整理并且更新.
如果有什么问题可以私信我, 内容有错误也欢迎大家帮我纠正!
2.4 补充
- 我们在上面只讨论了服务器和应用这两个角色, 其实, 还有另外一个角色叫做中间件middleware, 顾名思义, 它存在于这两者之间. 对于服务器来说, 它是一个应用, 而对于应用来说, 它又是一个服务器, 中间件必须也满足两方的WSGI协议.
- Flask中自带的服务器也是一个WSGI服务器, 只不过它的性能不好, 只能用于测试, 但是原理都是和上面一样的.
- Python的WSGI协议是参考了Java的
servlet
协议. - 在其他语言里面也有类似WSGI协议的规定, 比如Ruby中的Rack, Perl中的PSGI,他们的目的都是一样的.
- uWSGI这个服务器也有自己的协议, 就叫做uwsgi, 但是它也支持WSGI协议. 另外uWSGI是C写的, Gunicorn是Python写的. 这二位应该是在部署的时候的唯二之选.
https://zhuanlan.zhihu.com/p/46983059
参考文章
Python Web开发最难懂的WSGI协议,到底包含哪些内容
吐槽一下: 这个PEP 3333是WSGI协议的第二个版本(1.0.1)了, 第一个版本(1.0.0)记录在PEP 333中. 这个pep命名方式也是很厉害的
连接数据库通过配置文件app.config的更多相关文章
- 配置文件App.config的使用以及Readonly与Const的对比
以前我们学习的时候都把连接数据库的连接字符串写在一个类中,因为我们的数据库都在自己电脑上.如果更换数据库地址,需要更改这个类,然后重新编译才可以连接到数据库.现在我们需要将连接字符串当道一个文件中,然 ...
- C#的配置文件App.config使用总结 - 转
http://blog.csdn.net/celte/article/details/9749389 首先,先说明,我使用的app.config 配置文件的格式如下: <?xml version ...
- C#中怎样获取默认配置文件App.config中配置的键值对内容
场景 在新建一个程序后,项目中会有一个默认配置文件App.config 一般会将一些配置文件信息,比如连接数据库的字符串等信息存在此配置文件中. 怎样在代码中获取自己配置的键值对信息. 注: 博客主页 ...
- winform程序读取和改写配置文件App.config元素的值
winform程序读取和改写配置文件App.config元素的值 2016-05-16 17:49 by newbirth, 2412 阅读, 0 评论, 收藏, 编辑 1 2 3 4 5 6 7 & ...
- C#的配置文件App.config使用总结
应用程序配置文件是标准的 XML 文件,XML 标记和属性是区分大小写的.它是可以按需要更改的,开发人员可以使用配置文件来更改设置,而不必重编译应用程序.配置文件的根节点是configuration. ...
- 配置文件——App.config文件读取和修改
作为普通的xml文件读取的话,首先就要知道怎么寻找文件的路径.我们知道一般配置文件就在跟可执行exe文件在同一目录下,且仅仅在名称后面添加了一个.config 因此,可以用Application.Ex ...
- C#----操作应用程序配置文件App.config
对配置文件的一些疑问: 在应用程序的目录下,有两处值得注意的地方,一个是应用程序根目录下的App.config文件,和bin\debug\name.exe.config 或者 bin\Release\ ...
- 使用.NET Framework的配置文件app.config
在一般的项目中,为了使你的代码更加灵活,更方便调整,减少不必要的hard code,我们都在config中添加许多配置信息,一般可以选择.NET自带的配置文件形式app.config或者web项目中的 ...
- 通过读取配置文件App.config来获取数据库连接字符串
有两种方式://通过读取配置文件来获取连接字符串 第一种方式: App.config 文件的格式: <?xml version="1.0" encoding="ut ...
随机推荐
- 【算法】Logistic regression (逻辑回归) 概述
Logistic regression (逻辑回归)是当前业界比较常用的机器学习方法,用于估计某种事物的可能性.比如某用户购买某商品的可能性,某病人患有某种疾病的可能性,以及某广告被用户点击的可能性等 ...
- [JS Compose] 7. Ensure failsafe combination using monoids
monoids is a semi-group with a neutral element. A semigroup, it does not have an element to return s ...
- 【ichartjs】爬取理想论坛前30页帖子获得每个子贴的发帖时间,总计83767条数据进行统计,生成统计图表
统计数据如下: {': 2451} 图形化后效果如下: 源码: <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//E ...
- 一个异常org.apache.jasper.JasperException: java.lang.IllegalStateException: No output folder:的解决
今天对一个WebApp做完修改,导出成war包,再发布到Tomcat7中,居然访问不了了! 同样的问题一周前也出现过,后来一顿鼓捣,又莫名其妙好了,当时认为是Tomcat7闹点小毛病,也没多想. 但是 ...
- Oracle DB 数据库维护
• 管理优化程序统计信息 • 管理自动工作量资料档案库(AWR) • 使用自动数据库诊断监视器(ADDM) • 说明和使用指导框架 • 设置预警阈值 • 使用服务器生成的预警 • 使用自动任务 数 ...
- UNIX网络编程读书笔记:UNIX域协议
概述 UNIX域协议并不是一个实际的协议族,而是在单个主机上执行客户/服务器通信的一种方法,所用API与在不同主机上执行客户/服务器通信所用的API(套接口API)相同.UNIX域协议可视为进程间通信 ...
- keepalived 使用注意事项
1.启动用service keepalived start/stop 比直接 /sbin/keepalived start/stop要好,貌似解决了master停止了keepalived服务而back ...
- <续>调度算法补充
cpmpute->executors: 1.从storm配置获取<compoent-id,parallelism>集合 2.storm-task-info 获得<task-id ...
- ie 已限制此网页运行脚本或Active控件
ie 已限制此网页运行脚本或Active控件 CreateTime--2018年3月12日16:49:43 Author:Marydon 情景还原: 在本地调试html页,如果其中包含js或fla ...
- Python标准库:内置函数dict(mapping, **kwarg)
本函数是从一个映射函数对象构造一个新字典. 与dict(**kwarg)函数不一样的地方是參数输入是一个映射类型的函数对象,比方zip函数.map函数. 样例: #dict() #以键对方式构造字典 ...