起因

前段时间发现博客右边的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. odoo视图中常用widget

    widget="statusbar" 头部状态条标签 widget="email" 电子邮件地址标签 widget="selection" ...

  2. Okhttp3源码解析(5)-拦截器RetryAndFollowUpInterceptor

    ### 前言 回顾: [Okhttp的基本用法](https://www.jianshu.com/p/8e404d9c160f) [Okhttp3源码解析(1)-OkHttpClient分析](htt ...

  3. MYSQL批量导入数据报:[Err] 2006 - MySQL server has gone away 解决方法

    使用values 后接批量数据插入,因mysql 系统参数设置导致失败(数据量过大).可通过临时修改系统参数来解决,对系统安全性无影响: set global max_allowed_packet=1 ...

  4. python 18 re模块

    目录 re 模块 1. 正则表达式 2. 匹配模式 3. 常用方法 re 模块 1. 正则表达式 \w 匹配字母(包含中文)或数字或下划线 \W 匹配非字母(包含中文)或数字或下划线 \s 匹配任意的 ...

  5. HDU 6319

    题意略. 思路:倒着使用单调队列,大的放在前,小的放在后. 详见代码: #include<bits/stdc++.h> using namespace std; typedef long ...

  6. 安装hadoop集群--hdfs

    安装hadoop集群--hdfs 大数据软件 链接:https://pan.baidu.com/s/1-3PYLHMgvvONawJq55hstQ 提取码:izqf 准备一台干净的虚拟机-centos ...

  7. python3 使用urllib报错urlopen error EOF occurred in violation of protocol (_ssl.c:841)

    python3源码: import urllib.request from bs4 import BeautifulSoup response = urllib.request.urlopen(&qu ...

  8. HTML5学习手册

    目录 HTML 简介 HTML 简介 什么是 HTML? HTML 标签 HTML 文档 = 网页 基本的 HTML 标签 - 四个实例 HTML 标题 HTML 段落 HTML 链接 HTML 图像 ...

  9. codeforce#483div2C-Finite or not?数论,GCD

    传送门:http://codeforces.com/contest/984/problem/C 这道题 题意:求q/p是否能用k进制有限表示小数点后的数:   思路:数学推理:     1.首先把q/ ...

  10. lightoj 1046 - Rider(bfs)

    A rider is a fantasy chess piece that can jump like a knight several times in a single move. A rider ...