嗯……前阵子接了个活儿,需要做一个基于IP地址黑名单的分流网关。刚接到的时候心想iptables不就行了么,没想到一看客户给的IP黑名单规模……我擦……上亿个……

黑名单到了这个规模,就不得不考虑下优化的问题了。要知道从0.0.0.0到255.255.255.255,IP地址总共也只有232个,约43亿,除去不可能用于实际的地址以及内网地址之后就更少了,只有几分之一。上亿个IP地址的黑名单已经达到实际可用IP地址的几分之一了,而且还要实现大流量高性能查询,实在是……

最初打算是用redis来实现,而第一版程序也是这么实现的,不过可能是不熟悉redis,做出来的东西性能不够达标。

然后我想到一个点子……

技术需求如下:

IP地址由4个字节构成,从0.0.0.0到255.255.255.255,每个IP地址在网关只有两种状态:在/不在黑名单里。上面已经说过,IP地址由4个字节构成。那么我只需要构建一个232规模的字节数组,完成全IP地址到本地内存空间的映射。每个字节只存0或者1,表示对应的IP地址是不是黑名单IP即可。这么做需要4GB的内存。

进一步考虑,一个字节只存一个bool实在是太浪费,对这种映射关系稍做修改,每个IP地址对应到字节数组里一个字节的某个二进制位即可,这样就能把数据压缩到原先的八分之一,即512MB。这样32位的系统也可以胜任了(虽然提供的是64位系统)。

使用数据库技术,绕不过去的是查询和排序之类耗时的工作,对于如此庞大的黑名单库(其规模已经达到全IP地址数的几分之一),这是主要的性能消耗。但如果将IP地址映射到本地内存空间,那么直接就省掉了这个最消耗性能的操作,直接就能查到这个IP在不在黑名单里了。

好了,想法有了,实现起来也就是五分钟的事儿,我编写的版本如下(C实现,省略了由IP地址字符串转换成无符号32位整数的过程):

 #include "stdio.h"
#include "stdlib.h"
#include "string.h" #define BLACKLIST_FILE "d:\\dummy_ip.bin"//一个512MB大小的随机内容的二进制文件 char *test=new char[**];//IP地址映射到本地内存数据的数组
int init();
bool checkBlackList(unsigned long inputIP);
void setValue(unsigned long inputIP, bool inputValue); //全IP段IP黑名单快速查询 //原理:IP从0.0.0.0到255.255.255.255,总共2^32个IP地址。每个IP地址只有两个状态:在黑名单,或者不在
//因此最初设计是申请0x00000000到0xFFFFFFFF个字节的内存空间(4GB),建立全IP地址到内存的映射,每个字节存放一个二进位,存储该地址对应IP是不是黑名单IP
//经过压缩之后,每个字节存放8个二进制位,因此总空间可压缩到原先的八分之一,即512MB
//查询时,将IP地址的四个字节合组成一个int32并右移3位,得到该IP对应的字节,然后用这个int32的低三位确定字节里的二进制位,即是否是黑名单IP
//将IP转换成int32之后,单次查询仅需要1次内存直接访问和3次位操作
//适用于IP黑名单很大的情况 int main(int argc, char* argv[])
{
init();
for(int i=;i<;++i)
{ //生成随机的IP地址进行查询
unsigned char a=rand()%,b=rand()%,c=rand()%,d=rand()%;
unsigned long ip=a*b*c*d;
printf("IP:%u.%u.%u.%u is hit:%d\n",a,b,c,d,checkBlackList(ip));
}
return ;
} //数据初始化,将保存在本地文件的数据读取到内存里
int init()
{
FILE* fp = fopen(BLACKLIST_FILE,"r");
if (fp==NULL)
return ;
fgets(test,strlen(test),fp);
fclose(fp);
return ;
} //查询IP是否在黑名单里,仅仅需要三次位运算和一次内存访问
bool checkBlackList(unsigned long inputIP)
{
return test[inputIP>>] &(<<(inputIP & (unsigned long)));
} //设置黑名单IP的值。找到IP对应的字节,然后使用掩码和位运算设置对应的二进制位的值
void setValue(unsigned long inputIP, bool inputValue)
{
unsigned long byteIndex = inputIP >> ;
char maskByte = (char)(<<(inputIP & (unsigned long)));
test[byteIndex] = (inputValue?(test[byteIndex] | maskByte):(test[byteIndex] & (!maskByte)));
/*if(inputValue)//喜欢简洁,改成(:?)形式了,见上行
test[byteIndex] = test[byteIndex] | maskByte;
else
test[byteIndex] = test[byteIndex] & (!maskByte);*/
}

嗯,跑起来可比之前的redis实现快了不是一星半点。

