问题详解
目录
- 双写一致性
- 数据库如何处理的?
- 用过什么云产品
- 看看数据库
- celery
- 多线程
- mysql
- 1千万w,性别字段(男女),查询时候,这个字段要不要加索引
- 实现踢下线
- 实现黑名单功能
- 深浅拷贝是什么
- 魔法方法new和init有什么区别
- python的可变和不可变数据类型是什么?
- 什么是生成器,有什么应用场景
- 数据库三大范式
- mysql有哪些索引,分别有什么作用
- 事务的特性
- 事务的隔离级别
- 脏读,不可重复读,幻读 ,mysql5.7以后默认隔离级别是什么?
- 什么是qps,tps,并发量,pv,uv
- 什么是接口幂等性问题,如何解决?
- 什么是gil锁?
- python的垃圾回收机制是什么样的?
- 为什么计算密集型用多进程,io密集型用多线程
- 为什么有了gil锁还要有互斥锁?
- 进程,线程和协程
- 什么是鸭子类型?
- 什么是猴子补丁?有什么用途
- 什么是反射?
- http和https的区别
- 从浏览器输入一个地址,到看到页面信息,经历的过程
- 左连接,右连接,全连接:mysql不能直接支持
- union和union all的区别?
- tcp 三次握手和四次挥手
- osi七层协议,哪七层,每层有哪些,TCP/IP五层结构
- tcp和udp的区别?udp用在哪里了?
- wsgi uwsgi uWSGI,cgi,fastcgi 分别是什么?
- 符合WSGI协议的web服务器
- web服务器到底是什么?服务器中间件
- 如何定制上下文管理器
- python是值传递还是引用传递
- 什么是迭代器,生成器,装饰器
- django的信号用过么?如何用?干过什么?
- Dockerfile用过么,常用命令有哪些
- 并行与并发
- 同步,异步
- 阻塞与非阻塞
- 什么是IPC,如何进行进程间通信
- 正向代理,反向代理
- 什么是粘包
- http协议详情,http协议版本,http一些请求头
- GET请求和POST请求的区别
- 如何实现服务器给客户端发送消息,websocket是什么?用过么?
- 悲观锁和乐观锁,如何实现
- 消息队列
- socket
可变类型与不可变类型
可变类型:列表,字典,集合
不可变类型:字符串,元组,整型,浮点型
常用的魔法方法
python中的魔法方法,定义在类中的一些__开头的方法,在某种情况下会自动触发
__init__:类加括号产生对象,对对象进行初始化的时候触发
__new__:类加括号产生对象,调用元类中的__new__产生一个空对象,这个方法有返回值,再触发__init__对对象进行初始化
__call__:对象加括号的时候自动触发
__getattr__:对象点一个不存在的属性的时候自动触发
__setattr__:对象点属性等于一个值的时候自动触发
__getitem__:对象['属性'],属性不存在的时候自动触发
__setitem__:对象['属性']=值的时候自动触发
上下文管理器:只要重写了__enter__和__exit__方法,就具备这个能力
with 对象 as xx:
1.写了一行行代码,这个时候就自动触发__enter__
2.上下文结束,这里写了一行代码,触发__exit__,做一些资源清理工作
类中的装饰器
-classmethod:绑定给类的方法,类调用的时候会直接将类作为第一个参数传入,对象也可以调用,对象在调用的时候会将产生对象的类,作为第一个参数传入
-staticmethod:类中定义的静态方法,类或者对象调用的时候有几个参数传几个参数
-没有写装饰器的方法,是绑定给对象的方法,对象在调用的时候会把对象作为第一个参数传入
-propty:把方法伪装成属性,这样调用这个方法的时候直接调名字就可以,不用加括号
-类中隐藏方法或者属性:__属性,__方法,但是大家统一默认隐藏写一个_就可以了
双写一致性
出现原因:因为有些接口访问的次数比较频繁,如果每次方法接口都要查询数据库,那么对资源来说是一种浪费也比较占用数据库资源,特别是当并发比较高的时候会造成数据库的崩溃,所以这些接口访问的资源我们可以放到redis缓存数据库中,每次访问这个接口资源直接去redis缓存数据库中拿就可以啦,但是这样如果我们对mysql数据库进行数据的新增或者删除,而接口访问的是redis数据库,这样就会有一个数据的误差
解决办法:
1.定时更新redis缓存数据库
2.修改数据后,删除缓存数据
3.修改数据,更新缓存
我们使用的是使用celery开启一个定时任务来进行数据的更新,虽然会有一定的延迟性,但是对于图片的接口影响不会太大
断点续传
上传途中如果中断会在本地生成一个保存中断位置的文件,下次上传的时候直接读取中断位置文件基于该位置继续上传
内网穿透
内网穿透,即内网映射,内网ip端口映射到公网,让外网访问内网的实现
可以采取的办法:
开源的:frp,ngrok
收费的:花生壳,神卓互联
缓存击穿
http与https
-ssl认证
-http和https的区别
-https=http+ssl
-http版本区别:
-0.9:底层基于tcp,每次http请求,都是建立一个tcp连接,三次卧室,请求结束需要4次挥手
-1.1:请求头中有个参数Keep-alive,可以保证多个http请求共用一个TCP连接
-2.x:多路复用,多个请求使用同一个数据包
-http请求头:x-forword-for,user-agent,cookie,referer,contenType
-http:
-请求协议
-请求首行:请求头地址,请求方式,http的版本
-请求头:key-value,x-forword-for,user-agent,cookie,referer,contenType
-请求体
-响应协议:
-响应首行:响应状态码,响应字符串描述
-响应头:key-value,响应状态码,cookie
-响应体
数据库如何处理的?
-云数据库:阿里云数据库,花钱买服务---获取账号密码---》公司不需要自己配置
-mysql
-redis
-mongodb
-自己的数据库,部署在云服务器上的数据库,是自己的
用过什么云产品
-阿里云的ecs,也就是阿里云的服务器
-阿里云的oss,对象存储
-阿里云的云短信
-七牛云,文件存储
看看数据库
-配置文件dev.py 连接的是本地的127.0.0.1
-上线只是给一个地址,端口,用户名和密码
-上线的数据库服务和项目服务,是在同一台机器
celery
-异步任务
-定时任务
-延迟任务
多线程
-怎么用的,两种方式?
-在爬虫的时候使用,类实例化得到对象Thread类,传入target任务函数,对象.start
-写一个类,继承Thread,重新run方法,写任务,类实例化得到对象
-io密集型用多线程,计算密集型使用多进程---》只针对cpython
mysql
唯一索引和联系索引:
1千万w,性别字段(男女),查询时候,这个字段要不要加索引
因为他就两种状态,联机索引是没有用的,即便建立索引,也不会走
实现踢下线
session机制---》表中把它那条记录删除
-token机制
-下线人id---》放个位置
-进入到认证类中---》
实现黑名单功能
-建立个黑名单表
-id,用户id,ip,时间
深浅拷贝是什么
深浅拷贝是对数据进行复制
对于不可变类型来说深浅拷贝都是一样的重新拷贝一份数据
对于可变来兴来说如果内部嵌套的还有不可变类型
浅拷贝只拷贝第一层,嵌套的拷贝的是内存地址
深拷贝是完全再复制一份出来
魔法方法new和init有什么区别
__new__产生一个空对象,有返回值,返回值是空对象
__init__给new产生的对象设置初始值,独有的属性
python的可变和不可变数据类型是什么?
值改变内存地址不变是可变类型:列表,集合,字典,文件对象
值改变内存地址改变是不可变类型:整型,浮点型,字符串,元组,布尔
什么是生成器,有什么应用场景
当函数体代码有yeild关键字的时候,函数名加括号不会执行函数体代码,而会变成一个生成器,生成器对象点__next__执行
应用场景:比如说在进行文件操作时,如果文件过大,一次性打开会占用过多的内存空间可能会造成内存溢出,使用生成器用多少拿多少,减少内存消耗
数据库三大范式
第一范式是指数据库表的每一列都是不可分割的
第二范式:如果表是单主键,那么主键以外的列必须完全依赖于主键,如果表是复合主键,那么主表以外的列必须完全依赖于主键,不能依赖主键的一部分
第三范式:表中的非主键列必须和主键直接相关而不能间接相关,也就是说非主键列之前不能相关依赖,不存在传递依赖
mysql有哪些索引,分别有什么作用
聚簇索引(聚集索引,主键索引,主键如果不存在,隐藏一个主键)
辅助索引(普通索引,index)
唯一索引(unique)
联合索引(组合索引,多列索引)
事务的特性
原子性:数据库把'要么全做,要么全不做'的这种规则称为原子性
隔离性:事务之前互相隔离,不受影响,这与事务的隔离级别密切相关
一致性:事务执行前后的状态要一直,可理解为数据一致性
持久性:事务完成之后,它对数据的修改是永恒的,即使出现故障也能够正常保持
事务的隔离级别
# READ UNCOMMITTED:未提交读(读未提交)
# READ COMMITTED:已提交读(读已提交)
# REPEATABLE READ:可重复读
# SERIALIZABLE:可串行化
脏读,不可重复读,幻读 ,mysql5.7以后默认隔离级别是什么?
-脏读:指的是读到了其他事务未提交的数据,未提交意味着这些数据可能会回滚,也就是最终不会存到数据库中,也就是不存在的数据,读到了并一定最终存在的数据,就是脏读
-不可重复读:指的是在一个事务内,最开始督导的数据和事务结束前的任意时刻读到的同一批数据出现不一致的情况
-幻读:并不是说两次读取获取的结果集一同,幻读侧重的方面是某一次select操作得到的结果所表征的数据状态无法支撑后续的业务操作,更为具体一些:select某记录是否存在,不存在,准备插入记录,但执行insert时发现此记录已存在,无法插入,此时就发生了幻读
事务的隔离级别能解决的问题:
-读未提交:可以解决脏写的问题,解决了数据丢失,但是会出现脏读
-读已提交:解决了更新丢失和脏读的问题,但是会出现不可重复读的问题
-可重复读:解决了更新丢失,脏读,不可重复读,但是还会出现幻读的问题
-串行化:可以避免脏读,不可重复读,幻读,但是mysql执行效率会比较低
mysql5.7以后默认隔离级别是:可重复读
什么是qps,tps,并发量,pv,uv
-qps:Queries Per Second:每秒查询率,一台服务器每秒能够响应的查询次数
-tps:Transactions Per Second:是每秒处理的事务数,包括一条消息入和一条消息出,加上一次用户数据库访问
-并发量:系统同时能处理的请求数量
-PV(Page View):页面访问量,即页面浏览量或点击量,用户每次刷新即被计算一次,可以统计服务一天的访问日志得到
-UV(Unique Visitor):独立访客,统计1天内访问某站点的用户数,可以统计服务一天的访问日志并根据用户的唯一标识去重得到
-DAU(日活)DAU(Daily Active User):日活跃用户数量,常用语反映网站、app、网游的运营情况,DAU通常统计一日(统计日)之内,登录或使用了某个产品的用户数(去除重复登录的用户),与UV概念类似
-MAU(Month Active User):月活跃用户数量,指网站、app等去重后的月活跃用户数量
什么是接口幂等性问题,如何解决?
-幂等:幂等(idempotent、idempotence)是一个数学与计算机学概念
-一个幂等操作的特点是其任意多次执行所产生的影响均与一次执行的影响相同
-接口幂等性:无论调用多少次,产生的效果是一样的
-get请求:获取数据天然幂等
-put请求:修改数据天然幂等
-delete请求:删除,天然幂等
-post请求:新增数据,会出现不幂等的情况,要把它做成幂等性的
-解决:
1.唯一主键unique 新增用户用户名唯一,但是订单问题解决不了
2.token机制,随机字符串,在使用新增之前,先生成一个随机字符串token,发到前端一份(后端保存一份),只要调用新增接口,就必须懈怠随机字符串token,到后端,校验这个token在不在我们保存的里面,如果在,说明正常,删除token,允许新增,如果不在就不允许新增
3.前端:按钮只能点击一次
什么是gil锁?
-全局解释器锁,它的本质就是一个大的互斥锁,它是cpython的一个机制,gil锁只存在cpython解释器中,它限制了一个线程只有获取到gil锁,才能执行,如果没有拿到gil锁,线程是不能执行的
-解释器不仅仅只有cpython,pypy,jpython
-gil锁的作用是什么?
-限制线程只有获得它才能执行
-为什么要有gil锁?
-保证数据安全?互斥锁就可以了,gil锁不能保证数据安全
-python的垃圾回收机制,需要垃圾回收线程来做,同一时刻,有多个线程同时执行,垃圾回收可能会把正在其他线程使用的变量给回收掉,因为当时只有单核电脑,本来同一时刻就不能有多个线程同时执行,于是作者就干脆做了一个gil锁,让线程必须获得gil锁才能执行,但是后来随着多核的出现,导致python的多线程并不能利用多核
python的垃圾回收机制是什么样的?
-高级一点的语言,为了保证内存的使用效率,都会有垃圾回收的机制,而python使用下面三种方式来做垃圾回收
-引用计数
-有多少个变量指向它,它的引用计数就为几,当引用计数为0,说明没有变量指向它,这块内存空间就会被回收掉
-引用计数存在的问题:循环引用问题
-标记清除
-为了解决应用计数存在的循环引用问题
-第一阶段是标记阶段,它会把所有的活动对象打上标记,第二阶段是把那些没有标记的对象[非活动对象]进行回收
-它会把循环引用的内存空间,打上标记,然后回收掉
-分代回收
-把对象分为三代,一开始,对象在创建的时候,放在一代中,如果在一次一代的垃圾回收检查中,该对象存活下来,就会被防到二代中,同理在一次二代的垃圾检查中,该对象存活下来,就会被放到三代中,后面优先检查第一代中的对象,优先回收,其次依次网上检查做回收
为什么计算密集型用多进程,io密集型用多线程
-由于GIL锁的存在,几遍是多核机器,同一时刻,也只能有一个线程在执行
-线程需要cpu去调度执行
-如果开了多线程,是计算密集型,计算是消耗cpu,假设是四核电脑,不能充分利用这四个核,只能有一个核在执行,开多线程没有用
-而如果计算密集型,开了多进程,GIL是在cpython解释器进程中的,在进程中开启线程执行计算,可以充分利用多核优势
-开了一个进程,就开了一个cpython解释器的进进程,就会有一个gil锁
-由于GIL锁的存在,如果是计算密集型,开启多线程,不能利用多核优势
-开启多进程,可以利用多核优势
-io不消耗cpu,开启多线程,在同一时间短内是有并发效果的
-几遍是io密集型,用多进程是不是会显著提高效率?
本身开启进程是非常消耗资源的,如果是io密集型,没有必要开多进程,并不会有显著提高效果
为什么有了gil锁还要有互斥锁?
GIL:全局解释器锁,线程要执行,必须先获得gil锁,才能执行
互斥锁:为了保证多线程并发操作数据(变量)而设置的锁,保证在加锁和释放锁之间,其他线程不能操作,gil本质也是大的互斥锁
# 出现了数据错乱,出现了多条线程操作变量,出现的并发安全问题
a=0
线程1要计算: a+=1
1 线程1 拿到gil
2 读取a=0
3 假设时间片到了,释放gil,释放cpu
4 等待下次被调度执行
10 轮到它了,获取gil锁
11 继续往下执行:计算a+1
12 把结果赋值给a ,a=1
13 释放gil锁
线程2要计算: a+=1
5 线程2获得了gil锁
6 读取a=0
7 计算a+1
8 把结果赋值给a ,a=1
9 释放gil锁
# 什么临界区?处出现并发安全问题的这段代码称之为临界区,临界区会出现并发安全问题,所以要在临界区加锁
# 加锁
6 读取a=0
7 计算a+1
8 把结果赋值给a ,a=1
# 释放锁
# 互斥锁保证数据安全
a=0
线程1要计算: a+=1
1 线程1 拿到gil
# 加锁
2 读取a=0
3 假设时间片到了,释放gil,释放cpu
4 等待下次被调度执行
7 轮到它了,获取gil锁
8 继续往下执行:计算a+1
9 把结果赋值给a ,a=1
10 释放gil锁
线程2要计算: a+=1
5 线程2获得了gil锁
#获取锁,获取不到
6 释放gil锁
11 获得gil锁
#加锁
12 读取a=0
13 计算a+1
14 把结果赋值给a ,a=1
15 释放锁
16 释放gil锁
# gil锁并不锁住临界区,临界区需要我们自己用互斥锁加锁
进程,线程和协程
进程:资源分配的最小单位,一个应用程序运行起来,至少有一个进程,进程管理器中就可以看到一个个进程
线程:cou调度执行的最小单位,一个进程下至少有一个线程
协程:单线程下实现并发,程序层面控制的任务切换
代码如何实现
# 开启多进程两种方式
-1 写一个类,继承Process,重写类的run方法---》实例化得到对象,对象.start 开启了进程
-2 通过Process类实例化得到一个对象,传入任务 ,调用对象.start 开启了进程
你在哪里用过
# 同理开启线程两种方式
-1 写一个类,继承Thread,重写类的run方法---》实例化得到对象,对象.start 开启了进程
-2 通过Thread类实例化得到一个对象,传入任务 ,调用对象.start 开启了进程
你在哪里用过
# 开启协程
- 早期之前:借助于第三方gevent,基于greelet写的
- async 和 await 关键字,不借助于第三方,开启协程 asyncio 包
-必须写在一个函数前, async def task()---->这个函数执行的结果是协程函数
-await 只要是io操作的代码,前面必须加 await
在哪里用过?
-我一般计算密集型用多进程,io密集型用多线程
-闲来无事,爬别人数据喜欢开多线程,io操作居多
-程序中,异步做一件事情,也可以开多线程
-比如一个视图函数,异步的把数据写到文件中
-异步的发送钉钉通知
-异步的发送右键
-实际上,在项目中不需要我们开启进程线程,可以借助于第三方的框架比如celery就可以做异步操作
celery的worker就是进程线程架构
-django框架,是支持并发的,我们没有开启多进程和多线程,但是符合uwgi的web服务器在进入django框架之前,开启了进程和线程来执行视图函数
什么是鸭子类型?
-走路像鸭子,说话像鸭子,我们就可以叫他鸭子
-解释:鸭子类型是python面向对象中描述接口的一个概念,区分与其他编程语言,比如java,实现接口,必须显示的继承一个接口,而python实现接口遵循压力类型,不需要显示的继承一个接口也就是类,只要类中有对应的属性和方法,我们就称这几个类的对象为同一种类型
什么是猴子补丁?有什么用途
-猴子补丁:在程序运行过程中,动态替换的一种技术
场景:
1. 比如json模块,用内置的json,效率低,有一个第三方的ujson模块
-不改变原来程序代码,把程序所有的json都替换成ujson
-在程序运行的入口处,import ujson as json
2.django中pymysql的替换
import pymysql
pymysql.install_as_mysqlDB()
3.gevent---》猴子补丁---》monkey.pach.all()---》动态的替换内部的会阻塞的代码
-time
-socket
-把所有内置的会释放gil锁的模块,动态替换成gevent自己写的,不会释放gil锁的模块
-同步代码:io操作,会阻塞,会释放gil锁,这条线程就会被释放cpu的执行
-动态的把所有同步的代码,都替换成异步代码,把所有会阻塞的代码,都替换
-异步代码,遇到io,不释放gil锁
什么是反射?
反射:是在程序运行过程中通过字符串来操作对象的属性和方法
getattr:获取对象中字符串对应的属性的值或者方法的内存地址
hasattr:判断对象中是否有字符串对应的属性名或者方法名
setattr:将对象中字符串对应的属性名或者方法名修改名字
delattr:删除对象中字符串对应的属性或者方法
http和https的区别
https=http+ssl或者tls
监听端口不一样:80,443
保证了http传输的安全
从浏览器输入一个地址,到看到页面信息,经历的过程
1.在浏览器输入的是:域名--》要做域名解析---》把域名解析成ip地址+端口形式(DNS解析)---》如果解析不到页面就会报错
2.解析完成后,想解析出的域名和端口建立TCP连接,三次握手
3.向某个地址发送http的get请求
4.如果后端是使用nginx转发,nginx把http请求转发给web框架(django,flask)---》django请求生命周期
5.后端服务以http响应的形式返回给客户端浏览器
6.客户端浏览器把http响应体的内容展示在浏览器上,但是http响应还有:响应头,状态码
7.四次回收断开tcp连接:http协议版本
# https://blog.csdn.net/m0_52165864/article/details/126313277
左连接,右连接,全连接:mysql不能直接支持
-数据通常不在同一张表中,这就设计到连表操作,而表间连接方式有很多
-内连接:把两张表共有的数据连接到一起
-左连接:以左表为基准,把左表所有数据都展示,有可能右表没有,用null补齐
-右连接:已右表为基准,把右表所有数据都展示,有可能左表没有,用null补齐
-全连接:以左右两表数据作为基准,左右两表数据都展示,有可能左或表没有,用空补齐
union和union all的区别?
-select 出来结果,union,union all都是对结果进行合并,求并集
-union 会取出重复的数据
-union all 不会去除重复的数据
select name,id form user;
id name
1 lqz
2 zs
select name,id form book;
id name
1 lqz
2 西游记
select name,id form user union all select name,id form book;
id name
1 lqz
1 lqz
2 zs
2 西游记
tcp 三次握手和四次挥手
-TCP是可靠链接,使用三次握手,四次挥手保证了可靠链接,数据不会丢失
-SYN:SYN=1 表示要建立链接
-FIN:表示断开链接
-ACK:ACK=1 表示收到了,允许
-seq:随机数,建立连接无论客户端还是服务端要建立连接就要懈怠
-ack:回应请求就要加1返回
-三次握手:
-第一次:喂(SYN=1),我是lqz(seq=随机数)
客户端:SYN_SEND状态
服务端:没收到:listen 状态,收到了是:SYN_RCVD状态
-第二次:收到(ACK=1),lqz啊(ack=随机数+1),喂(SYN=1),我是刘亦菲(seq=随机数1)
服务端:SYN_RCVD状态
客户端:没收到服务端返回的第二次:SYN_SEND状态,一旦收到就是established
-第三次:收到(ACK=1),刘亦菲你好(ack=随机数1+1)
客户端:连接建好的状态 established
服务端:收到后,处于established
大白话,三次握手:
第一次:客户端向服务端发送建立连接请求【携带一个随机数】(SYN=1,seq=随机数)
第二次:服务端回应客户端的建立连接请求(ACK=1,ack=随机数+1),服务端向客户端发送建立连接请求(SYN=1,seq=另一个随机数)
第三次:客户端回应服务端的建立连接请求(ACK=1,ack=另一个随机数+1)
第四回收:
第一次:客户端向服务端发起断开连接的请求(FIN=随机数)
第二次:服务端收到后,回复这个请求,同意断开连接(ACK=1,ack=随机数+1)
第三次:服务端确认没有数据发送,向客户端发起断开连接的请求(FIN=另一个随机数)
第四次:客户端收到后,回复这个请求,断开与服务端的连接(ACK=1,ack=另一个随机数+1)
洪水攻击:
同一时间有大量的客户端请求建立连接,会导出服务端一直处于SYN_RCVD状态,服务端接收到了大量的syn请求,处于rcvd状态
osi七层协议,哪七层,每层有哪些,TCP/IP五层结构
-osi七层:应用层 表示层 会话层 传输层 网络层 数据链路层 物理层
-五层结构:应用层(三个合成一个) 传输层 网络层 数据链路层 物理连接层
-应用层 表示层 会话层(应用层)
-应用层协议:http,ftp,https,dns
-表示层:https=http+ssl或者tls 的加密
-会话层:负责建立、管理和终止表示层实体之间的会话连接
-传输层:
-tcp协议
-udp协议
-端口:端口协议
-网络层:
-ip地址协议
-数据链路层:
-mac地址:以太网协议
-数据帧:电信号的分组方式
-物理层:
-物理介质
-网线
tcp和udp的区别?udp用在哪里了?
-tcp:面向连接的可靠协议
-udp:无连接的不可靠协议
-都是处于传输层
-比如:
udp:一些聊天,dns协议用的udp协议
tcp:http mysql,redis客户端服务端通信
wsgi uwsgi uWSGI,cgi,fastcgi 分别是什么?
CGI:通用网关接口(Common Gateway Interface/CGI),CGI描述了服务器(nginx,apache)和请求处理程序(django,flask,springboot web框架)之间传输数据的一种标准
# 所有的bs架构软件都是遵循CGI协议的
# 一句话:CGI是一个标准,定义了客户端服务器之间如何传数据
Fastcgi:快速通用网关接口(Fast Common Gateway Interface/FastCGI),是一种让交互程序与Web服务器通信的协议。FastCGI是早期通用网关接口(CGI)的增强版本
# FastCGI致力于减少网页服务器与CGI程序之间互动的开销,从而使服务器可以同时处理更多的网页请求
#常见的fastcgi服务器:Apache,Nginx,Microsoft IIS
# CGI的升级版
WSGI:Python Web Server Gateway Interface,缩写为WSGI,python定义的web服务器与web应用程序或框架之间的一种通信接口
总结:WSGI为python定义的web服务器和web框架之间的接口标准
uWSGI:符合WSGI协议的web服务器,用c语言写的,性能比较高,咱们通常用来步骤django,flask
总结:uWSGI是web服务器,符合WSGI协议的服务器,处理发来的请求以及返回响应
uwsgi:uWSGI服务器直线的独有协议,用于定义传输信息的类型,是用于前端服务器与uwsi的通信规划
总结:是uWSGI自由的一个协议
uWSGI:web服务器,等同于wsgiref
uwsgi:uWSGI自有的协议
符合WSGI协议的web服务器
# 符合WSGI协议的web服务器
wsgiref,werkzeug(一个是符合wsgi协议的web服务器+工具包(封装了一些东西))
uWSGI 用c语言写的,性能比较高
gunicorn:python写的
tornado:也可以部署django项目
web服务器到底是什么?服务器中间件
客户端(浏览器、app) 跟 服务端(web框架)之间的东西,服务器中间件
# nginx apache 是一类东西,就是做请求转发
# uWSGI,gunicorn 只针对于python的web框架
# tomcat,jboss,weblogic 只针对java的web框架
如何定制上下文管理器
一个对象无果实现__enter__和__exit__方法,那么这个对象就支持上下文管理协议,即with语句
上下文管理协议适用于那些进入和退出之后自动执行一些代码的场景,比如文件,网络连接,数据库连接或使用锁,使用事务的编码场景
# 如何使用
class ComonSession:
def __enter__(self):
print('进入with语句块时执行此方法,此方法如果有返回值会赋值给as声明的变量')
self.session=session
return self.session
def __exit__(self, exc_type, exc_val, exc_tb):
self.session.close()
with ComonSession() as session:
session.filter()
print('上下文管理器结束了')
python是值传递还是引用传递
严格意义来说,python既不是值传递,也不是引用传递,python是自己的传递方式,规则是
如果传递的不可变类型,在函数中修改,就不会影响原来的变量
如果传递的是可变类型,在函数中修改,不会影响原来的变量,修改,而不是重新赋值
python一切皆对象---》内部函数是一切皆引用(对象的本质就是地址,就是引用)
什么值?什么是引用?
-值就是一个变量--》具体的值(一块内存空间放着这个变量的值)
-引用是一个变量=地址(内存地址指向了值)
什么是值传递?什么是引用传递?
-如果是值传递,函数中修改了传递的值,不会影响原来的
-如果是引用传递,函数中修改了传递的引用,就会影响原来的
什么是迭代器,生成器,装饰器
迭代:一种不依赖于索引的取值方式,我们不需要关注它的位置,只能够一个个取值
迭代取值跟索引取值的区别:迭代取值只能够从头到尾一个个取值,索引取值可以按照某个位置取值
可迭代对象:可以迭代的(for,next取值的)python中的对象称为可迭代对象
迭代器:可迭代对象调用__iter__,就得到了迭代器对象,迭代器有__iter__和__next__方法
自定义迭代器:写个类,类中重写__iter__和__next__方法,这个类就是迭代器
生成器:生成器的本质也是迭代器
函数体代码只要有yield关键字,这个函数加括号调用的时候并不会执行函数体代码,而是变生成器
生成器表达式:也可以做出生成器(i+1 for i in [1,2,3,4])
比如一堆数据,要放到列表中,但是你没放,放到了生成器中
for i in range(10)
装饰器:装饰器的本质是一个闭包函数,作为是在不改变被装饰对象源代码和调用的方式的基础上,为他添加新的功能
flask的路由就是基于装饰器
django的信号也可以用装饰器方式注册
django局部去除csrf认证
为接口记录访问日志
django的信号用过么?如何用?干过什么?
django提供的一种通知机制,他是设计模式观察者模式(发布订阅),在发生某种变化的时候,通知某个函数执行
内置信号:如果是内置信号用起来简单,只需要写个函数,跟内置信号绑定,当信号被处罚,函数就会执行
-绑定信号:在django中有两种方式
@receiver
connect
自定义信号:就比内置信号多了两步:1.定义信号 2.触发信号
Dockerfile用过么,常用命令有哪些
-是一些列脚本组成的文件,用来构建镜像
-FORM
-RUN
-ENV
-WORKDIE
并行与并发
并发:同一时间段内,执行多个任务的能力
并行:同一时刻,执行多个任务的能力
并发必须是多cpu支持的
同步,异步
这两者表示程序调度的角度
同步:同步是一件事一件事的做,只有执行完前一个任务,才会执行下一个任务,同步意味着有序
异步:当一个任务已经执行了,无序等待该任务执行完成,就可以切换到另一个任务上,异步意味着无序
阻塞与非阻塞
这两者表示程序执行的角度
阻塞:程序在等待某个操作完成期间,自身无法继续干别的事情,则该程序在该操作上是阻塞的
非阻塞:程序在等待某操作过程中,自身不被阻塞,可以继续运行干别的事情,则称该程序咋该操作上是非阻塞的
# https://zhuanlan.zhihu.com/p/621717027
什么是IPC,如何进行进程间通信
-IPC:Inter-Process Communication,进程间通信
-两种情况:
-在同台机器上的两个进程通信
-不同的机器上的两个进程进行通信
如何通信:
-python queue可以做进程间通信
-消息队列:redis就可以做消息对象,rabbitmq,kafka
-socket套接字:(展现形式:1 服务和服务之间通过接口调用 2 RPC调用:远程过程调用)
正向代理,反向代理
-正向代理:代理的客户端 VPN 爬虫代理池
-反向代理:代理的是服务端 nginx
什么是粘包
-因为TCP是流式协议,tcp客户端发送的多个数据包就会像水流一样流向TCP服务端,多个数据包就会粘在一起,区分不开是几个数据包,造成粘包现象
-1 每个包设置结束标志 http协议采用这种 /r/n/r/n
-2 每个包设置固定大小的头,头中包含包的大小
http协议详情,http协议版本,http一些请求头
特点:
1.基于请求响应--》服务端不能主动给客户端推送消息
2.无状态无连接---》不能做会话保持---》才出现了cookie,session,token
3.基于tcp之上的应用层协议
详情:
-请求协议:
请求首行,请求方式(get,post,delete),请求地址,请求http协议版本号/r/n
请求头: key:value (cookie,useragent,referer,x-forword-for)
请求体:编码方式
-响应协议:
响应首行:响应状态码(1xx,2xx),响应单词描述
响应头:key:value 跨域问题的响应头
响应体:html格式:浏览器中看到的,json格式给客户端使用
协议版本:
-0.9:http协议的最初版本,功能简陋,仅支持请求方式GET,并且仅能请求访问HTML格式的资源
-1.0:工作方式是每次TCP连接只能发送一个请求(默认短连接),当服务器响应后就会关闭这次连接,下一个请求需要再次建立TCP连接,就是不支持keep-alive
-1.1:引入了持久连接,即TCP连接默认不关闭,可以被多个请求复用,不用声明Connection:keep:alive,客户端和服务端发现对方一段时间没有活动,就可以主动关闭连接
-2.0:多路复用:对于 HTTP/1.x,即使开启了长连接,请求的发送也是串行发送的,在带宽足够的情况下,对带宽的利用率不够,HTTP/2.0 采用了多路复用的方式,可以并行发送多个请求,提高对带宽的利用率
-3.0:HTTP3.0又称为HTTP Over QUIC,其弃用TCP协议,改为使用基于UDP协议的QUIC协议来实现
GET请求和POST请求的区别
post更安全(不会作为url的一部分)
post发送的数据更大(get有url长度限制)
post能发送更多的数据类型(get只能发送ASCII字符)
post比get慢
post用于修改和写入数据,get一般用于搜索排序和筛选之类的操作
如何实现服务器给客户端发送消息,websocket是什么?用过么?
websocket:一种通信协议,区别在于http协议,可在单个TCP连接上进行全双工通信,允许服务端主动向客户端推送数据,浏览器和服务器只需要完成一次TCP连接,两者之间就可以建立之就行的连接,并进行双向数据传输
-没有跨域问题
-应用场景:
-只要设计到服务端主动给客户端发送消息的场景,都可以用
-实时聊天
-服务端发生变化,主动向客户端推送一个通知
-监控服务端的内存使用情况
-django中使用channles模块实现
-https://zhuanlan.zhihu.com/p/371500343
悲观锁和乐观锁,如何实现
-无论是悲观锁还是乐观锁,都是人们定义出来的概念,仅仅是一种思想,与语言无关
-并发控制:
1.当程序中出现并发问题时,就需要保证在并发情况下数据的准确性,以此确保当前用户和其他用户一起操作时,所得到的结果和他单独操作时的结果是一样的,这种手段就叫做并发控制
2.没有做好并发控制,就可能导致脏读、幻读、不可重复读等问题
-悲观锁:
悲观锁:当要对一条数据进行修改的时候,为了避免同时被其他人修改,最好的方法就是直接怼该数据进行加锁以防止并发,让并行变成串行
这种借助数据库锁机制,在修改数据之前先锁定,再修改的方式被称之为悲观并发控制
之所以叫悲观锁,是因为这是一种对数据的修改持有悲观态度的并发控制方式,总之假设最坏的情况,每次读取数据的时候都默认其他线程会更改数据,因此需要进行加锁操作,当其他线程想要访问数据时,都需要阻塞挂起
-悲观锁实现方式:
1.传统的关系型数据库使用这种锁机制,比如行锁,表锁等,读锁,写锁等,都是在做操作之前先上锁
2.编程语言中的线程锁,比如python的互斥锁
3.分布式锁:redis实现的分布式锁等
-乐观锁:
通过程序实现(没有真正的一把锁),不会产生死锁
总是假设最好的情况,每次去拿数据的时候都认为别人不会修改,所以不会上锁,只在更新的时候判断一下在此期间别人有没有去更新这个数据,如果更新了,我们就不做修改
-乐观锁实现方案:
1 CAS(Compare And Swap) 即比较并交换。是解决多线程并行情况下使用锁造成性能损耗的一种机制,CAS 操作包含三个操作数——内存位置(V)、预期原值(A)和新值(B),执行CAS操作的时候,将内存位置的值与预期原值比较,如果相匹配,那么处理器会自动将该位置值更新为新值,否则,处理器不做任何操作
CAS会出现ABA问题,比如,你看到桌子上有100块钱,然后你去干其他事了,回来之后看到桌子上依然是100块钱,你就认为这100块没人动过,其实在你走的那段时间,别人已经拿走了100块,后来又还回来了。这就是ABA问题
解决ABA问题:既然有人动了,那我们对数据加一个版本控制字段,只要有人动过这个数据,就把版本进行增加,我们看到桌子上有100块钱版本是1,回来后发现桌子上100没变,但是版本却是2,就立马明白100块有人动过
2 版本号控制:在数据表中增加一个版本号字段,每次更新数据时将版本号加1,同时将当前版本号作为更新条件,如果当前版本号与更新时的版本号一致,则更新成功,否则更新失败
3 时间戳方式:在数据表中增加一个时间戳字段,每次更新数据时将时间戳更新为当前时间戳,同时将当前时间戳作为更新条件,如果当前时间戳与更新时的时间戳一致,则更新成功,否则更新失败
悲观锁乐观锁使用场景
-并发量:如果并发量不大,可以使用悲观锁解决并发问题,但如果系统的并发非常大,悲观锁会带来非常大的性能问题,建立乐观锁
响应速度:如果需要非常高的响应速度,建议采用乐观锁方案,成功就执行,不成功就失败,不需要等待其他并发去释放锁,乐观锁并未真正加锁,效率高
冲突频率:如果冲突频率非常高,建立采用悲观锁,保证成功率,冲突频率大,选择乐观锁会需要多次重试才能成功,代价比较大
重试代价:如果重试代价大,建立采用悲观锁,悲观锁依赖数据库锁,效率低,更新失败的概率比较低
读多写少:乐观锁适用于读多写少的应用场景,这样可以提高并发粒度
#django中使用:下单,秒杀场景
# django中如何开启事务
-全局开启:每个http请求都在一个事务中
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'NAME': 'lqz',
'HOST': '127.0.0.1',
'PORT': '3306',
'USER': 'lqz',
'PASSWORD': 'lqz123',
#全局开启事务,绑定的是http请求响应整个过程
'ATOMIC_REQUESTS': True,
}
}
-每个视图函数开启
from django.db import transaction
@transaction.atomic
def seckill(request):
-局部开启
from django.db import transaction
def seckill(request):
with transaction.atomic():
pass
return HttpResponse('秒杀成功')
-保存点,回滚保存点Savepoint
-设置回滚点:sid = transaction.savepoint()
-提交回滚点:transaction.savepoint_commit(sid) transaction.commit()
-回滚到回滚点:transaction.savepoint_rollback(sid) transaction.rollback()
- 事务提交后回调函数
transaction.on_commit(send_email)
#django中使用悲观锁
#django中使用乐观锁
消息队列
消息队列是一种基于异步通信方式的分布式通信方式,他是一种将多个应用程序之间的消息存储和传递机制,通过底层的消息传递协议,让不同的应用通过队列交换数据,以下几个特点:
1.解耦
2.可靠性
3.可维护性
4.异步
5.可扩展性
socket
socket套接字,是一种网络通信的数据结构,他定义了通信协议。ip地址、端口号三个重要的网络资源,套接字可以实现不同主机之间的进程间通讯或者同一主机上不同进程之间的通讯