1.漏斗模型
reseller_id, login_at, session_time, click_times, cart_times, pay_times, pay_offer, pay_amount, search_list, view_times, catalog_id, supplier_id, category_id, last_share_time, which_app
2.表示商品的状态太多,修改的地方少而使用的地方多,导致查询的速度较慢,查询字段不易涵盖全和es需要不断调整
用一个新字段表示复合状态,其他状态共同维护这个状态
3.supplier和supplier_admin分离,supplier也要拆分。为后续多个店铺管理员做准备
4.gunicorn配置
import multiprocessing
import gevent.monkey
gevent.monkey.patch_all()
bind = ":8000"
debug = False
loglevel = 'error'
# 访问日志
accesslog = "/dev/null"
# 错误日志
errorlog = "/var/log/gunicorn.log"
capture_output = True # 影响代码中的print Redirect stdout/stderr to specified file in errorlog.
# 启动的进程数
workers = multiprocessing.cpu_count()
worker_connections = 1000
worker_class = 'gevent'
reload = False
timeout = 120
5.ab命令,-T表示请求头数据类型,-H表示其他header, -p表示输入参数文件 -s表示timeout
ab -c 10 -n 10 -T 'application/json' -H 'token:eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpYXQiOjE1NjcxNTMxMjgsImV4cCI6MTU2OTc0NTEyOCwic291cmNlX3R5cGUiOjMsInVpZCI6MTExNywiZGV2aWNlX2lkIjoxODk0ODIyODM1fQ.q6HSY8Vxz83U_tSime6enWIWJ7qOgldnkPmdrrq8pNY' -s 120 -p /home/test/filter_search.txt http://127.0.0.1:5000/api/v1.0/product/filter/search
6.APScheduler
schedulers(triggers, executors, job stores)
BackgroundScheduler和主业务一起跑,BlockedScheduler单独跑
job stores: 如果你需要在程序关闭或重启时,保存任务的状态,那么就要选择持久化的任务储存器。序列化和反序列化
triggers:
date 日期:触发任务运行的具体日期,作业任务只会执行一次
interval 间隔:触发任务运行的时间间隔
cron 周期:触发任务运行的周期 在特定时间周期性地触发
1.通过调用add_job()
2.通过装饰器scheduled_job()
第一种add_job()方法会返回一个apscheduler.job.Job实例,这样就可以在运行时,修改或删除任务。
1.调用remove_job(),参数为:任务ID,任务储存器名称
2.在通过add_job()创建的任务实例上调用remove()方法
7.系统日志
存储在es or mango
某个api的输入,输出,token(type, uid),操作时间,api_version
from flask import request_tearing_down
from flask import request_started, request_finished
request_tearing_down(action, app)
8.api权鉴
api鉴权
header里面增加以下参数:
AppId: 供应商code
AuthRole:'Supplier'
TimeStamp:当前时间戳 (10位的秒级时间戳)
Random:随机数(10位)
Signature:签名 (包括AppId,AuthRole,TimeStamp,Random)根据key值的字母升序排序后,以key1:value1:key2:value2这样方式连接组成的字符串,如'AppId:12346669:AuthRole:Supplier:Random:1565168569:TimeStamp:1565168569',带上私钥由AES加密后的到的签名
服务端验证签名和时间戳,将Signature和Random存入缓存,防止短时间多次请求或者重复攻击。
签名中也可以混入特定的secretkey来保证特定接口的请求
签名生成aes规则:
#aes demo https://blog.youkuaiyun.com/HH775313602/article/details/78991340
pip install pycrypto
import base64
from Crypto.Cipher import AES
# 补足字符串长度为16的倍数
def add_to_16(s):
while len(s) % 16 != 0:
s += '\0'
return str.encode(s) # 返回bytes
key = '1234567890123456' # 密钥长度必须为16、24或32位,分别对应AES-128、AES-192和AES-256
text = 'abcdefg' # 待加密文本
aes = AES.new(str.encode(key), AES.MODE_ECB) # 初始化加密器,本例采用ECB加密模式
encrypted_text = str(base64.encodebytes(aes.encrypt(add_to_16(text))), encoding='utf8').replace('\n', '') # 加密
decrypted_text = str(aes.decrypt(base64.decodebytes(bytes(encrypted_text, encoding='utf8'))).rstrip(b'\0').decode("utf8")) # 解密
print('加密值:', encrypted_text)
print('解密值:', decrypted_text)
9.重启脚本
import os
def daima():
# 进入后端目录/opt/socialcommerce
if not os.path.exists('/opt/socialcommerce'):
print('/opt/socialcommerce目录不存在')
os.chdir('/opt/socialcommerce')
# 从git下更新代码
os.system('git stash;git pull')
# 后端代码迁移
print('代码迁移')
os.chdir('/opt/socialcommerce/Pinshop')
os.system('pip3 install -r requirements.txt')
#os.system('rm -rf /opt/socialcommerce/Pinshop/migrations')
#os.system('python3 manage.py db init')
#os.system('python3 manage.py db migrate')
#os.system('python3 manage.py db upgrade')
print('代码迁移完成')
# 进入前端目录/opt/dist
if not os.path.exists('/opt/dist'):
print('/opt/dist目录不存在')
#os.chdir('/opt/dist')
#os.system('git pull')
#print('前端代码迁移完成')
#os.system('nginx -s reload')
print('nginx进程重启')
# 杀掉gunicorn进程
# os.system('kill -s 9 `pgrep gunicorn`')
os.system('ps aux | grep "gunicorn" |grep -v grep| cut -c 10-15 | xargs kill ')
print('杀掉gunicorn进程')
# 重启gunicorn进程
os.chdir('/opt/socialcommerce/Pinshop')
os.system('gunicorn manage:app -c ./guni_conf.py --chdir . 1>/dev/null 2>&1 &')
os.system('exit')
print('重启服务成功')
return
10.版本控制
a.通过app整体使用版本标志控制/Vx.xx,所有接口用新的url请求,新的接口调用老的接口的函数,优缺点:代码行数急剧增加,改动较多
b.通过app单个接口不断的使用新的版本,优缺点:app的接口上会存在很多版本号的接口
c.请求头header里面,放置版本信息,后台代码分支控制不同分支,优缺点:绝大部分接口不需要变动,变动接口走分支,单个函数看起来较长
比较合适的是方案三,原则:老的app会周期强制更新版本,这时候一些老的分支控制都会删除。
11.网络问题
短信改成异步的之后,会先发送到消息队列,这时候有人上传商品就会消耗大量网络,用户就收不到。
#api服务一套网络
#celery的work一套网络
#商品上传一套网络
12.从平台页面跳转到某个商户页面
点击dashboard按钮
1.获取supplier的token2(两个域名的storage独立)
2.跳转到supplier的主页面地址后面加上token=;前端判断这个地址,存储token2后跳转一下
13.orm报错
Table 'order' is already defined for this MetaData instance. Specify 'extend_existing=True' to redefine options and columns on an existing Table object.
find . -name "*.pyc" | xargs rm -rf
TypeError: Additional arguments should be named <dialectname>_<argument>, got 'commit'
py3clean .
in_transit_at = db.Column(db.TIMESTAMP(True), nullable=True, commit="物流商商开始发货时间")这个字段改过名字,哪里有缓存导致一直认为是额外字段
其实原因是commit错写成comment
14.开发单元测试分析-mock
1.mock简介
在做单元测试过程中,经常会有以下的场景:
1.被测对象依赖的对象构造复杂
我们想对class A进行单元测试,需要构造大量的class B、C、D等依赖对象,他们的构造过程复杂(体现在构造步骤多、耗时较长),这时我们可以利用mock去构造虚拟的class B、C、D对象用于class A的测试,因为我们只是想测试class A的行为是否符合预期,我们并不需要测试依赖对象。
2.被测单元依赖的模块尚未开发完成,而被测对象需要依赖模块的返回值进行测试: ----- 比如service层的代码中,包含对dao层的调用,但dao层代码尚未开发 ----- 比如web的前端依赖后端接口获取数据进行联调测试,但后端接口并未开发完成
哪些时机和场合需要使用mock(when&where)
1.单元测试/接口测试中测试对象依赖其他对象,这些对象的构造复杂、耗时或者根本无法构造(未交付)
2.我们只测试对象内部逻辑的质量,不关心依赖对象的逻辑正确性和稳定性
基于以下2个原则
1.不需要对所有的依赖对象/服务进行mock,只对那些构造步骤复杂、构造耗时较长、不稳定的依赖对象/服务进行mock。
2.如果做分层测试(比如分层自动化),高层的测试设计可以基于以下假设:低层的测试已保证低层对象的质量,高层对低层的依赖可以mock,无需关心所依赖的低层对象的质量。
2.比较困难的地方
1.流程经常变化,新功能迭代的快。
2.电商项目,查询msyql,es,sqs,redis的代码是主要的,字段比较多,暂时不好展开。
3.代码结构没有分层,所以难以mock替换查询接口(最主要原因)。