多进程如何不加锁读写文件队列

                                                              文 : robby

        在海量用户环境中,很容易出现高并发应用程序同时向服务器发送请求的情况,

有时候会因为发送请求过于密集,致使服务器负载过重,无法及时响应请求,

即使服务器能够正常响应,也常常会产生一定程度上的延时,造成应用程序的等待,

如果是交互程序的话,用户的体验就是网页打开很慢。

       针对这一种情况,有经验的程序员一般都会考虑先将需要发送的数据存放到队列里,

然后agent读取队列的数据向指定服务器发送数据。这样的好处有如下几点:

     1、  异步处理请求,可以有效减少应用程序尤其是交互程序的等待时间,

            上层应用只需要将请求放入队列,即可以继续往下执行其他任务

     2、  应用程序可以脱离繁琐复杂的底层通信,提高开发效率,可以更好地进行分工协作

     3、  可复用性,使用不同语言编写的应用程序都可以使用同一队列,避免了重复开发

     4、  高效性,队列可以针对网络通信做一些有针对性的优化

     5、  数据完整性,队列可以采用动态调度,重传等机制来保证数据能够完

             整地发送到目的地 

    队列根据数据存储方式不同一般可以分为:

     1、  基于共享内存的队列

     2、  基于文件的队列  

    读取内存数据要比读取硬盘数据快几个数量级,所以基于共享内存的队列要比基于

文件的队列在数据读取方面有优势一些。但是基于共享内存的操作往往具有较高的复杂性

和风险性,而且内存的容量是十分有限的,使用不当的话很容易造成out of memory。

另外如果服务器意外宕机的话,共享内存队列里面的数据将全部丢失,难以保证数据的

完整性和持久性。利用文件来存放队列数据,一般不用担心空间容量的问题,因为文件

数据是存放磁盘上的,而且磁盘数据是持久的,不会因为异常宕机重启后导致数据的丢失。

一般多进程对同一文件同时进行读写操作的时候都需要加锁,但是在linux环境下可以通过

追加写以及rename的方式不加锁对同一文件进行读写操作,而且不用担心会发生异常或者

数据丢失。 应用程序可以将要发送的数据以约定的格式存放到指定文件里,这里值得注意

的是必须是以a+追加写的方式写入文件,因为在linux内核中,以追加的方式在底层是自动

加锁的。write底层是调用sys_write。而sys_write则是调用vfs_write,vfs_write的实现里

有下面一行:

ret = rw_verify_area(WRITE, file, pos, count);

假设wrtie参数里通知要追加200字节(size_t count=200),当前尾端位移为offset,它会立即通

过inode结构将offset ~ offset+200这个区域lock住。这时候任何其他想操作这个区域的进程将

会阻塞,所以多个进程同时往文件里面写数据是不会造成数据的紊乱的(如果直接用open函数

打开,write函数写入的话,就算数据大小超过4K,原子操作也是不会出现乱序问题的) 。

        也许有人会问,在不对文件加锁的情况下,多进程同时往文件里面写数据也许没问题,

但是其他进程如何同时从文件里读取数据呢?这里可以通过rename这个函数来实现,其他进程

可先调用rename函数将队列文件重命名为其他文件,最好以时间戳作为文件名,这样就不会产

生文件覆盖的问题,然后该进程再去读取重命名后的文件数据,此时每个进程只会读取对应的

唯一文件,自然不会出现问题。

       通过以上方法,多进程可以在不用对文件加锁的情况下读写同一队列文件,这样能够有效

提高执行效率,而且减少了程序的复杂性。

你可能感兴趣的