ThinkPHP实现统计在线用户的几种方式
关于统计在线用户的功能。以前也做过,用的一些比较简单的方法,但是缺点也很明显:精确统计和服务器、数据库压力之间要做出平衡。
所以想找一个既能精确统计又能不占用太多服务器资源的方法。先说说一些平常的做法:
一,每次用户操作更新其在线时间
这个方法很直接,在用户表里加一个字段update_time,每次用户进行操作,都更新这个字段为当前时间,一般是在一个被所有Action继承的基类里写这个操作。
然后定义一个过期时间,比如10分钟,表示10分钟没进行任何操作的用户默认为不在线。这样,统计当前在线用户的sql语句大概是这样
select count(*) from think_user where update_time>now()-10*60
优点:实现简单,通俗易懂
缺点:1,对“在线”的定义模糊,万一用户看一篇文章时间比较长,10分钟内没进行任何操作,他就被忽略了;
2,如果user表数据量很大,那效率将极差.
二,将在线用户单独放入一张表
对于方法一的改进。新建一张表think_inline,字段有user_id、update_time,每次用户操作时,先判断表里有没有该用户的记录,没有则新增,有就更新update_time。
并同时加上删除失效数据操作
这样,统计在线就可以直接count这张表就行了。而且这表的数据量不会很大(至少要比用户表小的多)
delete from think_inline where update_time
优点:减少数据库压力
缺点:仍然对“在线”的定义不准确
三,用JS定时器
这个方法是综合了一和二。新建一张表think_inline,也是在基类中定义每次用户操作时更新时间,参考二的做法。
不同之处是,在每个html模板里,加上一个js定时器,setInterval('updateTime', 10*3600);每隔10分钟发送一次ajax请求,更新update_time字段。这样,即使用户在一个页面停留时间过长,也不会被误认为不在线了。并且可以通过减少请求的间隔,来增加精确度,当然了,对服务器的压力就更大了。
优点:对在线的判断较为准确
缺点:仍然不能既精确又不增加服务器压力,必须在两者之间进行取舍。
四,使用TP的SessionDb驱动进行最优化设计
这也就是网上有人说的session存入数据库的方法,这种方法优点很多。具体做法是。。。
1,为什么要将session存入数据库?
session是存储在服务器的一组临时数据。一般情况下,我们在做用户登录时,会将用户数据存入session。这样,在任何页面都可以方便调用,而且 每个客户端会产生唯一的session_id,不会混肴。并且在关闭浏览器后,服务器会有session回收机制,自动删除过期session。
这是session的优点:唯一性、方便调用、不会过多占用资源。但是也有缺点:在客户端是以cookie方式保存的,禁用cookie就没用了。
那么,服务器是如何存放session的呢?他是默认将session以文件的方式保存在硬盘上的。可是,对于我们码农来说,操作数据库要比读文件方便的多,并且可以对session数据进行各种操作。
而统计在线用户人数就是通过统计有多少条session记录来实现的。
2,如何把session存入数据库?
TP的SessionDb驱动就实现了这个功能。原理就是通过改写PHP默认的session操作来实现,核心函数session_set_save_handler(),有兴趣的可以研究一下。该驱动将session的增、读、取、和删都放入了数据库。
使用方法也很简单:
1,建表,驱动的注释里的sql语句运行下就好
2,添加配置:
//Session配置 'SESSION_TYPE' => 'db', //数据库存储session 'SESSION_TABLE' => 'think_session', //存session的表 'SESSION_EXPIRE' => 600, //session过期
这样,只要我们在程序里使用了session()函数,数据库里就会有记录。
3,利用数据库session实现统计在线用户
1,统计在线总人数
$map = array('session_expire'=>array('gt',NOW_TIME)); $inline = D('Session')->where($map)->count();
2,统计游客(未登录)人数
$map = array('session_expire'=>array('gt',NOW_TIME),'session_data'=>array('eq','')); $huiyuan = D('Session')->where($map)->count();
3,统计会员(已登录)人数
$map = array('session_expire'=>array('gt',NOW_TIME),'session_data'=>array('neq','')); $huiyuan = D('Session')->where($map)->count();
4,判断一个用户是否在线。
在用户表里新增一个字段:session_id。
(1)在登录操作里,保存该用户的session_id,
$session_id = session_id(); D('User')->where(array('id'=>$user_id))->save('session_id'=>$session_id);
(2)检查session表里是否存在该session_id,未过期并且有值,
$map = array('session_id'=>$session_id,'session_expire'=>array('gt',NOW_TIME),'session_data'=>array('neq','')); $res = D('Session')->where($map)->find();
4,总结
1,实现步骤:用户表新增字段保存session_id;使用TP的SessionDb驱动。是不是很简单?
2,优点:
(1)比上面三种方法对数据库和服务器的压力都小,操作简单
(2)能够区分在线用户是会员还是游客(session_data字段是否有值),discuz就是这样做的
(3)可以通过删除某用户的session记录,实现将其“踢下线”的功能
3,缺点:
(1)仍然不能精确统计,只能说,XX秒内在线多少人
4,注意事项:
(1)由于该方法的SessionDb驱动必须使用session()函数才能触发,所以必须配置自动开启session(默认就是开启)。TP在执行流程里会使用session()函数,所以你什么都不写,session数据也会存入数据库。
(2)因为数据库的session数据是不会自己删除的,所以对于过期的数据,必须调用session()方法才会删。
这也就意味着,如果你的网站一个人都没有访问,那么数据库的过期session记录会一直存在。
也就是由于这种机制,对于一些突发事件(用户直接X浏览器、直接关机、发生地震……),在其关闭浏览器的一段时间(session过期时间)后,其他用户对网站的访问,会触发session回收,删除过期记录。所以,就不怕统计出来的数据不准确了。
当然了,就算没人访问,你也可以在count时加上session_expire>time()来统计。
但是也有误差,就是session过期时间,5分钟后过期,就有5分钟的误差。时间设的越小,对数据库的操作就越频繁;越大,精确度就越低。
(3)这个session过期时间就意味着,如果用户5分钟内不进行任何操作,其就会自动退出登录。所以,为了用户体验好,请加上cookie自动登录功能。目前官网就是这么做的。
(4)评论里有人提到,验证码也会存入session,所以我们判断的时候,就不能值统计有值的记录了。
需要先获取有值的数据,再判断里面有没有保存用户信息的参数名。虽然session_data字段是用二进制存储的,但是查询出来就是一个字符串。
比如,我们用的user下标来保存的用户信息,
session(,$data);
(5)由于每种浏览器都有各自的session机制,所以,如果一个人在一台电脑上同时开了5种浏览器,则数据库会保存5条不同的记录
实际使用时,仍需要考虑搜索引擎的误差。在其抓取我们的页面时,也会产生session。
五,最终结论
由于HTTP协议的限制:请求完成后就会断开与客户端的连接。所以实际上,
我们根本无法精确而实时地统计在线人数
!尽管有各种各样的方法来增加统计的精确度,然而都是治标不治本。唯有放弃HTTP协议,使用“长连接”的链接方式,才能精确判断用户在还是离 。

