首页 > 解决方案 > Google Cloud Functions 在数千次正确响应后返回 401 错误

问题描述

我正在编写一个使用云函数作为其评估函数的遗传算法,并且在流程的 1/3 处遇到了 401 响应代码。鉴于许多成功的调用,我并不完全确定这里发生了什么,并且在 Google Cloud 日志中没有任何迹象表明有任何问题(在 CF 日志或通用云范围日志中)。

目的是将它用作更“严格”项目的通用评估函数,但是对于这个我传递了一个字符串列表以及“正确”字符串,并将每个字符串之间的 ASCII 距离返回到正确的字符串. 此信息以 JSON 数据包的形式传入。遗传算法基本上只需要发现正确的字符串即可完成。这基本上是对one-max优化问题的一种发挥。

作为参考,这只有在我增加调用次数并传递字符串后才真正发生。该过程运行良好,评估数量和传递的字符串数量较少,但是当我将其放大一点时,它会在中途窒息)。请注意,使用 CF 的全部目的是尝试以指数方式向上扩展以进行评估调用,否则我只会在本地运行它。

Cloud Function 相当简单(评估字符串优化问题):

import json

# Evaluate distance from expected to individual
def fitness(bitstring, expected, dna_size):
  f = 0
  for c in range(dna_size):
    f += abs(ord(bitstring[c]) - ord(expected[c]))
  return f
    
def evaluateBitstrings(request):
  resp = []
  request_json = request.get_json()
  if request_json and 'bitstrings' in request_json and 'expected' in request_json and 'dna_size' in request_json:
    for bitstring in request_json['bitstrings']:
      f = fitness(bitstring, request_json['expected'], int(request_json['dna_size']))
      resp.append((bitstring, f))

    return str(json.dumps(resp))
  else:
    return f'Error: missing JSON information'

我发送的 JSON 数据包包含 1000 个字符串的列表,因此它实际上只是对这些字符串进行循环并创建一个距离返回的 JSON 数据包。

它被配置为使用 512mb 的内存,有 180 秒的超时,并使用身份验证来防止匿名调用。我通过 Python 在本地触发调用asyncio,并aiohttp使用标头中包含的授权(在 Windows Subsystem for Linux (Ubuntu) 中通过 本地身份验证gcloud)。

这是相关的 Python 代码(使用 3.6)。其中一个问题是本地绑定了大量的aiohttp调用,我遇到了这篇关于使用信号量增加使用信号量的调用量的帖子

import aiohttp
import asyncio
...
base_url = "https://<GCF:CF>"
headers = {'Content-Type': 'application/json'}
...
token = sys.argv[2] # call to `gcloud auth print-identity-token` as parameter
headers['Authorization'] = 'bearer ' + token

async def fetch(bitstrings,expected,session):
  b = {'bitstrings':bitstrings,
       'expected':expected,
       'dna_size':len(expected)}
  async with session.post(base_url, data=json.dumps(b), headers=headers) as response:
    assert response.status == 200
    data = await response.read()
    try:
      return json.loads(data)
    except:
      print("An error occurred: {0}".format(data))

async def bound_fetch(sem, bitstrings, expected, session):
  async with sem:
    return await fetch(bitstrings, expected, session)

async def run(iterable, expected, token):
  tasks = []
  sem = asyncio.Semaphore(1000)

  async with aiohttp.ClientSession(trust_env=True) as session:
    chunks = [iterable[x:x+1000] for x in range(0,len(population), 1000)]

    # build up JSON array
    for chunk in chunks:
      task = asyncio.ensure_future(bound_fetch(sem, chunk, expected, session))
      tasks.append(task)

...

# Within the GA code
for generation in range(ga.GENERATIONS):
  ...
  loop = asyncio.get_event_loop()
  future = asyncio.ensure_future(run(population, ga.OPTIMAL, token))
  responses = []
  results = loop.run_until_complete(future)
  for res in results: # loop through task results
    for r in res: # json coming in as a list
      responses.append((r[0], float(r[1]))) # string, fitness

进一步参考,我已经通过 本地完成了这项工作functions-framework,并没有遇到这个问题。只有当我接触到云端时才会发生这种情况。


编辑:我解决了 Forbidden 问题(需要刷新令牌,我只是启动了对相关gcloud命令的子进程调用),但是现在我看到了一个新问题:

aiohttp.client_exceptions.ClientConnectorError: Cannot connect to host us-central1-cloud-function-<CF>:443 ssl:default [Connect call failed ('216.239.36.54', 443)]

现在这种情况偶尔会发生(10 分钟,70 分钟等)。我开始怀疑我是否在这里打了一场失败的战斗。

标签: pythongoogle-cloud-functionspython-asyncioaiohttp

解决方案


推荐阅读