首页 新闻 搜索 专区 学院

如何利用线程池解决调用第三接口出现 429 too many requests问题?

0
悬赏园豆:20 [待解决问题]

做报表统计的时候,需要调用一个第三方接口根据IP或许该IP的实际地址。
我先写了一个接口,从数据库中取出每条的ip 再调用第三方接口进行查询,把查到的地址信息存入数据库。
测试的时候出现429 too many requests问题,于是按照同事的建议做了些优化,并添加了线程池,最后测试的时候,没有出现因为 429 too many requests 问题而直接报500的错,但是 更新数据库中的数据1500条数据,一共花了一个小时,控制台打印显示大概是,每更新二三十条数据(调用二三十次第三方接口)它就会因为频繁请求等问题而停顿,尝试再次发起请求。控制台打印如下

2020-08-14 17:38:38 org.apache.http.impl.execchain.RetryExec-97[pool-4-thread-3]
INFO: I/O exception (org.apache.http.NoHttpResponseException) caught when processing request to {}->http://xxx.xxx.xx.xx:xxxx->http://ip-api.com:80: The target server failed to respond 
2020-08-14 17:38:38 org.apache.http.impl.execchain.RetryExec-113[pool-4-thread-3]
INFO: Retrying request to {}->http://xxx.xxx.xx.xx:xxxx->http://ip-api.com:80 

我主要想问的就是:
如何利用线程池解决调用第三接口出现 429 too many requests的问题 减少花费的时间,1500条数据更新花一小时有点长 希望大佬们能给我说一下解决思路,贴一下大概的代码 求求了!
或者
不使用线程池的话有没有其他方法可以解决这个问题
<br/><br/>
第三方接口: http://ip-api.com/json/ip地址?lang=zh-CN
<br/><br/>
下面是线程池相关部分的代码

 RejectedExecutionHandler handler = new RejectedExecutionHandler() {
            @Override
            public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
                if(!executor.isShutdown()){
                    try{
                        executor.getQueue().put(r);
                    }catch (InterruptedException e){
                        e.printStackTrace();
                    }
                }
            }
        };
//线程池
        ThreadPoolExecutor pool = new ThreadPoolExecutor(10, 50, 30*1000,TimeUnit.MILLISECONDS, new LinkedBlockingDeque<Runnable>(1000),handler);
 //循环要查的数据列表
for (ModuleQueryLogEntity moduleQueryLogEntity : moduleQueryLogDtos){
            pool.execute(() -> {
                try {
                    ModuleQueryUrlResponse response = new ModuleQueryUrlResponse();
		//传入ip地址调用第三方接口查询
                    response = getUrlResponse(moduleQueryLogEntity.getIp(), ModuleQueryUrlResponse.class);
		//如果查询成功,把查到的信息存入数据库
                    if(response.getStatus().equals(SUCCESS)){
                        moduleQueryLogEntity.setCity(response.getCity());
                        moduleQueryLogEntity.setCountry(response.getCountry());
                        moduleQueryLogRepository.save(moduleQueryLogEntity);
                    }
		//沉睡
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            });
        }
				
//下面是调用第三方接口的代码
public <T> T getUrlResponse(String ip,Class<T> responseType){
        HttpHeaders headers = new HttpHeaders();
        headers.add("Retry-After","100");
        StringBuilder url = new StringBuilder(FINE_IP);
        url.append(ip);
        url.append("?lang=zh-CN");
        log.info(url.toString());
        ResponseEntity<String> response = restTemplate.exchange(
                url.toString(),
                HttpMethod.GET,
                new HttpEntity<String>(headers),
                String.class);
        return JSON.parseObject(response.getBody(), responseType);
    }
				
				
北年的主页 北年 | 初学一级 | 园豆:108
提问于:2020-08-14 17:16
< >
分享
所有回答(2)
0

都告诉你问题了么,你就减少请求啊。把函数放到队列,对方肯定会告诉你api<->token并发的数量。如此你对应开合适(小于等于)该数量的线程,线程永远干检查队列、检出队列、执行队列的事。实际就是并行转串行。

花飘水流兮 | 园豆:11974 (专家六级) | 2020-08-14 17:27

我线程池的最大线程数小于了它的并发量,然后您说的检查队列、检出队列、执行队列是要怎么弄,我是想能不能查几次 停几秒再查,就不让它一直频繁请求,因为我每次拿ip去查都得调一次这个接口,它也没法给我批量的查ip,只能一次次的查,假设我查1000条,就必须调一千次。抱歉 我是个非常菜的菜鸡,大佬可以再给我详细解释一下吗?或者给一点伪代码 /(ㄒoㄒ)/~~

支持(0) 反对(0) 北年 | 园豆:108 (初学一级) | 2020-08-14 17:57

@北年:

List<任务> tasks;

启动一个Work线程_函数

{

while(IsRunning){

lock(tasks){

检查队列有无;

如果有:var task = tasks.Dequeue,

}

如果有:task.execute() ;

如果没有:Thread.Sleep(0);

}

}

如果允许最大3个,那么  for(i=0;i<3;i++){启动一个Work线程_函数};

以上是自管理方式;

也应该有第三方的(为什么不用内置的,通常内置的 是 整个程序范围内的管理),直接设定最大数量3,任务只管往里面扔就行了。

支持(0) 反对(0) 花飘水流兮 | 园豆:11974 (专家六级) | 2020-08-14 18:20
0

这跟线程池没啥关系,是第三方的网站对客户端限速了。可以尝试用代理IP的方式绕过限速

比如一个IP 1小时可以扒 1500条,那么100个IP 1小时就可以扒 150000条。这只是理想情况,实际需要测试,找出服务端的限速规则

sweetjian | 园豆:276 (菜鸟二级) | 2020-08-21 12:44
清除回答草稿
   您需要登录以后才能回答,未注册用户请先注册