内部的放到gitlab pages的博客,需要统计PV,不蒜子不能准确统计,原因在于gitlab的host设置了strict-origin-when-cross-origin, 导致不蒜子不能正确获取referer,从而PV只能统计到网站的PV。

为了方便统计页面的PV,这里简单的写了一个java程序,用H2作为db存储,实现类似不蒜子的后端。

step0

下载编译:

git clone https://github.com/jadepeng/simplepv
cd simplepv
mvn package -DskipTests

部署 web 程序

    java -jar simplepv-0.0.1-SNAPSHOT.jar --spring.profiles.active=prod

输出

2021-12-02 20:25:49.014  INFO 35916 --- [           main] com.jadepeng.simplepv.SimplepvApp        : The following profiles are active: prod
2021-12-02 20:25:53.585 INFO 35916 --- [ main] c.j.simplepv.config.WebConfigurer : Web application configuration, using profiles: prod
2021-12-02 20:25:53.589 INFO 35916 --- [ main] c.j.simplepv.config.WebConfigurer : Web application fully configured
2021-12-02 20:26:02.580 INFO 35916 --- [ main] org.jboss.threads : JBoss Threads version 3.1.0.Final
2021-12-02 20:26:02.697 INFO 35916 --- [ main] com.jadepeng.simplepv.SimplepvApp : Started SimplepvApp in 15.936 seconds (JVM running for 16.79)
2021-12-02 20:26:02.707 INFO 35916 --- [ main] com.jadepeng.simplepv.SimplepvApp :
----------------------------------------------------------
Application 'simplepv' is running! Access URLs:
Local: http://localhost:58080/
External: http://172.1.1.12:58080/
Profile(s): [prod]
----------------------------------------------------------

本程序默认使用 h2 作为存储,所以不用另外安装 mysql。

step1

引用 client.js, 也可以直接放入到网页中

var bszCaller, bszTag, scriptTag, ready;

var t,
e,
n,
a = !1,
c = []; // 修复Node同构代码的问题
if (typeof document !== 'undefined') {
(ready = function (t) {
return (
a || 'interactive' === document.readyState || 'complete' === document.readyState
? t.call(document)
: c.push(function () {
return t.call(this);
}),
this
);
}),
(e = function () {
for (var t = 0, e = c.length; t < e; t++) c[t].apply(document);
c = [];
}),
(n = function () {
a ||
((a = !0),
e.call(window),
document.removeEventListener
? document.removeEventListener('DOMContentLoaded', n, !1)
: document.attachEvent &&
(document.detachEvent('onreadystatechange', n), window == window.top && (clearInterval(t), (t = null))));
}),
document.addEventListener
? document.addEventListener('DOMContentLoaded', n, !1)
: document.attachEvent &&
(document.attachEvent('onreadystatechange', function () {
/loaded|complete/.test(document.readyState) && n();
}),
window == window.top &&
(t = setInterval(function () {
try {
a || document.documentElement.doScroll('left');
} catch (t) {
return;
}
n();
}, 5)));
} bszCaller = {
fetch: function (t, e) {
var n = 'SimplePVCallback' + Math.floor(1099511627776 * Math.random());
t = t.replace('=SimplePVCallback', '=' + n);
(scriptTag = document.createElement('SCRIPT')),
(scriptTag.type = 'text/javascript'),
(scriptTag.defer = !0),
(scriptTag.src = t),
document.getElementsByTagName('HEAD')[0].appendChild(scriptTag);
window[n] = this.evalCall(e);
},
evalCall: function (e) {
return function (t) {
ready(function () {
try {
e(t),
scriptTag && scriptTag.parentElement && scriptTag.parentElement.removeChild && scriptTag.parentElement.removeChild(scriptTag);
} catch (t) {
console.log(t), bszTag.hides();
}
});
};
},
}; const fetch = siteUrl => {
bszTag && bszTag.hides();
bszCaller.fetch(`${siteUrl}/api/pv/${window.btoa(location.href)}?jsonpCallback=SimplePVCallback`, function (t) {
bszTag.texts(t), bszTag.shows();
});
}; bszTag = {
bszs: ['site_pv', 'page_pv'],
texts: function (n) {
this.bszs.map(function (t) {
var e = document.getElementById('busuanzi_value_' + t);
e && (e.innerHTML = n[t]);
});
},
hides: function () {
this.bszs.map(function (t) {
var e = document.getElementById('busuanzi_container_' + t);
e && (e.style.display = 'none');
});
},
shows: function () {
this.bszs.map(function (t) {
var e = document.getElementById('busuanzi_container_' + t);
e && (e.style.display = 'inline');
});
},
}; if (typeof document !== 'undefined') {
fetch('http://localhost:8080/');
}

