背景:TextCnn模型用tensorflow+flask+gunicorn搭建模型預測併發API生產環境,模型調用拋異常。
報錯代碼:
#模型代碼處:
# Misc Parameters
tf.flags.DEFINE_boolean("allow_soft_placement", True, "Allow device soft device placement")
tf.flags.DEFINE_boolean("log_device_placement", False, "Log placement of ops on devices")
FLAGS = tf.flags.FLAGS
FLAGS.flag_values_dict()
FLAGS.set_default("checkpoint_dir",checkpoint_dir)
# Map data into vocabulary
vocab_path = os.path.join(FLAGS.checkpoint_dir, "..", "vocab")
self.vocab_processor = learn.preprocessing.VocabularyProcessor.restore(vocab_path)
#tensorflow的flags.py代碼處:
class _FlagValuesWrapper(object):
"""Wrapper class for absl.flags.FLAGS.
The difference is that tf.flags.FLAGS implicitly parses flags with sys.argv
when accessing the FLAGS values before it's explicitly parsed,
while absl.flags.FLAGS raises an exception.
"""
def __init__(self, flags_object):
self.__dict__['__wrapped'] = flags_object
def __getattribute__(self, name):
if name == '__dict__':
return super(_FlagValuesWrapper, self).__getattribute__(name)
return self.__dict__['__wrapped'].__getattribute__(name)
def __getattr__(self, name):
wrapped = self.__dict__['__wrapped']
# To maintain backwards compatibility, implicitly parse flags when reading
# a flag.
if not wrapped.is_parsed():
wrapped(_sys.argv)
return wrapped.__getattr__(name)
報錯信息:
File "/usr/local/lib/python3.6/site-packages/tensorflow/python/platform/flags.py", line 86, in __getattr__
wrapped(_sys.argv)
File "/usr/local/lib/python3.6/site-packages/absl/flags/_flagvalues.py", line 633, in __call__
name, value, suggestions=suggestions)
absl.flags._exceptions.UnrecognizedFlagError: Unknown command line flag 'c'
問題原因:
gunicorn的如下兩種命令行啓動方式都會通過命令行帶配置參數(gunicorn自己用的),被flask文件帶到了模型調用處,tensorflow解析sys.argv的時候不認識了,對於這種情況tensorflow代碼裏是有註釋說明的。
#gunicorn -c gun.py evalute:app
#gunicorn -w 4 -b 127.0.0.1:5000 evalute:app
#tensorflow的代碼註釋說明:
"""Wrapper class for absl.flags.FLAGS.
The difference is that tf.flags.FLAGS implicitly parses flags with sys.argv
when accessing the FLAGS values before it's explicitly parsed,
while absl.flags.FLAGS raises an exception.
"""
解決辦法:
1. 修改tensorflow的flags.py代碼
參考 https://blog.csdn.net/qq_35240640/article/details/103632902 加兩行代碼,不讓tensorflow解讀sys.argv。
百度的時候也有推薦修改tensorflow版本的,太麻煩,沒試。
def __getattr__(self, name):
wrapped = self.__dict__['__wrapped']
# To maintain backwards compatibility, implicitly parse flags when reading
# a flag.
if not wrapped.is_parsed():
while len(sys.argv) > 1:
sys.argv.pop()
wrapped(_sys.argv)
return wrapped.__getattr__(name)
2. 修改工程模型代碼
提前定義,讓tensorflow認識,雖然它也用不到。
#debug Parameters
tf.flags.DEFINE_integer("w", 3, "gunicorn workers' number")
tf.flags.DEFINE_string("b", "", "gunicorn workers' ip and port")
tf.flags.DEFINE_string("c", "", "gunicorn workers' config address")
FLAGS = tf.flags.FLAGS
顯然第2中方式更合理。
詳細追查過程:
其實在定位到上面的報錯之前還有一步。
第一步:
flask的啓用入口我涉及到兩種,一種是開發環境main函數入口;一種是生產環境gunicorn入口。
我最初模型的實例化放到了main函數裏,所以在通過gunicorn調用的時候就找不到模型了,通過給flask的app入口文件打log看報錯信息,定位了問題。
解決辦法就是把實例化代碼放到flask app文件的公用代碼處。
第二步:
在哪查看報錯信息:
1. 在通過配置文件啓動gunicorn的時候報錯信息查看debug文件。
2. 在通過-w -b直接啓動gunicorn的時候報錯信息在控制檯。
走的彎路:
在第二步排查的時候,我一度以爲是因爲gunicorn多進程實例化了多個模型而不該實例化多個模型所以報錯了。
於是試圖通過gunicorn提供的preload功能或flask的@app.before_request的註解功能提前實例化一個模型實例,最後通過報錯信息才知道問題癥結所在。