起因

前段时间发现博客右边的FlagCounter计数器突然没了,又看到了博客园封杀了FlagCounter的消息,有点摸不着头脑。于是上FlagCounter的网站上看了一眼,发现最近出现的来自新国家访问居然来自台湾。又经过一轮百度,看到有博主发表声明说由于国家立场拒绝使用FlagCounter了。于是我赶紧把公告栏清空了,又苦于没有替代品,就想着干脆自己写一个。

FlagCounter将台湾显示为国家


显示效果对比(左:FlagCounter 右:自制)

前端显示的是HTML,比FlagCounter的图片格式要清晰很多。

开发环境以及线上环境

  • SpringBoot 2.1.8
  • Redis 5.0.x
  • 域名 + SSL证书 (博客园需要https)

使用到的接口及开源数据

  • www.taobao.com/help/getip.php 用于获取IP地址(可以直接用下面的接口获取,之前疏忽了)
  • http://ip-api.com/json/ 用于解析IP地理位置(只提供HTTP,所以请求需要通过后端发送)
  • https://github.com/mukeshsolanki/country-picker-android 国旗图片来源

开发思路

  用户访问时通过淘宝接口在前端获取访客IP地址,传给后端。后端向ip-api请求ip地址解析后的国家代码,并记录到Redis数据库。同时向后端请求访客数据,并显示在页面上。

  其实在找国家代码标准的数据的时候,发现有两种标准:一种是ISO-3166标准,还有一种是GB/T 2659-2000。区别在于,前者把香港、台湾等地区标为HK、TW,后者直接都标记为CN。可能FlagCounter也只是被这个标准坑了一波,把所有的国家代码标注的地区都当做了国家处理。

关键代码

package com.qf;  

import com.alibaba.fastjson.JSON;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.ZSetOperations;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate; import java.util.HashMap;
import java.util.Set; @CrossOrigin
@RestController
@Transactional
public class Controller { @Autowired
private StringRedisTemplate redisTemplate; private RestTemplate restTemplate = new RestTemplate(); // 接口隐藏
@RequestMapping("xxxxxx")
public String getVisitor(){
// 取出排名前20的国家以及total(总数),所以是0-20共21个
Set<ZSetOperations.TypedTuple<String>> typedTupleSet = redisTemplate.opsForZSet().reverseRangeWithScores("visitor", 0, 20);
return JSON.toJSON(typedTupleSet).toString();
} // 接口隐藏
@RequestMapping("xxxxxx")
public void addVisitor(@PathVariable String ip){
// 请求ip-api接口,获取ip所属地信息
IPaddr iPaddr = restTemplate.getForObject("http://ip-api.com/json/"+ip, IPaddr.class, new HashMap<>());
String code = iPaddr.getCountryCode();
// 转换为GB/T 2659-2000标准
if (code.equals("HK") || code.equals("TW") || code.equals("MO")) {
code = "CN";
}
// 更新Redis中的记录
redisTemplate.opsForZSet().incrementScore("visitor", code, 1);
redisTemplate.opsForZSet().incrementScore("visitor", "total", 1);
}
}

