如何从Google App Engine中的延迟任务返回数据

Pat*_*ray 5 javascript python google-app-engine google-app-engine-python

原始问题

我目前正在尝试升级Web应用程序的工作版本,并且遇到了一个任务,该任务在单个HTTP请求期间完成需要花费太长时间的问题。应用程序通过HTTP Post操作从JavaScript前端获取JSON列表,然后返回该列表的排序/切片版本。随着输入列表变长,排序操作将花费更长的时间(显然),因此在适当长的输入列表上,我遇到了60秒的HTTP请求超时,应用程序失败了。

我想开始使用延迟库执行排序任务,但是在执行该任务之后,我不清楚如何存储/检索数据。这是我当前的代码:

class getLineups(webapp2.RequestHandler):
  def post(self):
    jsonstring = self.request.body
    inputData = json.loads(jsonstring)
    playerList = inputData["pList"]
    positions = ["QB","RB","WR","TE","DST"]

    playersPos = sortByPos(playerList,positions)
    rosters, playerUse = getNFLRosters(playersPos, positions)
    try:
      # This step is computationally expensive, it will fail on large player lists.
      lineups = makeLineups(rosters,playerUse,50000)

      self.response.headers["Content-Type"] = "application/json"
      self.response.out.write(json.dumps(lineups))
    except:
      logging.error("60 second timeout reached on player list of length:", len(playerList))
      self.response.headers["Content-Type"] = "text/plain"
      self.response.set_status(504)

app = webapp2.WSGIApplication([
  ('/lineup',getLineups),
], debug = True)
Run Code Online (Sandbox Code Playgroud)

理想情况下,我想用对延迟任务库的调用来替换整个try / except块:

deferred.defer(makeLineups,rosters,playerUse,50000)
Run Code Online (Sandbox Code Playgroud)

但是我不清楚如何从该操作中获得结果。我在想必须将其存储在数据存储中,然后再检索它,但是我的JavaScript前端如何知道操作何时完成?我已经阅读了Google网站上的文档,但对于如何完成此任务仍然不甚了解。

我如何解决

使用公认的答案中的基本轮廓,这是我解决此问题的方法:

def solveResult(result_key):
  result = result_key.get()

  playersPos = sortByPos(result.playerList, result.positions)
  rosters, playerUse = getNFLRosters(playersPos,result.positions)

  lineups = makeLineups(rosters,playerUse,50000)
  storeResult(result_key,lineups)

@ndb.transactional
def storeResult(result_key,lineups):
  result = result_key.get()
  result.lineups = lineups
  result.solveComplete = True
  result.put()

class Result(ndb.Model):
  playerList = ndb.JsonProperty()
  positions = ndb.JsonProperty()
  solveComplete = ndb.BooleanProperty()

class getLineups(webapp2.RequestHandler):
  def post(self):
    jsonstring = self.request.body
    inputData = json.loads(jsonstring)

    deferredResult = Result(
      playerList = inputData["pList"],
      positions = ["QB","RB","WR","TE","DST"],
      solveComplete = False
    )

    deferredResult_key = deferredResult.put()

    deferred.defer(solveResult,deferredResult_key)

    self.response.headers["Content-Type"] = "text/plain"
    self.response.out.write(deferredResult_key.urlsafe())

class queryResults(webapp2.RequestHandler):
  def post(self):
    safe_result_key = self.request.body
    result_key = ndb.Key(urlsafe=safe_result_key)

    result = result_key.get()
    self.response.headers["Content-Type"] = "application/json"

    if result.solveComplete:
      self.response.out.write(json.dumps(result.lineups))
    else:
      self.response.out.write(json.dumps([]))
Run Code Online (Sandbox Code Playgroud)

然后,Javascript前端将轮询queryLineups URL固定的时间,并在时间限制到期或接收回数据时停止轮询。我希望这对其他尝试解决类似问题的人有所帮助。如果事情变得松散,我还有很多工作要做,以使它优雅地失效,但这是可行的,只是需要改进。

pab*_*han 4

我对GAE不熟悉,但这是一个相当普遍的问题,所以我可以给你一些建议。

您的总体想法是正确的,所以我将对其进行扩展。工作流程可能如下所示:

  1. 您收到创建阵容的请求。您在数据存储中为其创建一个新实体。它应该包含一个 ID(您稍后将需要它来检索结果)和一个状态(PENDING|DONE|FAILED)。如果这对您有用,您还可以保存请求中的数据。
  2. 您推迟计算并立即返回响应。响应将包含任务的 ID。当计算完成后,它将把任务的结果保存在Datastore中并更新任务的状态。该结果将包含任务 ID,以便我们可以轻松找到它。
  3. 一旦前端收到 ID,它就会开始轮询结果。使用setTimeoutsetInterval将带有任务 ID 的请求发送到服务器(这是一个单独的端点)。服务器检查任务的状态,如果完成则返回结果(如果失败则返回错误)。
  4. 前端获取数据并停止轮询。