flask之蓝图的使用-g对象-数据库连接池

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个线程才能打满一个核心 从而多线程也有并行的效果

posted @ 2023-04-04 21:24  lsumin  阅读(117)  评论(0编辑  收藏  举报