我有一个移动应用程序,它读取存储在Apache服务器上的JSON文件。如果通过GUI更改了某些内容,则会重新生成该JSON文件的内容(使用PHP脚本)。
我担心尝试在Apache服务的过程中覆盖JSON文件可能会导致问题。
Apache在提供文件之前是否获得了读锁定?如果没有,如果我在服务的同时尝试写它会发生什么?
答案 0 :(得分:9)
没有。在与POSIX兼容的系统上,所有锁都是建议性的,所以即使apache获得读锁定,另一个进程也可以写入文件。
您可以使用strace
确定:
[pid 7246] open("/var/www/file.json", O_RDONLY|O_CLOEXEC) = 11
[pid 7246] fcntl(11, F_GETFD) = 0x1 (flags FD_CLOEXEC)
[pid 7246] mmap(NULL, 20, PROT_READ, MAP_SHARED, 11, 0) = 0x7f53f93da000
[pid 7246] munmap(0x7f53f93da000, 20) = 0
[pid 7246] writev(10, [{"HTTP/1.1 200 OK\r\nDate: Thu, 26 J"}, ...) = 365
[pid 7246] close(11) = 0
因此,您的JSON文件只能部分写入。要避免此问题,请将JSON文件写入同一文件系统上的临时文件,并使用原子rename
覆盖该文件。
这样,如果open
成功,apache将继续提供旧文件。如果rename
在open
之前完成,则apache将获取新的已完成文件。
如果您担心一致性(在电源故障等情况下),您可能还希望在关闭之前调用写入JSON文件的应用程序中的fsync
。
答案 1 :(得分:2)
你正在考虑使用* nix平台的错误范例。您想要的是原子文件写入脚本中的JSON文件。您可以通过将文件写入目标目录中的唯一临时文件名,然后使用rename()
将此文件移动到旧文件上来执行此操作。文件移动操作是原子操作。异步进程将打开旧的JSON文件或新的JSON文件,但不打开混合。
有多种方法可以构建临时文件名。请参阅tempnam()
上的PHP文档用户注释。我的系统会生成一个请求唯一ID,因此我只使用$_SERVER["UNIQUE_ID"]
作为基础。