欢迎访问悦橙教程(wld5.com),关注java教程。悦橙教程  java问答|  每日更新
页面导航 : > > 文章正文

redis~有序集合处理ip范围的查询问题,第一种方式就不多做介

来源: javaer 分享于  点击 46293 次 点评:195

redis~有序集合处理ip范围的查询问题,第一种方式就不多做介


目前有两种方式对 IP 以及归属地信息进行缓存:

  • 第一种是将起始 IP,结束 IP 以及中间所有 IP 转换成整型,然后以字符串方式,用转换后的 IP 作为 key,归属地信息作为 value 存入 Redis;
  • 第二种是采用有序集合和散列方式,首先将起始 IP 和结束 IP 添加到有序集合 ip2cityid,城市 ID 作为成员,转换后的 IP 作为分值,然后再将城市 ID 和归属地信息添加到散列 cityid2city,城市 ID 作为 key,归属地信息作为 value。

第一种方式就不多做介绍了,简单粗暴,非常不推荐。查询速度当然很快,毫秒级别,但缺点也十分明显,我用 1000 条数据做了测试,缓存时间长,大概 20 分钟,占用空间大,将近 1G。

下面介绍第二种方式直接看代码

python语言实现

# generate_to_redis.py
# -*- coding:utf-8 -*-
import time
import json
from redis import Redis
def ip_to_num(x):
    return sum([256 ** j * int(i) for j, i in enumerate(x.split('.')[::-1])])
# 连接 Redis
conn = Redis(host='127.0.0.1', port=6379, db=10)
start_time = time.time()
# 文件格式
# 1.0.0.0|1.0.0.255|澳大利亚|0|0|0|0
# 1.0.1.0|1.0.3.255|中国|0|福建省|福州市|电信
with open('./ip.merge.txt', 'r') as f:
    i = 1
    for line in f.readlines():
        item = line.strip().split('|')
        # 将起始 IP 和结束 IP 添加到有序集合 ip2cityid
        # 成员分别是城市 ID 和 ID + #, 分值是根据 IP 计算的整数值
        conn.zadd('ip2cityid', str(i), ip_to_num(item[0]), str(i) + '#', ip_to_num(item[1]) + 1)
        # 将城市信息添加到散列 cityid2city,key 是城市 ID,值是城市信息的 json 序列
        conn.hset('cityid2city', str(i), json.dumps([item[2], item[3], item[4], item[5]]))
        i += 1
end_time = time.time()
print 'start_time: ' + str(start_time) + ', end_time: ' + str(end_time) + ', cost time: ' + str(end_time - start_time)

java语言实现

以下是通过 Java 来实现将 IP 起始值和结束值通过有序集合存储,并根据传入的 IP 返回这个 IP 是否在 IP 范围集合中存在的示例代码:

  • redis中存储的图
  • java代码如下
package com.lind.redis;

import com.lind.redis.config.LettuceRedisAutoConfigure;
import lombok.extern.slf4j.Slf4j;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.test.context.junit4.SpringRunner;

/**
 * 关于ip地址范围检索的测试
 *
 * @author lind
 * @date 2024/3/6 11:14
 * @since 1.0.0
 */
@Slf4j
@RunWith(SpringRunner.class)
@SpringBootTest(classes = { LettuceConnectionFactory.class, LettuceRedisAutoConfigure.class })
public class IpRangeTest {

    private static final String IP_RANGE_KEY = "ip_ranges";

    @Autowired
    RedisTemplate redisTemplate;

    // 将 IP 地址转换为 long 类型的分数值
    public static double convertIPToScore(String ip) {
       String[] ipParts = ip.split("\\.");
       if (ipParts.length != 4) {
          throw new IllegalArgumentException("Invalid IP address format");
       }

       long score = 0;
       for (int i = 0; i < 4; i++) {
          long partValue = Long.parseLong(ipParts[i]);
          score = (score << 8) + partValue; // 将每个部分的值左移8位并相加
       }

       return score;
    }

    @Test
    public void testRangeIp() {
     redisTemplate.delete(IP_SCORE_RANGE_KEY);
		// 存储 IP 范围到 Redis
		storeIPScoreRangeToRedis("103.159.125.66", "103.159.125.177");
		storeIPScoreRangeToRedis("114.250.19.131", "116.250.19.255");
		storeIPScoreRangeToRedis("10.10.10.131", "10.10.20.255");
		storeIPScoreRangeToRedis("192.10.10.131", "192.10.10.255");

		// 检查用户真实 IP 是否在 IP 集合中
		String userIP = "114.250.19.132";
		boolean inBlacklist = isIpInRange(userIP);
		if (inBlacklist) {
			System.out.println(userIP + " is in the IP blacklist.");
		}
		else {
			System.out.println(userIP + " is not in the IP blacklist.");
		}

    }

	private void storeIPScoreRangeToRedis(String startIP, String endIP) {
		long startScore = convertIPToScore(startIP);
		double score = Double.valueOf(startScore);
		redisTemplate.opsForZSet().add(IP_SCORE_RANGE_KEY, startIP + "-" + endIP, score);
	}

    // 判断某个IP是否在ZSet的范围内
	// 通过起始值先过滤掉大于指定IP的,再剩下的集合进行遍历
	public boolean isIpInRange(String ipAddress) {
		ZSetOperations<String, String> opsForZSet = redisTemplate.opsForZSet();
		double ip = (double) convertIPToScore(ipAddress);
		Set<ZSetOperations.TypedTuple<String>> result = opsForZSet.rangeByScoreWithScores(IP_SCORE_RANGE_KEY, 0, ip);
		for (ZSetOperations.TypedTuple<String> o : result) {

			String[] arr = o.getValue().split("-");
			long start = convertIPToScore(arr[0]);
			long end = convertIPToScore(arr[1]);
			if (ip >= start && ip <= end) {
				System.out.println("find rangeL" + arr[0] + "-" + arr[1]);
				return true;
			}
		}

		return false;
	}

}

作者:仓储大叔,张占岭,
荣誉:微软MVP
QQ:853066980

支付宝扫一扫,为大叔打赏!

相关栏目:

用户点评