Skip to main content

PHP统计在线人数

在线人数统计一般用在有互动性的一些场景,比如论坛;也用在比较实时的应用,比如直播等等。对不同的场景做出不同方案,符合一个应用的需要才是最重要的,而且要看对统计的需求是即时性还是非即时,应用的方式是长连接还是短连接方式。 对这里有些看法,写下来抛砖引玉。欢迎各位拍砖。

对于短链接(非即时)

应用场景

论坛、网站、博客等

技术方案

PHP + Redis

原理说明

1 在线统计做单独的服务,不和系统耦合。 2 用户第一次请求时记录Session,定期发送心跳来确定用户是否存在,超时则清理之。 3 有后台任务对Session进行过期回收,类似于gc的机制

问题

1 为什么不用Memcache/Memcached?Memcache无法保证原子性,虽然Memcached有原子性操作(感谢ryan提醒),但数据结构都比较简单,要实现计算比较麻烦。 2 为什么选用Redis?每秒十万级的读写, async写入磁盘,丰富的数据结构

实现代码

heartbeat.php # 10秒钟请求一次

session_start();
$id = session_id();
redis()->setex("session.{$id}", true, 10);

getcount.php

echo redis()->get("count");

gc.php # 每三分钟运行一次回收

$count = count(redis()->keys("session.*"));
redis()->set("count", $count);

优点

1 可应用于分布式应用,后台任务只需要部署在单点 2 方便植入应用,一串JS代码即可搞定

缺点

1 非实时,需要JS配合心跳 2 有GC过程

扩展思考

在线人数非常大(100W+)如何支撑? redis的秒写入/读取大概在10W左右,心跳决定了Redis的写入并发, 如果10秒钟心跳一次,则理想状态下单台Redis服务器所支撑的最大容量为10W*10秒=100W。如果超过100W该怎么做?个人认为要从两方面入手。

第一,分离Redis服务角色,将收集服务器任务单一,只做写操作,读操作通过slave来完成。

第二,增加收集服务器,采用一致性哈希来分布存储不同的SESSION。

对于长连接

应用场景

直播、聊天室等

技术方案

PHP + APC

原理说明

1 在用户请求时APC计数器+1,连接断开时计数器-1

问题

1 为何选用APC?无网络消耗,直接操作内存,读写性能不亚于Redis

实现过程:

plugin_count.php

apc_inc('count');
register_shutdown_function(function(){
apc_dec('count');
?>

count.php

echo apc_fetch('count');

优点

1 不依赖后台任务 2 代码量极少

缺点

1 需要开启APC扩展(APC开启可能会对应用造成一定影响)

2 shutdown函数只能注册一次,有先后顺序关系,如果应用已经使用,则要考虑改用钩子来实现,不方便植入

3 数据源分布在各个应用上,要实现在线总数,需要有服务汇总count.php

扩展思考

如何实现在线总人数? 既然要, 那么就必须要有一个总服务器来完成汇总,个人有两种方式:

一,定时收集每台服务器的在线人数,汇总到总服务器,可以pull或者push;

二,如果不需要单台server在线人数,可以将APC换