“这是我参与8月更文挑战的第7天,活动详情查看:8月更文挑战”
前言
前段时间遇到这样一个问题,我们调第三方发短信接口,在业务峰值的时候,经常会有些短信没有发出去。找第三方排查之后发现,发短信接口接口限流了。原因是,发短信接口要求我们传一套账号密码,这个账号密码是第三方提供的。第三方针对这个账号密码做了限流,一套账号密码只有20QPS。尝试和第三方提供商沟通,看看能不能把我们这套账号密码的QPS提上去,得到的反馈是,20QPS是死的,无法针对某个账号来扩大,但是可以给我们提供多个账号。这里就不点名吐槽这个提供商了。咱们只能逆来顺受了。
分析
方案一 试错法
当使用第一套账号发送请求时,如果报了限流错误,捕获该错误。尝试用第二套账号发送请求,如果还报了限流错误,同样捕获错误用下一套账号发送请求。以此类推。
问题
很容易可以看出来,最坏的情况下,想要请求成功,需要请求n次。而且在业务高峰时,对于靠前的账号的压力会很大。而在业务低谷时,后面的账号几乎用不到。
方案二 顺序轮询(取模)
问题
首先需要有一个数字类型的id,且是有序的。才能做到顺序轮询。
最终方案 链表队列
所有账号形成一个链表,每次只从取头节点,取完之后删除本节点,再把本节点放到尾节点上,这样本来第二个节点就成了头节点。这样就不依靠外部来循环,让账号本身形成循环做到顺序轮训的效果。
链表队列源码
public class LinkList<T> {
private Node head = null;
class Node {
Node next = null;
T data;
Node(T data) {
this.data = data;
}
}
/**
* 插入数据
*
* @param data 数据
*/
public void addNode(T data) {
Node newNode = new Node(data);
if (head == null) {
head = newNode;
return;
}
Node tmp = head;
while (tmp.next != null) {
tmp = tmp.next;
}
tmp.next = newNode;
}
/**
* 获取头节点,再把获取到的节点置为尾节点
* @return
*/
public synchronized T get() {
T data = head.data;
Node tmp = head;
while (tmp.next != null) {
tmp = tmp.next;
}
tmp.next = head;
head = head.next;
tmp.next.next = null;
return data;
}
}