python延时一秒_python如何最快毫秒速度使用requests?

看了题主的更新,下面是一些新的回答。以下仅仅针对所看到的,因为有太多的需求细节不清楚,而且也确实没有太多的时间去研究。例如,如果你用账户1在服务器开始出票前0.5秒抢了一下,服务器返回“未开始”,会不会仍然对这个账户限流?也就是是说,账户1下一次可能成功的请求最早只能在开始出票后的0.5秒(每个账户1次每秒);账户提交过快的请求,比如每秒10次,那么会不会被抑制?比如这样做50次后,服务器会否对这个账户进行更长时间的限制;限流会刷新吗?比如账户1,由于提交了请求过多处于被限制状态,此时如果过了0.8秒再提请求,那么那边的限制时间会不会跟着延长0.8秒;除非用户在一秒内不再请求,才解开限流?如果服务器不管那么多,只是单纯的丢弃过度的请求,每过1秒钟时,放开一个请求,那么问题就好解决的多了。

看了一下你的代码,逻辑上是没有太大的问题,但是有几个系统性的问题可能对你的时延和成功率有影响。一是,你在时间到了以后才去创建线程,创建线程的工作本身非常慢(相对于其他的操作),然后是;二是,调用thread.start,这个方法的意思是“开始线程调度”,操作

现在关键问题是保证你的5个账号(我的理解是你作为经销商只能申请5个账号?),在开始一瞬间的成功率。可以试验一下如下的办法:

1、不用多线程。而是采用单个线程+多个进程的模式,每个进程负责1个账号。具体说来,程序的架子如下:

import sys

import datetime

import time

def handle(resp):

# 处理响应的函数

pass

def buy(account):

# 函数:买一张票

# 把账户信息account加入请求,并发送请求

# 为了成功率,我们try catch,实验若干次

request_body = () .... # 构建请求

c = 0

while True:

try:

resp = request.post(json=request_body, headers=headers)

# 处理返回的请求响应,

if handle(resp):

return True

except Exception as e:

print(str(e))

finally:

c += 1

if c > 2:

print("超过重试次数!")

break

return False

def main():

# 账户信息,可以是API Key或者是别的

account = sys.argv[1]

# 把字符串转成datetime类型,这样每次的比较可以省去这个转换。

buy_time = datetime.datetime.strptime(sys.argv[2], '%Y-%m-%dT%H:%M:%s')

milliseconds_jitter = int(sys.argv[3]) # 毫秒级别的跳动值,例如 10, -10,...

now = datetime.datetime.now

while True:

if now() - buy_time > datetime.timedelta(milliseconds=milliseconds_jitter):

for i in range(100):

buy(account) # 要买多于一张,循环,

time.sleep(1)

break

# time.sleep(5)

if __name__ == '__main__':

main()

2、调用如上的程序,对每个账户跑5个进程,真正的做到“并发”,这样这5个里面只要有一个成功,就可以。(具体的数字你可以调,看你的系统情况)这个是针对服务器不会刷新限流的“抑制时间”。

# 账户1

python '2019-07-01T00:00:32' -5

python '2019-07-01T00:00:32' 0

python '2019-07-01T00:00:32' 0

python '2019-07-01T00:00:32' 0

python '2019-07-01T00:00:32' +5

3、(可选)确保你的系统时间。在Windows系统下,打开时钟同步;在Linux下的话,打开NTP服务。--如果服务器的时间也是走标准NTP同步的话。

4、在Windows下,可以通过任务管理器将你的这些Python进程设定为“高优先级” -- 如果你有限制的专门服务器,其实最好设置为“实时” --这个我也不清楚,已经好多年没用Windows了;但是你可以做一下研究,看看可不可以设置更高的优先级,这样在系统调度的时候不会因为优先级低而被切换走过多。在Linux,可以nice(搜索linux nice)

--------- 之前的回答的分割线 ----------

这里涉及到很多问题,以下只考虑客户端的情况,还不考虑网络时延,

1、系统调用的时间

任何的datetime.now()或者time.sleep()的调用都涉及到系统调用(System Call)。所谓系统调用,就要涉及到从用户模式切换到内核模式,用直白的语言解释:就是要先存储当前的上下文信息,然后,然后程序会切换到内核模式,以运行这个获取时间的系统调用,然后将返回值存下,再切换回用户模式,恢复你的程序上下文,拿到那个时间值,再做一些封装等等。

以上的各个步骤都不是几个CPU周期就能完成的,可以说切换运行模式是非常“耗时的操作”,而且这个操作的成本非常之高(比起其他在用户模式运行的指令)。另外,如果你的

2、线程的调度时间

Python中的线程调度是依赖于底层的C库的,不过有个出了名的GIL,叫做全局锁的东西。简而言之就是说,为了保证不发生竞争或死锁,Python只允许一个线程在一个时间片运行。听起来好像这个是理所当然的,因为毕竟在以前单CPU单核的时代,线程就是快速的切换时间片而已,不过在多CPU多核的时代,这种方式根本无法利用多核的并行性,是一种资源浪费。以上是一点题外话。

回到本题,线程的调度时间是“无法预测的”,操作系统的内核有专门的线程调度的逻辑,在一个进程内会给每个线程一个小时间片执行,然后存起这个线程的执行状态,再调度另一个线程,这样循环往复。在Linux下,这种调度是preemptive(抢先式)的,也就是说,如果你有多于个线程,即便你有高精度的时延函数,你也无法精确的计时。你不知道调度线程的时候会有多少时延。

3、时延函数的精度

time.sleep从来都不是为了精确计时的,而且非常的依赖平台(底层的操作系统)。对于非实时操作系统(就是交换机、路由器等网络设备里面那种操作系统,例如VxWorks),这个精度很可能在~10ms左右波动。

4、Python的解释器与垃圾回收

Python是解释型语言并且依赖于垃圾回收(GC),也就是说你的代码并不是直接编译成机器代码执行的,而是Python的解释器先扫描源代码,再生成字节码然后自上而下的执行,主要的问题是,你不知道映射到底层的机器代码到底是什么样子,而有可能一些额外的解释器逻辑会造成不可预测的时延,一个最明显的例子就是垃圾回收(GC)。GC的例程会在后台一直扫描,并且在适当的时候进行垃圾回收,这个也是需要运行时间的。

总而言之,如果你想做到毫秒级的甚至更精确的,建议使用C,单线程,尽量少的系统调用,然后做profile,看你的代码稳定度,可以最大程度保证精确度。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值