现在的Web已经对跨会话的通信有很高的需求了,比如在线聊天室等。这些基于客户端事件而且数据更新频繁的东西当然NodeJS来做比较好。NodeJS除了便于处理事件外,更重要的是它有服务器级的内存变量。如果要在PHP中实现,最好的方式就是让会话共享内存。
跨会话的通信也经常使用数据库或硬盘来中转。硬盘的方法我不推荐,PHP访问硬盘的效率本身就不高,而且频繁的读写硬盘对硬盘本身也是一种开销。另一种是数据库中转的方式,这是最容易扩展也是最稳定的方式。也许有人会问,数据库不是存在硬盘上的吗?和直接操作硬盘有什么区别?因为几乎所有的数据库都有缓存机制,即使是普通表,也会在内存中做缓存,所以它可以比直接访问硬盘的效率高。而且数据库还有内存表模式,表完全在内存中,速度当然就更快了。另外,数据库未必是SQL,还有NoSQL,比如使用memcache连关系化解析都省了,这当然效率高。而且这些数据库提供了一系列的标准读写操作,所以更容易扩展。
这些数据库已经提供了内存读写,为什么我还要在程序里搞共享内存呢?这当然有其它因素需要考虑。虽然数据库也可以直接读写内存,但是数据库本身毕竟是作为一个数据的中转。我们的程序与数据库通信是走端口的,这个过程的开销不容忽视。至少,与程序内直接访问变量的速度相比,通过端口去查询数据库,再把数据返回给程序,这个过程就太绕了。所以如果某个模块需要一个高效的跨会话通信功能,直接让程序访问内存当然是最好的做法。
不过PHP毕竟不是C++,访问内存需要调用封装好的函数的,这效率其实并不高。我在Win7+Apache下测试的结果是读取内存的效率是访问普通变量的四分之一,不过我觉得这个效率对PHP而言已经很高了。另外,PHP并没有C++那么灵活的指针功能,我们储存在内存中的字节串也只是字节串而已,无法直接把它当做对象来读入。所以这就有很大的局限性,不过对于简单的通信还是可以胜任的。
PHP做内存共享有两套接口。一个是shm,它实际上是变量共享,会把对象变量序列化后再储存。使用起来倒是挺方便,但是序列化存储对于效率优先的内存访问操作而言就没啥意义了。更重要的是它是LinuxOnly的,所以我就不理它了。另外一个是shmop,它是Linux和Windows通用的,不过功能上比shm弱了一些。
接下来手册上有的东西我就不说了,就说一些使用shmop要注意的地方。首先是shmop_open这个函数的参数列表,手册上说的很模糊。它有4个参数,key、flags、mode、size。flags和size没啥好说的,key是自己给定的一个数字,每一个不同的数字标示一块不同的内存,值随便给定,只要在自己的程序逻辑中不混乱就行了。当然如果一个Apache下有多个站点就需要事先协商好,这也是shmop最大的一个坑,就是因为这个所以扩展性稳定性都不如使用数据库。mode参数是设置内存块的访问权限,和文件的权限设置一样,翻到手册的chmod函数介绍上就可以找到详细信息。还有shmop_delete函数也很诡异,我测试时创建的内存块是无法通过这个函数删除的。查了一些资料后才知道这个函数只是把块标记为删除,进程结束后才释放。这就坑爹了,进程结束后释放不是废话吗?不调用它难道进程结束后可以不释放?总之,一旦使用shmop创建了内存块就释放不掉的,除非进程结束。所以在创建内存块时建议key参数用常量而不用变量,否则很有可能造成内存泄露。
PHP确实不适合做这样的工作,显然NodeJS要方便的多。但是没办法,以前的程序是PHP写的,要全部重写工作量太大了。我也正在努力的脱离PHP,以后就不纠结这么蛋疼的问题了。