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返回结果,而是:
- 调用WebApp触发异步任务,得到
Task Queued响应后,记录当前时间。 - 定期(比如每10秒)查询状态表,获取任务的执行状态。
- 根据状态决定后续操作:成功则继续,失败则重试,运行中则继续等待。
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
相关产品推荐
相关产品推荐

