You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

GAS WebApp执行成功却仍抛出TimeoutError的解决方案咨询

Google Apps Script WebApp 超时问题解决方案

问题描述

我实现了一个WebApp脚本,可执行多种操作(包括修改Google表格),该操作通常耗时略超过60秒。但即便在Python请求中设置了timeout=360或timeout=None,有时仍会在不到30秒时遇到TimeoutError。

更棘手的是:收到超时错误后,WebApp脚本仍会继续运行并修改表格,但我无法确定它是否成功完成并返回"Done",还是执行出错;同时也不知道WebApp还需多久才能完成,无法判断是否要发起新调用(避免执行重叠)。

现有代码

Python 请求代码

webAppsUrl = "https://script.google.com/macros/s/xxxxxxx/exec"
web_app_response = requests.get(webAppsUrl, headers=headers, timeout=360)
if web_app_response.text == 'Done':
    ...
else:
    print('Try Again!')

Google Apps Script WebApp 代码

function doGet(e) {
  const lock = LockService.getDocumentLock();
  if (lock.tryLock(360000)) {
    try {
      All_Leagues_Funct();
      lock.releaseLock();
      return ContentService.createTextOutput('Done');
    } catch (error) {
      lock.releaseLock();

      const errorObj = {
        message: error.message,
        stack: error.stack
      };
      const folder = DriveApp.getFoldersByName("Error GAS").next();
      const file = folder.createFile(
        new Date().toString() + '.txt',
        JSON.stringify(errorObj, null, 2)
      );

      return ContentService.createTextOutput(error);
    }
  } else {
    return ContentService.createTextOutput('LockService Limit!');
  }
}

错误日志

