flask之蓝图的使用-g对象-数据库连接池
昨日内容补充
# session执行流程
open_session:前端写到cookie到后端 后端取出cookie对应的value值 解密 转到session对象中 后续再到视图函数中 使用session即可
save_session:请求走的时候 校验session有没有被改过 如果被改了 删除cookie 重新设置cookie
session用起来像字典---> 如何做 一个对象可以像字典一样使用?
__getitem__ __setitem__ 只要调用__setitem__就说明动了 session内部有一个对象属性modify---> 默认为false 触发了__setitem__ 置为true 后期只要判断modify 就可以判断session有没有被改过
今日内容详细
1 蓝图的使用
# blueprint 翻译过来的 称之为蓝图
# 作用是:之前全在一个py中写flask项目 后期肯定要划分目录
# 不用蓝图 划分目录
no_blueprint_flask # 项目名
src #核心源码位置
__init__.py # 包 里面实例化得到了app对象,
models.py #放表模型
views.py # 放视图函数
static # 放静态资源
templates # 放模板
home.html # 模板
manage.py # 启动文件
# 蓝图的使用步骤
第一步:导入蓝图类
from flask import Blueprint
第二步:实例化得到蓝图对象
bp = Blueprint('user', __name__)
第三步:在app中注册蓝图
app.register_blueprint(bp)
第四步:在不同的views.py 使用蓝图注册路由
@bp.route('/login')
补充:蓝图可以有自己的静态文件和模板
补充:注册蓝图时 可以使用前缀 必须以/开头
# 使用蓝图 划分小型项目目录
little_blueprint # 项目名
-src # 核心代码
-static # 静态文件
-1.jpg # 图片
-templates # 模板文件
-user.html # 模板
-views # 视图函数存放位置
-order.py # 订单相关视图
-user.py # 用户相关视图
-__init__.py # 包
-models.py # 表模型
-manage.py # 启动文件
# 使用蓝图 划分大型项目目录 多个app 像django一样
big_blueprint # 项目名
-src # 核心文件
-admin # admin的app
-static # 静态文件
-1.jpg # 图片
-templates # 模板文件目录
-admin_home.html # 模板文件
-__init__.py # 包
-models.py # 表模型
-views.py # 视图函数
-home # home app
-order # orderapp
-__init__.py # 包
-settings.py # 配置文件
-manage.py # 启动文件
2 g对象
# g 对象是什么?
global的缩写 在python中是个关键字 不能以关键字作为变量名 干脆用了g
g 对象 在整个请求的全局 可以放值 可以取值
全局变量 在任意位置导入使用即可
它为什么不学django使用request作为上下文?
因为使用request 可能会造成request数据的污染 不小心改了request的属性 但你不知道
建议使用g 因为g是空的 放入之后在当次请求中全局优先
# 以后想在当次请求中 放入一些数据 后面使用 就可以使用g对象
# g和session有什么区别?
g 是只针对于当次请求
session针对于多次请求
from flask import Flask, g, request
app = Flask(__name__)
app.debug = True
@app.before_request
def before():
if 'home' in request.path:
g.xx = 'xx'
def add(a, b):
# print('---',g.name)
print('---', request.name)
return a + b
@app.route('/')
def index():
print(g.xx)
name = request.args.get('name')
# g.name = name
request.method = name
res = add(1, 2)
print(res)
return 'index'
@app.route('/home')
def home():
print(g.xx)
'''
访问根路径g的name为zpf
访问hoem路径g的xx为xx
'''
3 数据库连接池
# flask 操作mysql
使用mysql
在视图函数中 创建pymysql的连接 查数据 查完 返回给前端
有什么问题?来一个请求 创建一个连接 请求结束 连接关闭 (django就是这么做的)
把连接对象 做成全局的 在视图函数中 使用全局的连接 查询 返回给前端
有什么问题?会出现数据错乱,详见下图
# 解决上面的两个问题
数据库连接池
创建一个全局的池
每次进入视图函数 从池中去一个连接使用 使用完放回到池中 只要控制池的大小 就能控制mysql连接数
# 使用第三方数据库连接池 使用步骤
1 安装 pip install dbutils
2 使用:实例化得到一个池对象
3 在视图函数值导入使用
conn = pymysql.connect(
user='',
password='',
host='',
database='',
port=3306
)
cursor = conn.cursor(pymysql.cursors.DictCursor) # 将返回数据转成字典格式
cursor.execute('select id, title, tags from question limit 2') # sql查询语句
res = cursor.fetchall() # 获取所有
print(res)
return jsonify(res)
# 不带池的代码
from flask import Flask, jsonify
import pymysql
app = Flask(__name__)
app.debug = True
@app.route('/question')
def question_detail():
conn = pymysql.connect(
user='',
password='',
host='',
database='',
port=3306
)
cursor = conn.cursor(pymysql.cursors.DictCursor) # 将返回数据转成字典格式
cursor.execute('select id, title, tags from question limit 2') # sql查询语句
res = cursor.fetchall() # 获取所有
print(res)
return jsonify(res)
if __name__ == '__main__':
app.run()
# 不带池的代码
from flask import Flask, jsonify
import pymysql
from POOL import pool
app = Flask(__name__)
app.debug = True
# 带池的代码
@app.route('/question_pool')
def question_poll():
conn = pool.connection()
cursor = conn.cursor(pymysql.cursors.DictCursor)
cursor.execute('select id, title, tags from question limit 2')
res = cursor.fetchall()
print(res)
return jsonify(res)
if __name__ == '__main__':
app.run()
# POOL
from dbutils.pooled_db import PooledDB
import pymysql
pool = PooledDB(
creator=pymysql, # 使用链接数据库的模块
maxconnections=10, # 连接池允许的最大连接数 0和None表示不限制连接数
mincached=2, # 初始化时 链接池中至少创建的空闲的链接 0表示不创建
maxcached=5, # 链接池中最多闲置的链接 0和None不限制
maxshared=3, # 链接池中最多共享的链接数量 0和None表示全部共享 PS:无用 因为pymysql和MySQLdb等模块的 threadsafety都为1 所以值无论设置为多少 maxcached永远为0 所以永远是所有链接都共享
blocking=True, # 连接池中如果没有可用连接后 是否阻塞等待 True=等待 False=不等待然后报错
maxusage=None, # 一个链接最多被重复使用的次数 None表示无限制
setsession=[], # 开始会话前执行的命令列表 如["set datestyle to ...", "set time zone ..."]
ping=0, # ping MySQL服务端 检查是否服务可用 如:0=None=never 1=default=whenever it is requested 2=when a cursor is created 4=when a query is executed 7=always
user='',
password='',
host='',
database='',
port=3306,
charset='utf8'
)
# 压力测试代码
from threading import Thread
import requests
def task():
res = requests.get('http://127.0.0.1:5000/article_pool')
print(len(res.text))
if __name__ == '__main__':
for i in range(500):
t = Thread(target=task)
t.start()
# 效果是:
使用池的连接数明显小
不使用池的连接数明显很大
# 查看数据库连接数
show status like 'Threads%'
补充
1 什么是gil锁 有什么作用
# 1 首先明确GIL并不是Python的特性 它是在实现Python解析器(CPython)时所引入的一个概念
# 2 GIL:全局解释器锁。每个线程在执行的过程都需要先获取GIL 保证同一时刻 只有一个线程可以执行代码
# 3 当线程遇到I/O 或者字节码执行100行(python2 python3中使用计时器时间达到阈值释放GIL) 才会释放GIL锁。线程的运行仍然是有先后顺序的并不是同时进行
# 4 Python使用多进程是可以利用多核的CPU资源的
# 5 多线程爬取比单线程性能有提升 因为遇到IO阻塞会自动释放GIL锁。多进程可以充分使用cpu的两个内核 而多线程却不能充分使用cpu的两个内核 cpython解释器中存在一个GIL 它的作用就是保证同一时刻只有一个线程可以执行代码 因此造成了我们使用多线程的时候无法实现并行
解决方案
# 1 更换解释器比如使用jpython(java实现的python解释器)(jpython xxx.py)
# 2 使用其他语言(c语言)编写程序
# 3 使用多进程完成多任务的处理
"""
多个进程操作同一份数据的时候 会出现数据错乱的问题 针对上述问题 解决方法就是加锁处理 将并发变成串行 牺牲效率但是保证了数据的安全
扩展:行锁 表锁 注意:
1 锁不要轻易的使用 容易造成死锁现象(我们一般写代码不会用到 都是内部封装好的)
2 锁只在处理数据的部分加起来保证数据安全(只在争抢数据的环节加锁处理即可)
"""
2 python的垃圾回收机制是什么样的
# python的垃圾回收 其实高级的语言都有自己的垃圾回收机制简称GC python当中主要通过三种方式解决垃圾回收的方式---> 引用计数 标记清除 分代回收
# 引用计数:如果有新的引用指向对象>对象引用计数就加一 引用被销毁时>对象引用计算减一 当用户的引用计数为0时 该内存被释放
# 标记清除:首先标记对象(垃圾检测) 然后清除垃圾(垃圾回收)---> 首先初始所有对象标记为白色 并确定根节点对象(这些对象是不会被删除)--->将根节点对象标记为黑色(表示对象有效)。将有效对象引用的对象标记为灰色(表示对象可达 但它们所引用的对象还没检查) 检查完灰色对象引用的对象后 将灰色标记为黑色。重复直到不存在灰色节点为止。最后白色结点都是需要清除的对象
# 分代回收:垃圾回收器会更频繁的处理新对象。一个新的对象即是你的程序刚刚创建的 而一个老的对象则是经过了结果时间周期之后仍然存在的对象。python会在放一个对象从0代移动到1代 或是从1代移动到2代的过程中提升(promote)这个对象。
3 为什么计算密集型用多进程 io密集型用多线程
# 如果我们想要提高计算密集型程序的效率 则需要采取多进程的方式并发 而不能采取多线程的方式 因为python中的多线程实际上是通过交替执行实现的 在同一时刻只有一个线程在执行 只有多进程的并发 才能让几条并行的程序同时在运算
# python多线程 可以粗浅理解只用了cpu的一个核心
# 为什么IO密集型用多线程?假设我们有多个线程都在发网络请求(request,response等) 一个请求从发出到接收的过程中cpu大部分时间都是在等
# 所以 当线程发出请求后 由于不占用cpu资源 可以阻塞等待 然后cpu执行权可以被另外一个线程所享有 去发网络请求
# IO密集型 单个cpu利用率很低 可能只有10% 所以多线程可以提升cpu利用率 可能10个线程才能打满一个核心 从而多线程也有并行的效果