在使用HttpClient过程中, 对于链接超时以及请求超时的设置是必不可少的。
HttpClient httpClient = new HttpClient();
httpClient.getHttpConnectionManager().getParams().setConnectionTimeout(params.getConnectionTimeout());
httpClient.getHttpConnectionManager().getParams().setSoTimeout(params.getSoTimeout());
相对于客户端,服务端(譬如nginx)对于接收的多请求处理更为复杂。
请求数比较多的情况下,怎么判断每个请求的超时时间并加以处理?
- 将客户端的每个请求按照请求顺序依次放入有序集合(ArrayList)中,启动一个线程循环遍历该集合,如果发现有请求超时则中断该请求;
- 将客户端的每个请求依次放入排序集合(TreeMap)中,启动一个线程循环遍历每个元素,如果发现当前遍历的对象未超时,则停止后续遍历,已超时的则直接中断请求;
- 将客户端的每个请求按照超时时间存入排序阻塞集合(DelayQueue)中,启动线程获取集合数据时如果未到超时时间则阻塞线程。
分析以上三种方案:
- 每次需要循环遍历所有数据比对,如果说数据比较多的情况下耗时时间还是比较长的;如果数据比较少,并且超时时间未到,则cpu会消耗比较大(while(true));
- 不用遍历所有数据,但会跟1存在cpu消耗比较大的情况;
- 借鉴了2的优势,同时又不会浪费cpu资源。
现在可以看下三种方案的简单实现:
第一种方案
package tech.zhaohuijun;
/**
* Vincent 创建于 2017/03/22.
*/
public class Request {
//服务器收到请求时间,单位毫秒
private long receivetime;
//超时时间,单位毫秒
private long timeout;
//请求状态 1请求中2请求完成3请求超时
private int status=1;
//请求名称
private String name;
public Request(String name) {
this.name = name;
}
public void interrupt() {
this.status = 3;
System.out.println("请求"+this.getName()+"过期,已被中断");
}
public int getStatus() {
return status;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public void setStatus(int status) {
this.status = status;
}
public long getReceivetime() {
return receivetime;
}
public void setReceivetime(long receivetime) {
this.receivetime = receivetime;
}
public long getTimeout() {
return timeout;
}
public void setTimeout(long timeout) {
this.timeout = timeout;
}
}
package tech.zhaohuijun;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
/**
* Vincent 创建于 2017/3/22.
*/
public class ServerTimeoutThread implements Runnable {
private List<Request> requestList;
private CountDownLatch countDownLatch;
public ServerTimeoutThread(List<Request> requestList, CountDownLatch countDownLatch) {
this.requestList = requestList;
this.countDownLatch = countDownLatch;
}
@Override
public void run() {
while (true) {
for (Request request : requestList) {
if (request.getReceivetime() + request.getTimeout() <= System.currentTimeMillis() && request.getStatus() == 1) {
request.interrupt();
countDownLatch.countDown();
}
}
//为了测试cpu循环占用的问题,暂停500ms打印
try {
TimeUnit.MILLISECONDS.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("------");
}
}
}
package tech.zhaohuijun;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CountDownLatch;
/**
* Vincent 创建于 2017/3/22.
*/
public class Server {
private static final List<Request> REQUEST_LIST =new ArrayList<>();
public static void main(String[] args) throws InterruptedException {
//添加多个请求,超时时间不一样
long currentTimeMillis = System.currentTimeMillis();
//超时时间5s
Request request1=new Request("request-1");
request1.setReceivetime(currentTimeMillis);
request1.setTimeout(5000);
//超时时间15s
Request request2=new Request("request-2");
request2.setReceivetime(currentTimeMillis);
request2.setTimeout(15000);
//超时时间10s
Request request3=new Request("request-3");
request3.setReceivetime(currentTimeMillis);
request3.setTimeout(10000);
REQUEST_LIST.add(request1);
REQUEST_LIST.add(request2);
REQUEST_LIST.add(request3);
CountDownLatch countDownLatch=new CountDownLatch(REQUEST_LIST.size());
//启动线程处理超时
Thread thread=new Thread(new ServerTimeoutThread(REQUEST_LIST,countDownLatch));
thread.setDaemon(true);
thread.start();
countDownLatch.await();
}
}
输出:
------
------
------
------
------
------
------
------
------
------
请求request-1过期,已被中断
------
------
------
------
------
------
------
------
------
------
请求request-3过期,已被中断
------
------
------
------
------
------
------
------
------
------
请求request-2过期,已被中断
第二种方案
package tech.zhaohuijun;
/**
* Vincent 创建于 2017/03/22.
*/
public class Request {
//服务器收到请求时间,单位毫秒
private long receivetime;
//超时时间,单位毫秒
private long timeout;
//请求状态 1请求中2请求完成3请求超时
private int status=1;
//请求名称
private String name;
public Request(String name) {
this.name = name;
}
public void interrupt() {
this.status = 3;
System.out.println("请求"+this.getName()+"过期,已被中断");
}
public int getStatus() {
return status;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public void setStatus(int status) {
this.status = status;
}
public long getReceivetime() {
return receivetime;
}
public void setReceivetime(long receivetime) {
this.receivetime = receivetime;
}
public long getTimeout() {
return timeout;
}
public void setTimeout(long timeout) {
this.timeout = timeout;
}
}
package tech.zhaohuijun;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
/**
* Vincent 创建于 2017/3/22.
*/
public class ServerTimeoutThread implements Runnable {
private TreeMap<Long, Request> requestTreeMap;
private CountDownLatch countDownLatch;
public ServerTimeoutThread(TreeMap<Long, Request> requestTreeMap, CountDownLatch countDownLatch) {
this.requestTreeMap = requestTreeMap;
this.countDownLatch = countDownLatch;
}
@Override
public void run() {
while (true) {
Set<Map.Entry<Long, Request>> entrySet = requestTreeMap.entrySet();
for (Map.Entry<Long, Request> entry : entrySet) {
Request request = entry.getValue();
if (entry.getKey() > System.currentTimeMillis()) {
break;
}
if (request.getStatus() == 1) {
request.interrupt();
countDownLatch.countDown();
}
}
//为了测试cpu循环占用的问题,暂停500ms打印
try {
TimeUnit.MILLISECONDS.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("------");
}
}
}
package tech.zhaohuijun;
import java.util.TreeMap;
import java.util.concurrent.CountDownLatch;
/**
* Vincent 创建于 2017/3/22.
*/
public class Server {
private static final TreeMap<Long,Request> REQUEST_TREE_MAP =new TreeMap<>();
public static void main(String[] args) throws InterruptedException {
//添加多个请求,超时时间不一样
long currentTimeMillis = System.currentTimeMillis();
//超时时间5s
Request request1=new Request("request-1");
request1.setReceivetime(currentTimeMillis);
request1.setTimeout(5000);
//超时时间15s
Request request2=new Request("request-2");
request2.setReceivetime(currentTimeMillis);
request2.setTimeout(15000);
//超时时间10s
Request request3=new Request("request-3");
request3.setReceivetime(currentTimeMillis);
request3.setTimeout(10000);
REQUEST_TREE_MAP.put(request1.getReceivetime()+request1.getTimeout(),request1);
REQUEST_TREE_MAP.put(request2.getReceivetime()+request2.getTimeout(),request2);
REQUEST_TREE_MAP.put(request3.getReceivetime()+request3.getTimeout(),request3);
CountDownLatch countDownLatch=new CountDownLatch(REQUEST_TREE_MAP.size());
//启动线程处理超时
Thread thread=new Thread(new ServerTimeoutThread(REQUEST_TREE_MAP,countDownLatch));
thread.setDaemon(true);
thread.start();
countDownLatch.await();
}
}
输出:
------
------
------
------
------
------
------
------
------
------
请求request-1过期,已被中断
------
------
------
------
------
------
------
------
------
------
请求request-3过期,已被中断
------
------
------
------
------
------
------
------
------
------
请求request-2过期,已被中断
第三种方案:
package tech.zhaohuijun;
import java.util.concurrent.Delayed;
import java.util.concurrent.TimeUnit;
/**
* Vincent 创建于 2017/03/22.
*/
public class Request implements Delayed {
//服务器收到请求时间,单位毫秒
private long receivetime;
//超时时间,单位毫秒
private long timeout;
//请求状态 1请求中2请求完成3请求超时
private int status=1;
//请求名称
private String name;
public Request(String name) {
this.name = name;
}
public void interrupt() {
this.status = 3;
System.out.println("请求"+this.getName()+"过期,已被中断");
}
public int getStatus() {
return status;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public void setStatus(int status) {
this.status = status;
}
public long getReceivetime() {
return receivetime;
}
public void setReceivetime(long receivetime) {
this.receivetime = receivetime;
}
public long getTimeout() {
return timeout;
}
public void setTimeout(long timeout) {
this.timeout = timeout;
}
@Override
public long getDelay(TimeUnit unit) {
return unit.convert(this.getReceivetime()+this.getTimeout()-System.currentTimeMillis(),TimeUnit.MILLISECONDS);
}
@Override
public int compareTo(Delayed o) {
if(o == null || ! (o instanceof Request)) return 1;
if(o == this) return 0;
Request s = (Request)o;
long l1 = this.getReceivetime() + this.getTimeout();
long l2 = s.getReceivetime() + s.getTimeout();
if (l1 > l2) {
return 1;
}else if (l1 == l2) {
return 0;
}else {
return -1;
}
}
}
package tech.zhaohuijun;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.DelayQueue;
/**
* Vincent 创建于 2017/3/22.
*/
public class ServerTimeoutThread implements Runnable {
private DelayQueue<Request> requestDelayQueue;
private CountDownLatch countDownLatch;
public ServerTimeoutThread(DelayQueue<Request> requestDelayQueue, CountDownLatch countDownLatch) {
this.requestDelayQueue = requestDelayQueue;
this.countDownLatch = countDownLatch;
}
@Override
public void run() {
while (true) {
Request request = null;
try {
request = requestDelayQueue.take();
if (request.getStatus() == 1) {
request.interrupt();
countDownLatch.countDown();
}
System.out.println("------");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
package tech.zhaohuijun;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.DelayQueue;
/**
* Vincent 创建于 2017/3/22.
*/
public class Server {
private static final DelayQueue<Request> REQUEST_DELAY_QUEUE =new DelayQueue<>();
public static void main(String[] args) throws InterruptedException {
//添加多个请求,超时时间不一样
long currentTimeMillis = System.currentTimeMillis();
//超时时间5s
Request request1=new Request("request-1");
request1.setReceivetime(currentTimeMillis);
request1.setTimeout(5000);
//超时时间15s
Request request2=new Request("request-2");
request2.setReceivetime(currentTimeMillis);
request2.setTimeout(15000);
//超时时间10s
Request request3=new Request("request-3");
request3.setReceivetime(currentTimeMillis);
request3.setTimeout(10000);
REQUEST_DELAY_QUEUE.put(request1);
REQUEST_DELAY_QUEUE.put(request2);
REQUEST_DELAY_QUEUE.put(request3);
CountDownLatch countDownLatch=new CountDownLatch(REQUEST_DELAY_QUEUE.size());
//启动线程处理超时
Thread thread=new Thread(new ServerTimeoutThread(REQUEST_DELAY_QUEUE,countDownLatch));
thread.setDaemon(true);
thread.start();
countDownLatch.await();
}
}
输出:
请求request-1过期,已被中断
------
请求request-3过期,已被中断
------
请求request-2过期,已被中断
------