FlagCounter被封杀?自己实现一个简单的多国访客计数器的更多相关文章

  1. 用mkdocs在gitee码云上建立一个简单的文档博客

    利用mkdocs建立简单的文档博客 一.概述 MkDocs 是一个用于创建项目文档的 快速, 简单 , 完美华丽 的静态站点生成器. 文档源码使用 Markdown 来撰写, 用一个 YAML 文件作 ...

  2. 从开发到部署,使用django创建一个简单可用的个人博客

    本文参考于: 简书-Django搭建简易博客教程:http://www.jianshu.com/p/d15188a74104 自强学堂-Django基础教程:http://www.ziqiangxue ...

  3. 一个简单的hexo搭建博客网站的故事

    首先安装hexo mkdir hexo #创建一个文件夹 cd hexo #切换到hexo目录下面 npm install -g hexo-cli npm install hexo --save 然后 ...

  4. 爬虫浅谈一:一个简单c#爬虫程序

    这篇文章只是简单展示一个基于HTTP请求如何抓取数据的文章,如觉得简单的朋友,后续我们再慢慢深入研究探讨. 图1: 如图1,我们工作过程中,无论平台网站还是企业官网,总少不了新闻展示.如某天产品经理跟 ...

  5. 哪种缓存效果高?开源一个简单的缓存组件j2cache

    背景 现在的web系统已经越来越多的应用缓存技术,而且缓存技术确实是能实足的增强系统性能的.我在项目中也开始接触一些缓存的需求. 开始简单的就用jvm(java托管内存)来做缓存,这样对于单个应用服务 ...

  6. 在Openfire上弄一个简单的推送系统

    推送系统 说是推送系统有点大,其实就是一个消息广播功能吧.作用其实也就是由服务端接收到消息然后推送到订阅的客户端. 思路 对于推送最关键的是服务端向客户端发送数据,客户端向服务端订阅自己想要的消息.这 ...

  7. ASP.NET Aries 入门开发教程2:配置出一个简单的列表页面

    前言: 朋友们都期待我稳定地工作,但创业公司若要躺下,也非意念可控. 若人生注定了风雨飘摇,那就雨中前行了. 最机开始看聊新的工作机会,欢迎推荐,创业公司也可! 同时,趁着自由时间,抓紧把这系列教程给 ...

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

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

  9. 如何开发一个简单的HTML5 Canvas 小游戏

    原文:How to make a simple HTML5 Canvas game 想要快速上手HTML5 Canvas小游戏开发?下面通过一个例子来进行手把手教学.(如果你怀疑我的资历, A Wiz ...

随机推荐

  1. 一文了解Python常见的序列化操作

    关于我 一个有思想的程序猿,终身学习实践者,目前在一个创业团队任team lead,技术栈涉及Android.Python.Java和Go,这个也是我们团队的主要技术栈. Github:https:/ ...

  2. 如何在Python中表示一个对象

    关于我 一个有思想的程序猿,终身学习实践者,目前在一个创业团队任team lead,技术栈涉及Android.Python.Java和Go,这个也是我们团队的主要技术栈. Github:https:/ ...

  3. java杨辉三角

    public static void main(String[] args) { Scanner scanner=new Scanner(System.in); System.out.println( ...

  4. tomcat8 编写字符编码Filter过滤器无效问题

    做一个解决全站的字符编码过滤器,过滤器类和配置如下: 过滤器类: package com.charles.web.filter; import java.io.IOException; import ...

  5. 在windows7系统下如何查看及升级powershell到3.0版本

    最近在学习.net core web api 开发,用到了Mysql.Data.EntityFrameworkCore框架,在根据数据库表生成对应实体类时提示必须先升级本机powershell到3.0 ...

  6. stage_ros的world文件配置方法

    官方文档参阅:http://rtv.github.io/Stage/modules.html stage_ros是一个基于stage的2D模拟器,用于ROS的仿真测试.虽然现在越来越多的人在使用gaz ...

  7. atcoder D - Game on Tree(树形dp+尼姆博弈)

    题目链接:http://agc017.contest.atcoder.jp/tasks/agc017_d 题解:简单的树上的尼姆博弈,这个应该看的出来然后就是简单的树形dp然后异或一下就行. #inc ...

  8. 牛客小白赛5 无关(relationship) 容斥原理(计算因子数的模板)

    链接:https://www.nowcoder.com/acm/contest/135/A来源:牛客网 若一个集合A内所有的元素都不是正整数N的因数,则称N与集合A无关.   给出一个含有k个元素的集 ...

  9. 【Offer】[22] 【链表中倒数第k个结点】

    题目描述 思路分析 测试用例 Java代码 代码链接 题目描述 输入一个链表,输出该链表中倒数第k个结点. 思路分析 采用双指针的方法,第一个指针首先向前移动k-1个位置,第二个指针指向头节点,然后将 ...

  10. Vue2.x-社交网络程序项目的总结

    最近几天一直在学习Vue的课程,通过这个项目进行进一步的学习Vue方面的知识.掌握如何使用Vue搭建前端,如何请求Node.js写好的后端接口. 一.实现前后端连载 首先在后端的文件中  vue  i ...