超时机制的简单实现

原创
2017/03/22 11:26
阅读数 87

在使用HttpClient过程中, 对于链接超时以及请求超时的设置是必不可少的。

HttpClient httpClient = new HttpClient();
httpClient.getHttpConnectionManager().getParams().setConnectionTimeout(params.getConnectionTimeout());
httpClient.getHttpConnectionManager().getParams().setSoTimeout(params.getSoTimeout());

相对于客户端,服务端(譬如nginx)对于接收的多请求处理更为复杂。

请求数比较多的情况下,怎么判断每个请求的超时时间并加以处理?

  1. 将客户端的每个请求按照请求顺序依次放入有序集合(ArrayList)中,启动一个线程循环遍历该集合,如果发现有请求超时则中断该请求;
  2. 将客户端的每个请求依次放入排序集合(TreeMap)中,启动一个线程循环遍历每个元素,如果发现当前遍历的对象未超时,则停止后续遍历,已超时的则直接中断请求;
  3. 将客户端的每个请求按照超时时间存入排序阻塞集合(DelayQueue)中,启动线程获取集合数据时如果未到超时时间则阻塞线程。

分析以上三种方案:

  1. 每次需要循环遍历所有数据比对,如果说数据比较多的情况下耗时时间还是比较长的;如果数据比较少,并且超时时间未到,则cpu会消耗比较大(while(true));
  2. 不用遍历所有数据,但会跟1存在cpu消耗比较大的情况;
  3. 借鉴了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过期,已被中断
------
展开阅读全文
打赏
0
0 收藏
分享
加载中
更多评论
打赏
0 评论
0 收藏
0
分享
返回顶部
顶部