Node.js无用户注册系统如何获取用户IP实现文章浏览量去重统计
基于IP去重的文章浏览量统计实现方案
第一步:正确获取用户真实IP地址
IP获取逻辑要匹配你的应用部署场景,避免拿到代理服务器的IP导致统计失效:
- 无反向代理的本地/直连部署场景
Express框架直接取req.ip,Koa框架取ctx.ip,Node.js原生HTTP服务可以用req.socket.remoteAddress获取IP - 有Nginx/CDN等反向代理的场景
首先需要在反向代理层配置透传真实IP,以Nginx为例,在对应的站点配置块中添加以下参数:
然后在应用框架开启代理信任:Express添加配置proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;app.set('trust proxy', 1),Koa添加配置app.proxy = true,框架会自动从代理透传的请求头中提取真实IP,避免用户伪造请求头造假IP。
第二步:设计MongoDB存储结构
推荐用两个集合实现,避免单文档存储内容过大:
- 文章集合
articles新增views字段,类型为Number,默认值为0,存储文章累计浏览量 - 新建IP浏览记录集合
article_views,字段设计如下:articleId:关联的文章ID,加单字段索引ipAddress:访问用户的IP地址viewAt:访问时间,可用于定期清理过期记录
- 给
article_views集合创建联合唯一索引,避免同一IP重复记录同一文章的访问:db.article_views.createIndex({ articleId: 1, ipAddress: 1 }, { unique: true })
第三步:实现中间件逻辑
以Express框架为例,中间件示例代码如下:
// 已提前定义好Mongoose模型:ArticleModel对应文章集合,ArticleViewModel对应浏览记录集合 const viewCountMiddleware = async (req, res, next) => { // 仅匹配动态文章请求,可根据自身路由规则调整判断条件 if (req.method !== 'GET' || !req.path.startsWith('/articles/')) return next(); const articleId = req.params.id; const userIp = req.ip; // 框架已开启代理信任,直接取真实IP try { // 尝试插入浏览记录,唯一索引冲突时会抛出错误 await ArticleViewModel.create({ articleId, ipAddress: userIp, viewAt: new Date() }); // 插入成功说明是该IP首次访问当前文章,浏览量+1 await ArticleModel.updateOne({ _id: articleId }, { $inc: { views: 1 } }); } catch (err) { // 11000为MongoDB唯一索引冲突错误码,代表该IP已访问过,无需处理 if (err.code !== 11000) { console.error('浏览量统计异常:', err); } } next(); };
用唯一索引冲突判断重复访问,比先查询再插入的逻辑性能更高,也天然避免了并发请求的竞态问题。
注意事项
- 同一公网IP下的多个用户会被判定为同一个访问主体,这是无用户体系下用IP去重的固有缺陷,仅能实现近似统计
- 可以定期清理3个月以上的浏览记录,降低存储成本,同时允许老用户隔一段时间后访问重新计数,更贴近真实浏览情况
- 如果使用了云CDN服务,需要在CDN控制台配置开启真实IP透传,不同厂商的请求头名称可能有差异,按对应文档配置即可
内容的提问来源于stack exchange,提问作者user16832151
相关产品推荐
相关产品推荐

