【问题标题】:Make API request every x seconds in Python 3在 Python 3 中每 x 秒发出一次 API 请求
【发布时间】:2017-05-23 15:17:15
【问题描述】:

我正在尝试使用 Python 3 在服务器上进行压力测试。这个想法是每 1 秒向 API 服务器发送一个 HTTP 请求,持续 30 分钟。我尝试使用 requestsapscheduler 来做到这一点,但我一直得到

作业“send_request(触发:interval[0:00:01],下次运行时间:2017-05-23 11:05:46 EDT)”的执行 已跳过:已达到最大运行实例数 (1)

我怎样才能做到这一点?以下是我目前的代码:

import requests, json, time, ipdb
from apscheduler.schedulers.blocking import BlockingScheduler as scheduler

def send_request():
    url = 'http://api/url/'

    # Username and password
    credentials = { 'username': 'username', 'password': 'password'}

    # Header
    headers = { 'Content-Type': 'application/json', 'Client-Id': 'some string'}

    # Defining payloads
    payload = dict()

    payload['item1']    = 1234
    payload['item2'] = 'some string'
    data_array = [{"id": "id1", "data": "some value"}]
    payload['json_data_array'] = [{ "time": int(time.time()), "data": data_array]

    # Posting data
    try:
        request = requests.post(url, headers = headers, data =  json.dumps(payload))
    except (requests.Timeout, requests.ConnectionError, requests.HTTPError) as err:
        print("Error while trying to POST pid data")
        print(err)
    finally:
        request.close()

    print(request.content)

    return request.content

if __name__ == '__main__':
    sched = scheduler()
    print(time.time())
    sched.add_job(send_request, 'interval', seconds=1)
    sched.start()
    print('Press Ctrl+{0} to exit'.format('Break' if os.name == 'nt' else 'C'))

    try:
        # This is here to simulate application activity (which keeps the main thread alive).
        while true:
            pass
    except (KeyboardInterrupt, SystemExit):
        # Not strictly necessary if daemonic mode is enabled but should be done if possible
        scheduler.shutdown()

我尝试搜索堆栈溢出,但到目前为止,其他问题都没有达到我想要的效果,或者我错过了一些东西。如果是这样的话,我会很感激有人指出我正确的线程。非常感谢!

【问题讨论】:

  • @calico_ 谢谢,很快就会看看。
  • @calico_ 是的,问题是请求时间超过 1 秒。但由于这是一个压力测试,如果请求已经到位,我不能跳过该请求。我希望代码做的是即使先前的请求尚未完成/尚未返回,也要发出 API 请求。
  • 是的,抱歉,其他答案不彻底。我编辑了答案以包含解决方案。

标签: python


【解决方案1】:

我认为我标记的副本以及@jeff 的答案很好地描述了您的错误

编辑:显然不是......所以在这里我将描述如何解决最大实例问题:

最大实例问题

当您向调度程序添加作业时,您可以为作业的最大允许并发实例数设置一个参数。您可以 应该在此处阅读: BaseScheduler.add_job()

因此,解决您的问题只需将其设置为更高的值:

sch.add_job(myfn, 'interval', seconds=1, max_instances=10)

但是,你想要多少并发请求?如果它们的响应时间超过一秒,而您每秒请求一个,那么如果您让它运行足够长的时间,您将始终最终收到错误...

调度器

有几个调度程序选项可用,这里有两个:

后台调度器

您正在导入阻塞调度程序 - 启动时会阻塞。因此,在调度程序停止之前,您的其余代码不会被执行。如果您需要在启动调度程序后执行其他代码,我会像这样使用后台调度程序:

from apscheduler.schedulers.background import BackgroundScheduler as scheduler

def myfn():
    # Insert your requests code here
    print('Hello')

sch = scheduler()
sch.add_job(myfn, 'interval', seconds=5)
sch.start()

# This code will be executed after the sceduler has started
try:
    print('Scheduler started, ctrl-c to exit!')
    while 1:
        # Notice here that if you use "pass" you create an unthrottled loop
        # try uncommenting "pass" vs "input()" and watching your cpu usage.
        # Another alternative would be to use a short sleep: time.sleep(.1)

        #pass
        #input()
except KeyboardInterrupt:
    if sch.state:
        sch.shutdown()

阻塞调度器

如果启动调度器后不需要执行其他代码,可以使用阻塞调度器,更简单:

apscheduler.schedulers.blocking import BlockingScheduler as scheduler

def myfn():
    # Insert your requests code here
    print('Hello')

# Execute your code before starting the scheduler
print('Starting scheduler, ctrl-c to exit!')

sch = scheduler()
sch.add_job(myfn, 'interval', seconds=5)
sch.start()

【讨论】:

  • 我尝试了两种解决方案,问题仍然存在。我认为其背后的原因是请求本身需要超过 1 秒的时间来执行(我尝试每 5 秒运行一次,这样可以正常工作)
【解决方案2】:

我以前从未在 python 中使用过调度程序,但是这个other stackOverflow question 似乎可以解决这个问题。

这意味着任务花费的时间超过一秒,默认情况下,给定的作业只允许一个并发执行...... -Alex Grönholm

在您的情况下,我想使用线程可以满足您的需求。 如果你在 python 中创建了一个继承线程的类,类似于:

class Requester(threading.Thread):
  def __init__(self, url, credentials, payload):
    threading.Thread._init__(self)
    self.url = url
    self.credentials = credentials
    self.payload = payload        
  def run(self):
    # do the post request here
    # you may want to write output (errors and content) to a file
    # rather then just printing it out sometimes when using threads 
    # it gets really messing if you just print everything out

然后就像你如何处理轻微的变化。

if __name__ == '__main__':
  url = 'http://api/url/'
# Username and password
  credentials = { 'username': 'username', 'password': 'password'}
# Defining payloads
  payload = dict()
  payload['item1']    = 1234
  payload['item2'] = 'some string'
  data_array = [{"id": "id1", "data": "some value"}]
  payload['json_data_array'] = [{ "time": int(time.time()), "data": data_array]
  counter = 0
  while counter < 1800:
    req = Requester(url, credentials, payload)
    req.start()
    counter++
    time.sleep(1)

当然,如果你愿意,你可以完成剩下的部分,如果你愿意,你可以让 KeyboardInterrupt 真正完成脚本。

如果这是问题所在,这当然是一种绕过调度程序的方法。

【讨论】:

  • 嗯,所以 Requester 不会等到下一个 Requester 开始?这是一个有趣的想法,我会尝试一下,看看它是如何工作的。谢谢!
  • 有一个问题,如果我们定义def run(self),而不是req.start(),我们不应该用req.run() 来代替吗?我也更新了header部分,不小心把一些不相关的代码一起拿出来了。
  • 因此,当使用线程时,您通常不会真正调用 run 方法,run 会从 start 方法中调用。
  • 附注。我来自更多的 java 背景,但我确实看到了这个 [stackoverflow.com/questions/660961/… 和 Jerubs 的答案,这可能是一种更 Python 的方式来工作这个解决方案。因此,如果您不想创建线程的子类,您可以改为执行以下操作:def makeRequest(url, headers, payload):#the actual processing of the request(抱歉,我似乎无法弄清楚如何将代码块中的代码)
  • apscheduler 包以干净的面向对象方式实现了这一目标,但确实需要了解一些中级 Python 原则。有关详细信息,请参阅下面的答案。
猜你喜欢
  • 1970-01-01
  • 2020-02-02
  • 1970-01-01
  • 2021-04-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多