低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
- 上一篇
消息队列rabbitmq
为什么用消息队列 举例 比如在一个企业里,技术老大接到boss的任务,技术老大把这个任务拆分成多个小任务,完成所有的小任务就算搞定整个任务了。 那么在执行这些小任务的时候,可能有一个环节很费时间,并且优先级很低,推迟完成也不影响整个任务运转,那么技术老大就会将这个很费时间,且不重要的任务,丢给他的小弟去解决,自己继续完成其他任务。 转化为计算机思想 那个技术老大就是一个 程序系统,那个小弟就是消息队列。 当程序系统发现某些任务耗费时间且优先级较低,迟点完成也不影响整个任务,就把这个任务丢给消息队列。 场景 在程序系统中,例如外卖系统,订单系统,库存系统,优先级较高 发红包,发邮件,发短信,app消息推送等任务优先级很低,很适合交给消息队列去处理,以便于程序系统更快的处理其他请求。 消息队列工作流程 消息队列一般有三个角色: 队列服务端 队列生产者 队列消费者 消息队列工作流程就如同一个流水线,有产品加工,一个输送带,一个打包产品 输送带就是 不停运转的消息队列服务端 加工产品的就是 队列生产者 在传输带结尾打包产品的 就是队列消费者 队列产品 RabbitMQ Erlang编写的消息队...
- 下一篇
云原生之容器安全实践
概述 云原生(Cloud Native)是一套技术体系和方法论,它由2个词组成,云(Cloud)和原生(Native)。云(Cloud)表示应用程序位于云中,而不是传统的数据中心;原生(Native)表示应用程序从设计之初即考虑到云的环境,原生为云而设计,在云上以最佳状态运行,充分利用和发挥云平台的弹性和分布式优势。 云原生的代表技术包括容器、服务网格(Service Mesh)、微服务(Microservice)、不可变基础设施和声明式API。更多对于云原生的介绍请参考CNCF/Foundation。 笔者将“云原生安全”抽象成如上图所示的技术沙盘。自底向上看,底层从硬件安全(可信环境)到宿主机安全 。将容器编排技术(Kubernetes等)看作云上的“操作系统”,它负责自动化部署、扩缩容、管理应用等。在它之上由微服务、Service Mesh、容器技术(Docker等)、容器镜像(仓库)组成。它们之间相辅相成,以这些技术为基础构建云原生安全。 我们再对容器安全做一层抽象,又可以看作构建时安全(Build)、部署时安全(Deployment)、运行时安全(Runtime)。 在美团内...
相关文章
文章评论
共有0条评论来说两句吧...
文章二维码
点击排行
推荐阅读
最新文章
- MySQL8.0.19开启GTID主从同步CentOS8
- SpringBoot2配置默认Tomcat设置,开启更多高级功能
- Windows10,CentOS7,CentOS8安装MongoDB4.0.16
- Red5直播服务器,属于Java语言的直播服务器
- Linux系统CentOS6、CentOS7手动修改IP地址
- Springboot2将连接池hikari替换为druid,体验最强大的数据库连接池
- SpringBoot2整合MyBatis,连接MySql数据库做增删改查操作
- Jdk安装(Linux,MacOS,Windows),包含三大操作系统的最全安装
- SpringBoot2全家桶,快速入门学习开发网站教程
- Eclipse初始化配置,告别卡顿、闪退、编译时间过长