限流器的另类用法

248 阅读1分钟

前言

昨天收到产品的一个紧急呼救,需要帮助业务导出19-23年moka系统中人才库下存在的并且在业务系统a中的人员信息,唰的一声接口文档甩过来,刚开始粗略看了下接口就开始写逻辑,因为这个功能很简单,就是取两个系统的交集嘛,下面是刚开始脑袋里的想法

image.png  是不是很简单大伙儿,我也是这么想的,于是开始codeding,由于是紧急需求并且只是用来导出一份excel,所以我在本地写了一个接口,接口的入参就是业务系统中19-22年的所有moka applicationId,但是我不知道那些applicationId已经进入到了人才库了,于是我下面开始着手从moka系统中查找数据,下面是我的代码(简化了意思):

String bodyStr = "";
                List<String> infos = new LinkedList<>();
                Map<String,String> map = new HashMap<>();
                //m oka接口所需要的参数
                map3.put("archivedAtStart","2019-06-01"); // 时间范围
                map3.put("archivedAtEnd","2022-12-01");
                map3.put("talentPoolIds","2333"); // 人才库id
                // 分页参数
                map3.put("nextCursor", pageNumber);
            while (true) {
                okhttp3.Response response = HttpUtil.doGet(serviceUrl2, map2, map3);
                bodyStr = Objects.requireNonNull(response.body()).string();
                ResponseMoka mokaResponse = JSONObject.parseObject(bodyStr, ResponseMoka.class);
                if (mokaResponse == null || StringUtils.isBlank(mokaResponse.getData())) {
                    break;
                }
                EntryMokas entryMokas = JSONObject.parseObject(mokaResponse.getData(), EntryMokas.class);
                if (CollectionUtils.isEmpty(entryMokas.getCandidates())) {
                    log.info("entryMokas is empty");
                }
                List<Owners> candidates = entryMokas.getCandidates();
                List<String> phones = candidates.stream().map(Owners::getPhone).collect(Collectors.toList());
                infos.addAll(phones);
                pageNumber = entryMokas.getNextCursor();
                // 小于20 取完
                if (candidates.size() < 20) {
                    break;
                }
            }

 狠简单,就是去调接口20一批取出人家存着的所有数据,看着牟问题 但是在测试后发现,我司某业务对应人才库数据有几千多条(包含离职、在职),在取数据过程中发生了异常,通过日志发现,是moka对接口做了限流,一次最多发100次请求

image.png 啊呀,一分钟发100个请求怎么控制捏,如果让大伙儿来搞,大伙儿有什么办法嘛~ 欢迎xd闷评论区留言交流

 我当时的想法是moka限流了,那我自己给自己限流不就好了嘛,那问题来了,自己怎么限流呢,我想到了guvwa包里给我们提供了一个单机版的限流工具RateLimiter,关于RateLimiter作用是这样的:”通过设置许可证的速率来定义RateLimiter。在默认配置下,许可证会在固定的速率下被分配,速率单位是每秒多少个许可证。为了确保维护配置的速率,许可会被平稳地分配,许可之间的延迟会做调整”。

 意思就是设置每秒的qps,通过令牌桶算法对qps进行限流,通俗易懂的的讲就是在桶里放令牌,我以每秒存放1/N个令牌,当任务过来的时候拿上令牌就可以通过,而拿不到令牌的就放弃或等待(需要阻塞队列),每次发放令牌都是按照每秒1/N的速率发放的。

 好,60s1000个请求,那我的令牌产生的速率就是1.6,对代码进行改良,很简单

final RateLimiter rateLimiter = RateLimiter.create(1.6);


String bodyStr = "";
List<String> infos = new LinkedList<>();
Map<String,String> map = new HashMap<>();
//m oka接口所需要的参数
map3.put("archivedAtStart","2019-06-01"); 
map3.put("archivedAtEnd","2022-12-01");
map3.put("talentPoolIds","2333");
// 分页参数
map3.put("nextCursor", pageNumber);
while (true) {
// 获取令牌
rateLimiter.acquire();
okhttp3.Response response = HttpUtil.doGet(serviceUrl2, map2, map3);
bodyStr = Objects.requireNonNull(response.body()).string();
ResponseMoka mokaResponse = JSONObject.parseObject(bodyStr, ResponseMoka.class);
if (mokaResponse == null || StringUtils.isBlank(mokaResponse.getData())) {
    break;
}
EntryMokas entryMokas = JSONObject.parseObject(mokaResponse.getData(), EntryMokas.class);
if (CollectionUtils.isEmpty(entryMokas.getCandidates())) {
    log.info("entryMokas is empty");
}
List<Owners> candidates = entryMokas.getCandidates();
List<String> phones = candidates.stream().map(Owners::getPhone).collect(Collectors.toList());
infos.addAll(phones);
pageNumber = entryMokas.getNextCursor();
if (candidates.size() < 20) {
    break;
}
}

只是多了两行代码,但是却很好的避免了moka限流问题,只要我们的请求量和moka同步,就不会出现被限流情况~ &nesp;rateLimiter其实作用很大,我只是利用其一个小特性方便了这次的导出场景,大家可以参考文档了解一下参数,比如我这次用到的是稳定的吞吐率,里面还有预热的吞吐率等等 我是花和尚南北,欢迎大家一起交流~