有小夥伴反饋代碼運行服務概率性出現返回None的問題
併發執行
併發執行一個簡單腳本
def ok(t):
return "ok"
併發執行均可正常返回ok,無任何異常
日誌信息
查看出現錯誤的日誌信息
Traceback (most recent call last):
File "/app/runCode/callPy3.py", line 49, in call_func
FileNotFoundError: [Errno 2] No such file or directory:
'/app/box/sleep_time_fb3fd130-ba80-11ea-b4b2-0242ac1e573d.py'
從錯誤日誌信息中可以發現執行的文件沒有找到,看看代碼中這塊的處理邏輯
代碼邏輯
try:
# 執行關鍵字函數,並返回結果
return eval(_call_fun_)
except Exception as e:
traceback.print_exc()
finally:
try:
# 刪除文件
os.remove(file_path)
except Exception as e:
traceback.print_exc()
從代碼中可以看出,首先執行關鍵字函數後,再 finally
執行刪除文件的操作,debug斷點試驗均爲該處理順序,那爲何會出現該問題呢?
長時間執行
一開始併發執行的代碼,均爲直接返回,而用戶反饋的有問題代碼是因爲sleep了30s,是否是代碼在sleep時代碼文件被刪除?
故在執行某一個sleep 30秒的代碼時,多次執行立即返回的代碼,此問題復現
刪除文件操作
review所有代碼,發現整個服務執行文件刪除操作的僅僅2個地方:
- 上面代碼中提到的運行關鍵字函數後刪除代碼文件
- 在預處理時before_first_request初始化刪除所有文件
既然運行關鍵字函數後刪除代碼文件的操作沒有問題,那麼問題很大概率時因爲before_first_request的請求
before_first_request
查看這段代碼
# @app.before_first_request
def before_first_request():
# 初始化時操作,僅第一次請求時操作
try:
...
os.remove(file_path)
except Exception as e:
traceback.print_exc(e)
before_first_request在官方以及非官方文檔中均如此描述:
在對應用程序實例的第一個請求之前註冊要運行的函數, 只會執行一次
ok,既然懷疑before_first_request出現了問題,打印日誌並部署運行
執行log信息
# @app.before_first_request
def before_first_request():
# 初始化時操作,僅第一次請求時操作
try:
app.logger.info("**********************************刪除文件啦啦啦啦啦")
os.remove(file_path)
except Exception as e:
traceback.print_exc(e)
通過日誌發現有如下
INFO:werkzeug:172.30.139.0 - - [30/Jun/2020 16:27:08] "POST /colab/call HTTP/1.1" 200 -
INFO:__main__:**********************************刪除文件啦啦啦啦啦
INFO:__main__:ImmutableMultiDict([('code', 'import time\ndef ok(t):\n time.sleep(t)\n return "ok"'), ('fun_call', 'ok(0)'), ('user', 'swapi')])
INFO:werkzeug:172.30.139.0 - - [30/Jun/2020 16:27:08] "POST /colab/call HTTP/1.1" 200 -
INFO:__main__:**********************************刪除文件啦啦啦啦啦
INFO:__main__:ImmutableMultiDict([('code', 'import time\ndef ok(t):\n time.sleep(t)\n return "ok"'), ('fun_call', 'ok(0)'), ('user', 'swapi')])
INFO:werkzeug:172.30.139.0 - - [30/Jun/2020 16:27:08] "POST /colab/call HTTP/1.1" 200 -
INFO:__main__:**********************************刪除文件啦啦啦啦啦
INFO:__main__:ImmutableMultiDict([('code', 'import time\ndef ok(t):\n time.sleep(t)\n return "ok"'), ('fun_call', 'ok(0)'), ('user', 'swapi')])
INFO:werkzeug:172.30.71.0 - - [30/Jun/2020 16:27:08] "POST /colab/call HTTP/1.1" 200 -
INFO:__main__:**********************************刪除文件啦啦啦啦啦
INFO:__main__:ImmutableMultiDict([('code', 'import time\ndef ok(t):\n time.sleep(t)\n return "ok"'), ('fun_call', 'ok(0)'), ('user', 'swapi')])
執行機制
很明顯,before_first_request被多次執行了,啥原因呢?我們再次看看before_first_request的具體實現
#: A lists of functions that should be called at the beginning of the
#: first request to this instance. To register a function here, use
#: the :meth:`before_first_request` decorator.
#:
#: .. versionadded:: 0.8
self.before_first_request_funcs = []
@setupmethod
def before_first_request(self, f):
"""Registers a function to be run before the first request to this
instance of the application.
.. versionadded:: 0.8
"""
self.before_first_request_funcs.append(f)
在初始化後第一次請求時會調用,後續的所有請求都不會在調用該函數,除非重新初始化
多進程執行
而該服務啓動的方式恰好時多進程
app.run(host='0.0.0.0', port=5005, debug=False, processes=20)
將多進程修改爲1個進程或者修改成多線程的方式執行
app.run(host='0.0.0.0', port=5005, debug=False, threaded=True)
此時問題沒有復現
或者不使用 @app.before_first_request
註解的方式,直接init執行初始化操作同樣可以解決以上問題