0 公司环境
# 实际工作python编程环境
# 1 框架的选择
-同步 Django、Flask框架
-连接mysql
-操作原生: pymysql
-操作orm: sqlalchemy、peewee(很少使用)
-异步 Sanic、FastAPI框架
# 一旦开了异步,后面所有的框架/库都需要用异步框架
-连接redis:aioredis
-连接mysql
-操作原生: aiomysql # 本质是Asyncio框架+MySQL
-操作orm: GINO
# sqlachemy不支持异步,peewee支持异步(peewee-async,基于peewee)
# Asyncio框架
是用来编写并发代码的库,使用 async/await 语法
asyncio被用作多个提供高性能 Python异步框架的基础
# 2 公司数据库
-公司内部连接mysql
-部署是 内网直接连接mysql服务器
-但开发时,可能会有堡垒机/跳板机进行日志审计,需要先连接到跳板机,再转到服务器
-数据库密码不放在配置文件中,放到环境变量中
# 3 工作流程
# A阶段:项目准备
0.公司配电脑(或给补贴用自己的)
本地系统:windows--远程连接、mac、乌班图
1.搭好开发环境 eg:python解释器、django、redis、mysql等
-注意:区分正式环境、测试环境 # 千万不要去动 正式环境的代码以及数据等!!!
-本地装mysql,redis --->或搞个虚拟机,docker拉起来
redis不支持widows,源码开源,有专业团队负责做出windows平台的软件,但最新版只有3.x
2.自己在本地生成一个ssh公钥,给相关负责人,gitlab或者gitee配进去,对项目有读写权限
3.把项目拉下来,运行起来 # 注意:难装的模块
4.熟悉业务、熟悉项目 找一条完整的线 # 请求-->视图-->缓存-->数据库等等 先走通,再读代码
# B阶段:项目开发 核心:别人咋做,你咋搞
0.在现有项目写接口,按照别人写好的一条线,复制修改代码
1.一定要自我测试
2.提交代码,到项目管理平台 # eg: 禅道
3.当此提交的id号,填到项目管理平台上,点完成
4.每天拉取和提交代码的次数,遵循老员工的次数
5.功能完成
6.代码review (专门有人通读你的代码) # 基本是好一点的公司,刚去的时候会被review
# (大厂)每周五:专门挑出时间小组内代码review
# C阶段:项目测试
1.把项目跑在测试环境/机器上
2.测试人员测完,他点完成,这个需求就结束了
3.测着有问题,再改再提交--->测试
# 4 人情世故
-刚去,不会的可以问 # 同样的问题不要问第二次!!!录下来
-同事搞好关系,买瓶饮料
-不要早走、迟到 # 至少头一个月
-公司暗语 # 多听少说
# 5.杂项
压力测试工具: Apache Benchmark(简称: ab)、JMeter
图形化界面GUI: PyQt(公司使用较多)、Tkinter # 写一点小工具而已
# 桌面编程: 主要使用C#来写
远程连接公司内部数据库
# 公司内部连接数据库
开发时,一般是先连接到公司服务器的跳板机上,才有权限访问到实际数据库所在的服务器地址
# 跳板机 Jumpserver python编写的
是内网服务器统一访问的入口
# eg: 借助sshtunnel库的SSHTunnelForwarder模块
通过本地22端口ssh到跳板机,通过跳板机绑定远程MySQL服务使用 (或者本地开启一个转发端口给远程使用)
Tunnel n. 隧道 Forwarder
import pymysql
from sshtunnel import SSHTunnelForwarder
server = SSHTunnelForwarder(
ssh_address_or_host=('机器B的IP', 22), # 指定ssh登录到跳转机
ssh_username='机器B的用户名', # 跳转机的用户
ssh_password='机器B的密码', # 跳转机的密码
remote_bind_address=('机器C的IP', 3306) # 绑定远程的MySQL服务器
# local_bind_address=('0.0.0.0', 10022) # 开启本地转发端口,通过该端口转发到远程mysql
)
server.start()
db = pymysql.connect(
host='127.0.0.1',
port=server.local_bind_port,
user='机器C的用户名',
passwd='机器C的密码',
db='数据库名'
)
cursor = db.cursor()
cursor.execute('select * from article limit 1')
data = cursor.fetchall()
print(data)
db.close()
server.close()
1 秒杀的设计方案
# 参考资料:
核心敖丙 https://zhuanlan.zhihu.com/p/92307325 !!!
语雀 https://www.yuque.com/keep_running/systemdesign/wwlgra !!!
https://blog.csdn.net/jsxingmang/article/details/109248952
https://blog.csdn.net/g6U8W7p06dCO99fQ3/article/details/121586060
# 1.跟语言无关,是解决秒杀高并发的思路方案
瞬时并发量非常高
读多写少
流程简单
# 2.是在原有项目继续添加修改,还是新写一个接口服务? 解耦
新写一个秒杀服务,避免影响原有项目 # 其实就是一个秒杀的微服务雏形
因为若是在原有项目上添加修改,程序容易因为并发量崩溃,或数据库数据出现问题,都会影响原来项目
# 3.重新设计库和几个表 秒杀服务:单独一个库 和 两三个表就行了
eg:卖10000个0.01元的python课程 课程表(喊库存量)、订单表
# 4.根据并发量的大小,进行不同的设计
注意:不要超卖,查询库存 就是抢到的人 不能大于 实际的订单量
# 4.1 并发量:很少
一些只是噱头,吸引流量的商品 eg: 0.01元的python课程 肯定不会有很多订单 (亏本的肯定不会很多)
-添加锁限制 # 借助mysql的悲观锁、乐观锁就行了
-生成订单、同步扣款
# 4.2 并发量:几百
-添加锁限制 # 借助mysql的悲观锁、乐观锁就行了
-把同步换成异步 # celery+redis,生成订单、扣款
# 4.3 并发量:几千
-限流 # 限流方式有多种 eg: 用户请求令牌、redis库存预热、专业的限流组件-阿里的Sentinel、Hystrix等
# 1.用户请求令牌 限制每个用户能够发送 总的秒杀请求 有令牌的用户才能进到后端的订单操作等 个人感觉这个不太合理!!!
-设置每个用户的令牌数
# 每个用户id最多生成10个令牌,变相等于每个用户最多刷新十次 访问十次秒杀页面
-有资格秒杀的用户,访问秒杀页面,返回给个令牌,同时放到redis一份
-带有令牌的用户才能往里走 redis中有令牌,再往后走
# 只要redis中令牌查过一次,直接删除(爬虫不能反复的发),防止用户同个令牌多次访问
# 2.库存预热 提前把商品的库存加载到Redis中,秒杀成功扣减库存的操作丢给redis去做
-搭建redis主从(从库读库存、主库扣减库存),提前把商品的库存加载到Redis中
-redis-Lua脚本 类似Redis事务 # 想当于 添加了锁的限制,避免超卖现象
写一个脚本把判断库存、扣减库存的操作都写在一个脚本丢给Redis去做
库存到0了,后面的都Return False了是吧
一个失败了你修改一个开关,直接挡住后续的所有请求
-真正秒杀成功的请求,放进后续的缓冲消息队列里,后续再异步的去修改库存就好了
-缓冲 # 利用专业的消息队列 rabbitmq 做流量削峰
把有资格秒的用户id放到消息队列中 (就是把限流后的用户请求,放进消息队列里)
-添加到缓冲队列后,视图直接返回前端动图 (您正在排队,请稍候)
-前端再轮询 每隔3s,向后端发一次请求,查询是否秒杀成功
-异步消费 # 从缓冲队列中取出秒杀请求,再异步执行后续的库存校验、生成订单等操作
-worker一个个给执行,1w个都消费完了
-生成订单、mysql持久化 也可写成异步去操作
-分布式锁 # 项目部署是在多台机器上,mysql的锁 性能跟不上 换成分布式锁
# 核心: 限流,锁,异步,缓冲(消息队列)
2 分布式id生成方案
https://www.cnblogs.com/liuqingzheng/p/11074623.html
# 1.分布式的系统中生成id号
若按当前时间生成id号,可能会重复,全局唯一ID的系统是非常必要的
# 2.生成id号的要求
-全局唯一 # 不能重复
-趋势递增
-单调递增
-信息安全 # 不要被猜出来
# 4.id的生成方案
# A mysql的自增 : 可以做,但性能跟不上
# B uuid : 全局唯一,安全,但没有递增的趋势
就不适合作为数据库的主键索引(聚树索引)
没有递增趋势,每次新增数据时,可能插入到前面数据中,那么主键索引树需要移动,影响性能
# C redis : 自增+时间+用户id 比较好的方案
# D snowflake : 雪花算法 业界普遍选择的id生成方案
-跟语言无关(任何语言都有实现)
-不依赖于服务(redis,mysql),直接写在机器上的程序代码上 没有额外的服务挂掉风险
-作为辅助索引即可 最好不要来用作为 数据库的主键索引(可能需要移动也会影响数据库部分性能),
-词放到简历上
3 分布式锁
https://www.cnblogs.com/Edmondhui/p/16893390.html
# 实现分布式锁
1.数据库的悲观,乐观锁
2.基于redis实现分布式锁
# 基于Redis的实现分布式锁
本质原理基于:SETNX
a.获取锁的时候,使用setnx加锁,并使用expire命令为锁添加一个超时时间
超过该时间则自动释放锁,锁的value值为一个随机生成的UUID,通过此在释放锁的时候进行判断
b.获取锁的时候还设置一个获取的超时时间,若超过这个时间则放弃获取锁
c.释放锁的时候,通过UUID判断是不是该锁,若是该锁,则执行delete进行锁释放
# redis官方提供分布式锁
https://github.com/SPSCommerce/redlock-py