1.4K Star 7.6K Fork 1.9K

GVP狮子的魂 / ip2region

 / 详情

高并发访问,响应速度慢。

已完成
创建于  
2022-09-05 08:16

调用如下代码:RegionUtil.getRegion(ip) 方法。持续最高5个并发请求。平均比不加多了大约1.5s。
public class RegionUtil
{
private static final Logger log = LoggerFactory.getLogger(RegionUtil.class);

private static final String JAVA_TEMP_DIR = "java.io.tmpdir";

// file(完全基于文件的查询),vectorIndex(缓存VectorIndex索引),content(缓存整个xdb数据)
// 本工具类仅支持使用“缓存整个xdb数据”方式处理。(file和vectorIndex方式非线程安全,需要将工具类优化为不同线程不同对象方式解决。)
private static final String SEARCHER_TYPE = "content";

static Searcher searcher = null;

/**
 * 初始化IP库
 */
static
{
    try
    {
        // 因为jar无法读取文件,复制创建临时文件
        String dbPath = RegionUtil.class.getResource("/ip2region/ip2region.xdb").getPath();
        File file = new File(dbPath);
        if (!file.exists())
        {
            String tmpDir = System.getProperties().getProperty(JAVA_TEMP_DIR);
            dbPath = tmpDir + "ip2region.xbd";
            file = new File(dbPath);
            ClassPathResource cpr = new ClassPathResource("ip2region" + File.separator + "ip2region.xdb");
            InputStream resourceAsStream = cpr.getInputStream();
            if (resourceAsStream != null)
            {
                FileUtils.copyInputStreamToFile(resourceAsStream, file);
            }
        }

        if ("file".equals(SEARCHER_TYPE)) {
            searcher = Searcher.newWithFileOnly(dbPath);
        } else {
            byte[] cBuff;
            if ("vectorIndex".equals(SEARCHER_TYPE)) {
                cBuff = Searcher.loadVectorIndexFromFile(dbPath);
                searcher = Searcher.newWithVectorIndex(dbPath, cBuff);
            } else if ("content".equals(SEARCHER_TYPE)) {
                cBuff = Searcher.loadContentFromFile(dbPath);
                searcher = Searcher.newWithBuffer(cBuff);
            } else {
                throw new IOException("invalid cache policy `" + SEARCHER_TYPE + "`, options: file/vectorIndex/content");
            }
        }
        log.info("bean [{}]", searcher);
    }
    catch (Exception e)
    {
        log.error("init ip region error:{}", e);
    }
}

/**
 * 解析IP
 *
 * @param ip
 * @return
 */
public static String getRegion(String ip)
{
    try
    {
        if (searcher == null || StringUtils.isEmpty(ip))
        {
            log.error("Searcher is null");
            return StringUtils.EMPTY;
        }
        long startTime = System.currentTimeMillis();
        // 检查ip
        Searcher.checkIP(ip);
        String result = searcher.search(ip);
        long endTime = System.currentTimeMillis();
        log.debug("region use time[{}] result[{}]", endTime - startTime, result);
        return result;

    }
    catch (Exception e)
    {
        log.error("error:{}", e);
    }
    return StringUtils.EMPTY;
}

}

评论 (7)

无敌才是王道 创建了任务
无敌才是王道 修改了描述
展开全部操作日志

仔细检查你的代码逻辑,问题很明显,你先打个日志确保逻辑如你所构思的。
xdb文件在jar里面就先读取出来,整个xdb buffer起来。
或者直接丢到jar外面。

大佬,初始化ip的地方加了三个打印,验证执行顺序。小弟不理解的是xdb于第一次访问的时候缓存起来了,请求也正常,就是部分外网访问的时候慢。
static
{
try
{
// 因为jar无法读取文件,复制创建临时文件
String dbPath = RegionUtil.class.getResource("/ip2region/ip2region.xdb").getPath();
File file = new File(dbPath);
if (!file.exists())
{
log.info("==============11111111111111==============");
String tmpDir = System.getProperties().getProperty(JAVA_TEMP_DIR);
dbPath = tmpDir + "ip2region.xbd";
file = new File(dbPath);
ClassPathResource cpr = new ClassPathResource("ip2region" + File.separator + "ip2region.xdb");
InputStream resourceAsStream = cpr.getInputStream();
if (resourceAsStream != null)
{
log.info("==============222222222222==============");
FileUtils.copyInputStreamToFile(resourceAsStream, file);
}
}

        if ("file".equals(SEARCHER_TYPE)) {
            searcher = Searcher.newWithFileOnly(dbPath);
        } else {
            byte[] cBuff;
            if ("vectorIndex".equals(SEARCHER_TYPE)) {
                cBuff = Searcher.loadVectorIndexFromFile(dbPath);
                searcher = Searcher.newWithVectorIndex(dbPath, cBuff);
            } else if ("content".equals(SEARCHER_TYPE)) {
                log.info("==============33333333333333==============");
                cBuff = Searcher.loadContentFromFile(dbPath);
                searcher = Searcher.newWithBuffer(cBuff);
            } else {
                throw new IOException("invalid cache policy `" + SEARCHER_TYPE + "`, options: file/vectorIndex/content");
            }
        }
        log.info("bean [{}]", searcher);
    }
    catch (Exception e)
    {
        log.error("init ip region error:{}", e);
    }
}