Traceback (most recent call last):
  File "C:\Users\Computador\AppData\Local\Programs\Python\Python310\lib\site-packages\urllib3\connection.py", line 174, in _new_conn
    conn = connection.create_connection(
  File "C:\Users\Computador\AppData\Local\Programs\Python\Python310\lib\site-packages\urllib3\util\connection.py", line 95, in create_connection
    raise err
  File "C:\Users\Computador\AppData\Local\Programs\Python\Python310\lib\site-packages\urllib3\util\connection.py", line 85, in create_connection
    sock.connect(sa)
TimeoutError: [WinError 10060] 连接尝试失败,因为连接方在一段时间后没有正确响应,或者已建立的连接失败,因为连接的主机未能响应

During handling of the above exception, another exception occurred:     

Traceback (most recent call last):
  File "C:\Users\Computador\AppData\Local\Programs\Python\Python310\lib\site-packages\urllib3\connectionpool.py", line 703, in urlopen
    httplib_response = self._make_request(
  File "C:\Users\Computador\AppData\Local\Programs\Python\Python310\lib\site-packages\urllib3\connectionpool.py", line 386, in _make_request    
    self._validate_conn(conn)
  File "C:\Users\Computador\AppData\Local\Programs\Python\Python310\lib\site-packages\urllib3\connectionpool.py", line 1040, in _validate_conn  
    conn.connect()
  File "C:\Users\Computador\AppData\Local\Programs\Python\Python310\lib\site-packages\urllib3\connection.py", line 358, in connect
    self.sock = conn = self._new_conn()
  File "C:\Users\Computador\AppData\Local\Programs\Python\Python310\lib\site-packages\urllib3\connection.py", line 179, in _new_conn
    raise ConnectTimeoutError(
urllib3.exceptions.ConnectTimeoutError: (<urllib3.connection.HTTPSConnection object at 0x000001484196FCD0>, '连接到 script.googleusercontent.com 超时。(connect timeout=360)')

During handling of the above exception, another exception occurred:     

Traceback (most recent call last):
  File "C:\Users\Computador\AppData\Local\Programs\Python\Python310\lib\site-packages\requests\adapters.py", line 440, in send
    resp = conn.urlopen(
  File "C:\Users\Computador\AppData\Local\Programs\Python\Python310\lib\site-packages\urllib3\connectionpool.py", line 785, in urlopen
    retries = retries.increment(
  File "C:\Users\Computador\AppData\Local\Programs\Python\Python310\lib\site-packages\urllib3\util\retry.py", line 592, in increment
    raise MaxRetryError(_pool, url, error or ResponseError(cause))      
urllib3.exceptions.MaxRetryError: HTTPSConnectionPool(host='script.googleusercontent.com', port=443): 针对 URL 的重试次数已达上限: /macros/echo?user_content_key=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX (由 ConnectTimeoutError 引起: (<urllib3.connection.HTTPSConnection object at 0x000001484196FCD0>, '连接到 script.googleusercontent.com 超时。(connect timeout=360)'))

During handling of the above exception, another exception occurred:     

Traceback (most recent call last):
  File "c:\Users\Computador\Desktop\Squads Python\squads_sw.py", line 539, in matches_infos
    web_app_response = requests.get(url, headers=headers, timeout=360)  
  File "C:\Users\Computador\AppData\Local\Programs\Python\Python310\lib\site-packages\requests\api.py", line 75, in get
    return request('get', url, params=params, **kwargs)
  File "C:\Users\Computador\AppData\Local\Programs\Python\Python310\lib\site-packages\requests\api.py", line 61, in request
    return session.request(method=method, url=url, **kwargs)
  File "C:\Users\Computador\AppData\Local\Programs\Python\Python310\lib\site-packages\requests\sessions.py", line 529, in request
    resp = self.send(prep, **send_kwargs)
  File "C:\Users\Computador\AppData\Local\Programs\Python\Python310\lib\site-packages\requests\sessions.py", line 667, in send
    history = [resp for resp in gen]
  File "C:\Users\Computador\AppData\Local\Programs\Python\Python310\lib\site-packages\requests\sessions.py", line 667, in <listcomp>
    history = [resp for resp in gen]
  File "C:\Users\Computador\AppData\Local\Programs\Python\Python310\lib\site-packages\requests\sessions.py", line 237, in resolve_redirects     
    resp = self.send(
  File "C:\Users\Computador\AppData\Local\Programs\Python\Python310\lib\site-packages\requests\sessions.py", line 645, in send
    r = adapter.send(request, **kwargs)
  File "C:\Users\Computador\AppData\Local\Programs\Python\Python310\lib\site-packages\requests\adapters.py", line 507, in send
    raise ConnectTimeout(e, request=request)
requests.exceptions.ConnectTimeout: HTTPSConnectionPool(host='script.googleusercontent.com', port=443): 针对 URL 的重试次数已达上限: /macros/echo?user_content_key=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX (由 ConnectTimeoutError 引起: (<urllib3.connection.HTTPSConnection object at 0x000001484196FCD0>, '连接到 script.googleusercontent.com 超时。(connect timeout=360)'))

解决方案建议

1. 切换为异步执行模式

Google Apps Script WebApp的同步请求存在默认超时限制,超过一定时间会主动断开连接。最佳方案是将耗时操作改为异步执行:

  • 在WebApp的doGet方法中,不直接执行All_Leagues_Funct(),而是创建时间驱动触发器,延迟几秒后执行该函数。
  • 修改GAS代码示例:
function doGet(e) {
  const lock = LockService.getDocumentLock();
  if (lock.tryLock(360000)) {
    try {
      // 创建异步触发器,延迟1秒执行
      ScriptApp.newTrigger("All_Leagues_Funct")
        .timeBased()
        .after(1000)
        .create();
      lock.releaseLock();
      return ContentService.createTextOutput('Task Queued');
    } catch (error) {
      lock.releaseLock();
      // 错误处理逻辑不变
      const errorObj = {
        message: error.message,
        stack: error.stack
      };
      const folder = DriveApp.getFoldersByName("Error GAS").next();
      const file = folder.createFile(
        new Date().toString() + '.txt',
        JSON.stringify(errorObj, null, 2)
      );
      return ContentService.createTextOutput(error);
    }
  } else {
    return ContentService.createTextOutput('LockService Limit!');
  }
}

// 新增:触发器执行的函数添加状态记录
function All_Leagues_Funct() {
  // 先在状态表标记任务开始
  const statusSheet = SpreadsheetApp.openById("你的状态表ID").getSheetByName("TaskStatus");
  statusSheet.appendRow([new Date().toISOString(), "Running", "All_Leagues_Funct"]);
  
  try {
    // 原有业务逻辑
    // ...
    
    // 任务成功,更新状态
    statusSheet.appendRow([new Date().toISOString(), "Success", "All_Leagues_Funct"]);
  } catch (error) {
    // 任务失败,更新状态
    statusSheet.appendRow([new Date().toISOString(), "Failed: " + error.message, "All_Leagues_Funct"]);
    // 错误日志逻辑不变
  }
}

2. 实现任务状态查询机制

Python端不再等待WebApp返回结果,而是:

  1. 调用WebApp触发异步任务,得到Task Queued响应后,记录当前时间。
  2. 定期(比如每10秒)查询状态表,获取任务的执行状态。
  3. 根据状态决定后续操作:成功则继续,失败则重试,运行中则继续等待。

Python查询状态表示例(使用gspread库):

import gspread
import time

gc = gspread.service_account(filename='service_account.json')
status_sheet = gc.open_by_key("你的状态表ID").worksheet("TaskStatus")

def check_task_status():
    # 获取最新的状态记录
    rows = status_sheet.get_all_values()
    if not rows:
        return "Unknown"
    latest_row = rows[-1]
    return latest_row[1]

# 触发任务
webAppsUrl = "https://script.google.com/macros/s/xxxxxxx/exec"
response = requests.get(webAppsUrl, headers=headers, timeout=30)
if response.text == 'Task Queued':
    # 轮询状态
    max_wait_time = 360  # 最多等待6分钟
    start_time = time.time()
    while time.time() - start_time < max_wait_time:
        status = check_task_status()
        if status.startswith("Success"):
            print("任务执行成功")
            break
        elif status.startswith("Failed"):
            print(f"任务执行失败: {status}")
            # 可选:重试逻辑
            break
        else:
            print("任务执行中,等待10秒...")
            time.sleep(10)
    else:
        print("任务超时未完成")

3. 优化GAS执行效率

减少All_Leagues_Funct()的执行时间,从根源降低超时概率:

  • 批量读写Google表格:使用getRange().getValues()/setValues()一次性读写多个单元格,避免循环调用单个单元格的API。
  • 减少不必要的Drive/Services调用:比如重复获取文件夹、表格对象,可提前缓存。
  • 使用SpreadsheetApp.flush()减少缓存刷新次数,只在必要时调用。

4. 调整Python请求重试策略

捕获ConnectTimeout异常后,不要直接重试,先查询任务状态,确认当前没有任务在运行后再重试,避免执行重叠:

import requests
import time

webAppsUrl = "https://script.google.com/macros/s/xxxxxxx/exec"
max_retries = 3
retry_count = 0

while retry_count < max_retries:
    try:
        web_app_response = requests.get(webAppsUrl, headers=headers, timeout=360)
        if web_app_response.text == 'Done':
            print("任务完成")
            break
        elif web_app_response.text == 'LockService Limit!':
            print("有任务在执行,等待30秒后重试")
            time.sleep(30)
            retry_count +=1
        else:
            print("任务未完成,重试")
            retry_count +=1
    except requests.exceptions.ConnectTimeout:
        print("连接超时,先检查任务状态")
        status = check_task_status()
        if status == "Running":
            print("任务正在运行,等待60秒后再检查")
            time.sleep(60)
        else:
            print("无运行中任务,重试")
            retry_count +=1
else:
    print("重试次数用尽,任务未完成")

内容的提问来源于stack exchange,提问作者Digital Farmer

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.26 19:08:08