在使用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 ListrequestList; private CountDownLatch countDownLatch; public ServerTimeoutThread(List 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 ListREQUEST_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 TreeMaprequestTreeMap; private CountDownLatch countDownLatch; public ServerTimeoutThread(TreeMap requestTreeMap, CountDownLatch countDownLatch) { this.requestTreeMap = requestTreeMap; this.countDownLatch = countDownLatch; } @Override public void run() { while (true) { Set > entrySet = requestTreeMap.entrySet(); for (Map.Entry 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 TreeMapREQUEST_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 DelayQueuerequestDelayQueue; private CountDownLatch countDownLatch; public ServerTimeoutThread(DelayQueue 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 DelayQueueREQUEST_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过期,已被中断------