大规模IP地址黑名单高性能查询实现的更多相关文章

  1. Python实现IP地址归属地查询

    一.使用淘宝IP地址库查询 使用淘宝的Rest API,可以快速查询IP地址的归属地: 图00-淘宝IP地址库RestAPI使用说明 图01-使用淘宝免费IP地址库-查询IP归属地 存在问题:淘宝的免 ...

  2. IP地址归属地查询

    http://www.ipip.net/download.html#ip_code 下载免费版 IP 地址数据库. 网站下面有官方给出的查找IP地址所属国家.省.市的办法. python版本列出 py ...

  3. Trie树的应用:查询IP地址的ISP

    1. 问题描述 给定一个IP地址,如何查询其所属的ISP,如:中国移动(ChinaMobile),中国电信(ChinaTelecom),中国铁通(ChinaTietong)?现有ISP的IP地址区段可 ...

  4. 手机归属地查询-IP地址查询-身份证查询-域名备案查询--Api接口

    使用这些接口是需要密钥的 公共密钥 appkey: 10003  secret: d1149a30182aa2088ef645309ea193bf  生成后sign: b59bc3ef6191eb9f ...

  5. 黄聪:分享几个免费IP地址查询接口(API)

    淘宝IP地址库 提供的服务包括:1. 根据用户提供的IP地址,快速查询出该IP地址所在的地理信息和地理相关的信息,包括国家.省.市和运营商.2. 用户可以根据自己所在的位置和使用的IP地址更新我们的服 ...

  6. 通过Web Service实现IP地址查询功能

    实例01 实现一个简单的Web服务访问 本实例将实现IP地址查询接口服务,根据用户传入的IP地址返回IP所在的省.市.地区,实例中将会用到IP地址库用于查询信息,由于数据较多,所以读者可在光盘资源文件 ...

  7. 分享几个免费IP地址查询接口(API)

    淘宝IP地址库 提供的服务包括:1. 根据用户提供的IP地址,快速查询出该IP地址所在的地理信息和地理相关的信息,包括国家.省.市和运营商.2. 用户可以根据自己所在的位置和使用的IP地址更新我们的服 ...

  8. 虚拟机下CentOS 配置IP地址的三种方法

    1.自动获取IP地址(我不是用的这种方法,不做过多介绍) 虚拟机使用桥接模式,相当于连接到物理机的网络里,物理机网络有DHCP服务器自动分配IP地址. #dhclient 自动获取ip地址命令 #if ...

  9. 虚拟机下CentOS 6.5配置IP地址的三种方法

    实验软件环境:虚拟机Vmware Workstation10.0 .CentOS 6.5 32位 1.自动获取IP地址 虚拟机使用桥接模式,相当于连接到物理机的网络里,物理机网络有DHCP服务器自动分 ...

随机推荐

  1. epoll的LT模式缺点

    本文为原创,转载请注明:http://www.cnblogs.com/gistao/ epoll提供了ET和LT两种模式,网上文章很多,这里只总结下LT模式下的两个缺点 epoll对fd的管理实现是用 ...

  2. jQuery插件开发的两种方法及$.fn.extend的详解

    jQuery插件开发分为两种: 1 类级别 类级别你可以理解为拓展jquery类,最明显的例子是$.ajax(...),相当于静态方法. 开发扩展其方法时使用$.extend方法,即jQuery.ex ...

  3. zookeeper的安装(图文详解。。。来点击哦!)

    zookeeper的安装(图文详解...来点击哦!) 一.服务器的配置 三台服务器: 192.168.83.133   sunshine 192.168.83.134   sunshineMin 19 ...

  4. jQuery中的Ajax几种请求方法

    在网上查的几种Ajax的请求的方法: jQuery 确实是一个挺好的轻量级的JS框架,能帮助我们快速的开发JS应用,并在一定程度上改变了我们写JavaScript代码的习惯.废话少说,直接进入正题,我 ...

  5. bootstrap-14

    基础导航条: 使用方法:1. 首先在制作导航的列表(<ul class="nav">)基础上添加类名"navbar-nav" 2.在列表外部添加一个 ...

  6. 启动odoo-10.0成功,但是访问时出错

    启动odoo-10.0显示成功 2017-01-05 06:49:48,211 532 INFO ? odoo: Odoo version 10.02017-01-05 06:49:48,211 53 ...

  7. 关于Unity中SteamVR_Controller.Input的错误

    当我在看某鸥的视频的时候,里面讲到用Unity做一个贪食蛇后,加入SteamVR插件,并且能用手柄控制蛇的移动.当我跟着上面一步一步做的时候,发现我代码都写完后,启动报错,而视频里面的老师讲的缺没有报 ...

  8. JS自动格式化输入的数字/千位分隔符

    <script> function cc(s){         if(/[^0-9\.]/.test(s)) return "invalid value";     ...

  9. 如何让老式浏览器支持html5新增的语义元素

    html5新增加了一些语义元素,如header, footer, nav, aritcle, aside,等等. 然而,有些老款浏览器无法识别这些元素,会把它们当成 inline 元素对待,这会导致一 ...

  10. wget: unable to resolve host address 解决办法

    vim /etc/resolv.conf 加上下面两句: nameserver 8.8.8.8 #google域名服务器nameserver 8.8.4.4 #google域名服务器