工作实际环境、分布式id、分布式锁、秒杀设计方案

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
posted @ 2022-11-16 22:21  Edmond辉仔  阅读(103)  评论(0编辑  收藏  举报