上面 fetch 的地址,填写 webserver 部署后的地址。

step2

在需要显示 pv 的地方

<span id="busuanzi_container_site_pv">本站总访问量<span id="busuanzi_value_site_pv"></span>次</span>
<span id="busuanzi_container_page_pv">本文总阅读量<span id="busuanzi_value_page_pv"></span>次</span>

原理

当前只统计了PV,未统计uv,后续有空可以增加。

原理,每个url存储一条记录

public class PV implements Serializable {

    @Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "id")
private Long id; @Column(name = "url")
private String url; @Column(name = "pv")
private Integer pv; }

统计PV时,lock url的host,获取pv对象,如果不存在则新增,然后pv+1

注意: 这里用了个lock,防止并发出错

@Override
public PVDTO increment(String url) {
Lock lock = null;
// 简单锁一下
try {
URL uri = new URL(url);
lock = this.lock(uri.getHost(), 30000);
if (lock == null) {
throw new RuntimeException("请稍后重试");
} PV pv = incrementPV(url); PV sitePv = incrementPV(uri.getHost()); return new PVDTO(pv.getPv(), sitePv.getPv());
} catch (MalformedURLException e) {
throw new RuntimeException("url not support");
} finally {
if (lock != null) {
this.releaseLock(lock);
}
}
} private PV incrementPV(String url) {
PV pv = this.pVRepository.findFirstByUrl(url).orElse(new PV().url(url).pv(0));
pv.setPv(pv.getPv() + 1);
this.pVRepository.save(pv);
return pv;
}

开源

代码地址: https://github.com/jadepeng/simplepv

欢迎使用。

实现一个简单的类似不蒜子的PV统计器的更多相关文章

  1. 一个简单的类似Vue的双向绑定

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...

  2. 手把手教你从零写一个简单的 VUE

    本系列是一个教程,下面贴下目录~1.手把手教你从零写一个简单的 VUE2.手把手教你从零写一个简单的 VUE--模板篇 今天给大家带来的是实现一个简单的类似 VUE 一样的前端框架,VUE 框架现在应 ...

  3. 计算机程序的思维逻辑 (60) - 随机读写文件及其应用 - 实现一个简单的KV数据库

    57节介绍了字节流, 58节介绍了字符流,它们都是以流的方式读写文件,流的方式有几个限制: 要么读,要么写,不能同时读和写 不能随机读写,只能从头读到尾,且不能重复读,虽然通过缓冲可以实现部分重读,但 ...

  4. 在iOS中实现一个简单的画板App

    在这个随笔中,我们要为iPhone实现一个简单的画板App. 首先需要指出的是,这个demo中使用QuarzCore进行绘画,而不是OpenGL.这两个都可以实现类似的功能,区别是OpenGL更快,但 ...

  5. 使用MongoDB和JSP实现一个简单的购物车系统

    目录 1 问题描述  2 解决方案  2.1  实现功能  2.2  最终运行效果图  2.3  系统功能框架示意图  2.4  有关MongoDB简介及系统环境配置  2.5  核心功能代码讲解  ...

  6. nodejs实现一个简单的爬虫

    nodejs是js语言,实现一个爬出非常的方便. 步骤 1. 使用nodejs的request模块,获取目标页面的html代码:https://github.com/request/request 2 ...

  7. 3小时搞定一个简单的MIS系统案例Northwind,有视频、有源代码下载、有真相

    一.瞎扯框架.架构 楼主自从1998年从C语言.MASM.Foxbase开始学计算机开始接触这个行当16年以来,2001年干第一份与程序.软件.然后是各种屌的东西开始,差不多干了13年了,这13年来, ...

  8. 自己动手模拟开发一个简单的Web服务器

    开篇:每当我们将开发好的ASP.NET网站部署到IIS服务器中,在浏览器正常浏览页面时,可曾想过Web服务器是怎么工作的,其原理是什么?“纸上得来终觉浅,绝知此事要躬行”,于是我们自己模拟一个简单的W ...

  9. 【转】用C写一个简单病毒

    [摘要]在分析病毒机理的基础上,用C语言写了一个小病毒作为实例,用TURBOC2.0实现. [Abstract] This paper introduce the charateristic of t ...