日志:
11:27:18.639 [schedule-pool-1] INFO c.h.c.u.RegionUtil - [,43] - ==============11111111111111==============
11:27:18.639 [schedule-pool-1] INFO c.h.c.u.RegionUtil - [,51] - ==============222222222222==============
11:27:18.761 [schedule-pool-1] INFO c.h.c.u.RegionUtil - [,64] - ==============33333333333333==============
11:27:18.774 [schedule-pool-1] INFO c.h.c.u.RegionUtil - [,71] - bean [org.lionsoul.ip2region.xdb.Searcher@57f30478]
11:27:18.775 [schedule-pool-1] INFO c.h.c.u.RegionUtil - [getRegion,99] - region use time[0] result[0|0|0|Ě͸IP|Ě͸IP]
11:27:18.779 [schedule-pool-1] INFO sys-user - [run,55] - [10.81.4.91]0 Ě͸IP[admin][Logout][͋³ö³ɹ¦]
11:27:41.400 [schedule-pool-1] INFO c.h.c.u.RegionUtil - [getRegion,99] - region use time[0] result[0|0|0|Ě͸IP|Ě͸IP]
11:27:41.400 [schedule-pool-1] INFO sys-user - [run,55] - [10.81.4.91]0 Ě͸IP[admin][Success][µǂ¼³ɹ¦]
11:27:41.475 [http-nio-8080-exec-3] INFO c.h.c.u.RegionUtil - [getRegion,99] - region use time[0] result[0|0|0|Ě͸IP|Ě͸IP]
11:28:36.928 [schedule-pool-2] INFO c.h.c.u.RegionUtil - [getRegion,99] - region use time[0] result[0|0|0|Ě͸IP|Ě͸IP]
11:28:36.928 [schedule-pool-2] INFO sys-user - [run,55] - [10.81.4.91]0 Ě͸IP[admin][Logout][͋³ö³ɹ¦]
11:28:54.813 [schedule-pool-1] INFO c.h.c.u.RegionUtil - [getRegion,99] - region use time[0] result[0|0|0|Ě͸IP|Ě͸IP]
11:28:54.814 [schedule-pool-1] INFO sys-user - [run,55] - [10.81.4.91]0 Ě͸IP[admin][Success][µǂ¼³ɹ¦]
11:28:54.834 [http-nio-8080-exec-17] INFO c.h.c.u.RegionUtil - [getRegion,99] - region use time[0] result[0|0|0|Ě͸IP|Ě͸IP]

不理解为何需要预先加载,然后静态代码块加载一次不可以吗?并且只是访问响应速度慢,但是测试确实很快。大佬求解

第一,你这里是有个 bug 吧:

String tmpDir = System.getProperties().getProperty(JAVA_TEMP_DIR);
dbPath = tmpDir + "ip2region.xbd";
file = new File(dbPath);

检测用的 xdb,临时文件怎么又变成了 xbd 了。

第二,getRegion方法对于 基于文件 和 vector 索引的方法都是 非线程安全的 ,文档有明确说明,你这每一次拷贝一个 xdb 怎么快的起来。

仔细想清楚自己的使用逻辑在具体产品中的执行情况,我觉得这个问题与项目本身无关

大佬, 我这边 知道文件和vector是非线程安全的,所以配置的是content。文件和vector只是暂时写了判断,但是没有构思具体以后时候使用。( 明确项目里一直使用的content方式 ),xbd这个bug确实没发现,感谢大佬,有无可能是这个bug导致查询慢,我去验证一下。

// file(完全基于文件的查询),vectorIndex(缓存VectorIndex索引),content(缓存整个xdb数据)
// 本工具类仅支持使用“缓存整个xdb数据”方式处理。(file和vectorIndex方式非线程安全,需要将工具类优化为不同线程不同对象方式解决。)
private static final String SEARCHER_TYPE = "content";

还有一个就是我代码里比您的示例多了一个方法,
Searcher.checkIP(ip);

并且content只会加载一次,初始化代码放到了静态代码块里

狮子的魂 任务状态待办的 修改为已完成

登录 后才可以发表评论

状态
负责人
里程碑
Pull Requests
关联的 Pull Requests 被合并后可能会关闭此 issue
分支
开始日期   -   截止日期
-
置顶选项
优先级
参与者(2)
5187 lionsoul 1578914315
C
1
https://gitee.com/lionsoul/ip2region.git
git@gitee.com:lionsoul/ip2region.git
lionsoul
ip2region
ip2region

搜索帮助