我有a python-based daemon that provides a REST-like interface over HTTP to some command line tools。该工具的一般性质是接受请求,执行一些命令行操作,将pickle数据结构存储到磁盘,并将一些数据返回给调用者。在守护进程启动时会产生一个辅助线程,它会定期查看磁盘上的pickle数据,并根据数据中的内容进行一些清理。
如果pickle数据所在的磁盘恰好是Linux机器上的本地磁盘,这样可以正常工作。如果你切换到安装了NFS的磁盘,守护进程就可以正常启动,但随着时间的推移,NFS挂载的共享“消失”,并且守护进程无法通过os.getcwd()
之类的调用来判断它在磁盘上的位置。您将开始看到它记录错误,如:
2011-07-13 09:19:36,238 INFO Retrieved submit directory '/tech/condor_logs/submit'
2011-07-13 09:19:36,239 DEBUG CondorAgent.post_submit.do_submit(): handler.path: /condor/submit?queue=Q2%40scheduler
2011-07-13 09:19:36,239 DEBUG CondorAgent.post_submit.do_submit(): submitting from temporary submission directory '/tech/condor_logs/submit/tmpoF8YXk'
2011-07-13 09:19:36,240 ERROR Caught un-handled exception: [Errno 2] No such file or directory
2011-07-13 09:19:36,241 INFO submitter - - [13/Jul/2011 09:19:36] "POST /condor/submit?queue=Q2%40scheduler HTTP/1.1" 500 -
未处理的异常解析为守护程序无法再查看磁盘。此时尝试使用os.getcwd()
计算守护程序的当前工作目录的任何尝试都将失败。即使尝试更改为NFS挂载/tech
的根目录,也会失败。 logger.logging.*
方法总是乐于将日志和调试消息写入位于/tech/condor_logs/logs/CondorAgentLog
的NFS挂载共享上的日志文件中。
磁盘肯定仍然可用。还有其他基于C ++的守护进程,在基于python的守护进程时,在此共享上具有更高的频率读取和写入。
我陷入了调试这个问题的僵局。既然它适用于本地磁盘,代码的一般结构一定要好,对吧?有一些关于NFS挂载的共享和我的代码是不兼容的,但我不知道它可能是什么。
在处理长时间运行的Python守护程序时,是否必须实现特殊注意事项?该守护程序将经常读取和写入安装在NFS上的文件共享?
如果有人想看到代码处理HTTP请求并将pickle对象写入磁盘的部分位于github here中。通过读取pickle对象,子线程用于定期清理磁盘中的东西的部分是here。
答案 0 :(得分:5)
我有我的问题的答案,它与我在NFS共享上进行文件I / O的事实无关。事实证明,如果I / O超过NFS安装而不是本地磁盘,问题就会显得更快。
关键信息是代码通过SocketServer.ThreadingMixIn
和HTTPServer
类进行了线程化。
我的处理程序代码正在做一些接近以下的事情:
base_dir = getBaseDirFromConfigFile()
current_dir = os.getcwd()
temporary_dir = tempfile.mkdtemp(dir=base_dir)
chdir(temporary_dir)
doSomething()
chdir(current_dir)
cleanUp(temporary_dir)
这就是流量,或多或少。
问题不在于I / O是在NFS上完成的。问题是os.getcwd()
不是线程局部的,它是一个全局的过程。因此,当一个线程发出chdir()
移动到它刚刚在base_dir
下创建的临时空间时,下一个调用os.getcwd()
的线程将获得另一个线程temporary_dir
而不是静态启动HTTP服务器的基本目录。
解决方案是摆脱chdir()
和getcwd()
来电。启动并停留在一个目录中,并通过绝对路径访问其他所有内容。
NFS vs本地文件通过我循环播放。事实证明我的阻止:
chdir(temporary_dir)
doSomething()
chdir(current_dir)
cleanUp(temporary_dir)
当文件系统是NFS而不是本地时,运行很多。它使问题更快地发生,因为它增加了一个线程仍然在doSomething()
而另一个线程正在运行代码块的current_dir = os.getcwd()
部分的机会。在本地磁盘上,线程在整个代码块中移动很快,很少像那样交叉。但是,给它足够的时间(大约一周),并且在使用本地磁盘时会出现问题。
这是Python中线程安全操作的一个重要教训!
答案 1 :(得分:2)
要从字面上回答这个问题,是的,有一些关于NFS的问题。 E.g:
NFS不是缓存一致的,因此如果多个客户端访问文件,他们可能会收到陈旧数据。
特别是,您不能依赖O_APPEND以原子方式附加到文件。
根据NFS服务器的不同,O_CREAT | O_EXCL可能无法正常工作(至少在现代Linux上可以正常工作)。
特别是旧的NFS服务器缺乏或完全不工作的锁定支持。即使在更现代的服务器上,在服务器和/或客户端重启后锁定恢复也可能成为问题。 NFSv4是一种有状态协议,应该比旧协议版本更强大。
所有这一切都说,听起来你的问题与上述任何一个都没有关系。根据我的经验,Condor守护进程将在某些时候,根据配置,清理已完成的作业留下的文件。我的猜测是在这里寻找嫌犯。