随机推荐

  1. 重学c#系列——list(十二)

    前言 简单介绍一下list. 正文 这里以list为介绍. private static readonly T[] s_emptyArray = new T[0]; public List() { t ...

  2. JBOSS未授权访问漏洞利用

    1. 环境搭建 https://www.cnblogs.com/chengNo1/p/14297387.html 搭建好vulhub平台后 进入对应漏洞目录 cd vulhub/jboss/CVE-2 ...

  3. the Agiles Scrum Meeting 10

    会议时间:2020.4.18 20:00 1.每个人的工作 今天已完成的工作 个人结对项目增量开发组:完成自动创建仓库功能 issues:增量组:准备评测机制,增加仓库自动创建和管理 团队项目增量开发 ...

  4. WEB前端工程师如何做职业规划?

    对于一个WEB前端的职业规划,其实是有各种的答案,没有哪种答案是完全正确的,全凭自己的选择,只要是自己选定了,坚持去认真走,就好.在这里, 我只是 简要说一下自己对于这块儿内容的理解.有一个观点想要分 ...

  5. Prometheus基于文件的服务发现

    Prometheus基于文件的服务发现 一.基于文件的服务发现 1.prometheus.yml 配置文件的写法 2.file_sd 目录下的文件 3.配置结果 二.注意事项 三.参考链接 一.基于文 ...

  6. [火星补锅] 水题大战Vol.2 T2 && luogu P3623 [APIO2008]免费道路 题解

    前言: 如果我自己写的话,或许能想出来正解,但是多半会因为整不出正确性而弃掉. 解析: 这题算是对Kruskal的熟练运用吧. 要求一颗生成树.也就是说,最后的边数是确定的. 首先我们容易想到一个策略 ...

  7. hdu 1861 游船出租(模拟题,,水)

    题意: 现有公园游船租赁处请你编写一个租船管理系统. 当游客租船时,管理员输入船号并按下S键,系统开始计时:当游客还船时,管理员输入船号并按下E键,系统结束计时. 船号为不超过100的正整数.当管理员 ...

  8. PicGo+Gitee(码云)中的404错误解决方案

    今天在用PicGo配置Gitee时,出现了404问题,记录一下解决方案. 安装与配置 PicGo默认是不支持Gitee的,只能通过安装插件来进行支持.我这里安装的插件是Gitee. 在图床设置---& ...

  9. google-chrome 启动报错 nss_util.cc(627)] NSS_VersionCheck("3.26") failed. NSS >= 3.26 is required

    一.错误情况 报错如下: [0807/144244.712736:FATAL:nss_util.cc(627)] NSS_VersionCheck("3.26") failed. ...

  10. 【Python+postman接口自动化测试】(6)Chrome开发者工具

    Chrome开发者工具 Elements: HTML元素面板,用于定位查看元素源代码 Console: js控制台面板,js命令行,查看前端日志 Sources: 资源面板,用于断点调试js Netw ...