本帖最后由 ArayChou 于 2010-04-20 18:16:23 编辑

解决方案 »

  1.   


    /**
     * @author Aray Chou
     *         Email: Aray(dot)Chou(dot)CN(at)gmail(dot)com
     *         Replace "(dot)" with "." and replace "(at)" with "@"
     */
    package com.aray.core.common;/**
     * 常量
     */
    public class Constants
    { /** 访问频率控制,单位毫秒, 现在的值为30秒 */
    public static int REQUEST_CONTROL_PERIOD = 10 * 1000; /** 访问频率控制,在一端时间内的最大访问次数 */
    public static int REQUEST_CONTROL_ALLOW_TIMES = 10; /** 访问频率控制,最小不能访问的时间,这个时间段内,必须输入验证码后才能访问系统 */
    public static int REQUEST_CONTROL_BAN_TIME = 10 * 1000; /** 访问频率控制,检查时间间隔 */
    public static int REQUEST_CONTROL_CHECK_INTERVAL = 60 * 1000;}
      

  2.   


    /**
     * @author Aray Chou
     *         Email: Aray(dot)Chou(dot)CN(at)gmail(dot)com
     *         Replace "(dot)" with "." and replace "(at)" with "@"
     */
    package com.aray.core.filter;import java.lang.reflect.Field;
    import java.util.ArrayList;
    import java.util.Collections;
    import java.util.HashSet;
    import java.util.Hashtable;
    import java.util.Map;
    import java.util.Set;import com.aray.core.common.Constants;/**
     * <p>
     * 控制访问频率
     * </p>
     * 如果用户的ip在 {@link com.aray.core.common.Constants#REQUEST_CONTROL_PERIOD}内的请求数大于等于
     * {@link com.aray.core.common.Constants#REQUEST_CONTROL_ALLOW_TIMES},则标记此ip为"超出控制"
     * 超出控制的ip的用户必须输入验证码后马上访问系统。如果没有输入验证码,此状态在[{@link com.aray.core.common.Constants#REQUEST_CONTROL_BAN_TIME},
     * {@link com.aray.core.common.Constants#REQUEST_CONTROL_BAN_TIME} +
     * {@link com.aray.core.common.Constants#REQUEST_CONTROL_CHECK_INTERVAL}]后,系统自动解除控制。
     * 
     */
    public class RequestControl
    {
    /** 所有control对象,在此对象rehash的时候,会清除里面超时的control */
    private static MyHashtable<String, RequestControl> controls = new MyHashtable<String, RequestControl>(1000); /** 超过控制的control对象,系统会开线程自动清除里面超时的control */
    private static Set<RequestControl> outofControlObject = Collections.synchronizedSet(new HashSet<RequestControl>()); static
    {
    // 新开线程,清理超时的control
    Runnable run = new Runnable()
    {
    @Override
    public void run()
    {
    while (true)
    {
    ArrayList<RequestControl> timeout = new ArrayList<RequestControl>();
    for (RequestControl c : outofControlObject)
    {
    if (c.isTimeOut())
    timeout.add(c);
    } for (RequestControl c : timeout)
    c.timeOutClear(); try
    {
    Thread.sleep(Constants.REQUEST_CONTROL_CHECK_INTERVAL);
    }
    catch (InterruptedException e)
    {
    e.printStackTrace();
    }
    }
    }
    };
    Thread t = new Thread(run);
    t.setDaemon(true);
    t.setName("RequestControlCleaner");
    t.start();
    } /**
     * 检查客户端是否超过访问限制
     * 
     * @param ip
     *            客户端ip
     * @return 超过访问限制,返回true
     */
    public static boolean over(String ip)
    { RequestControl control = controls.get(ip); // 第一个访问,新建control对象
    if (control == null)
    {
    control = new RequestControl();
    controls.put(ip, control);
    return false;
    } // 已经超过控制
    if (control.outOfControl)
    return true; // 设置事件时间
    control.time = System.currentTimeMillis(); boolean result = !control.queue.appendCurentTime(); // 队列已满,超过访问控制
    if (result)
    {
    control.queue.reset();
    control.outOfControl = true; // 将超过控制的control对象放在这里,系统会定期检查,自动移除超时的control
    outofControlObject.add(control);
    } return control.outOfControl; } /**
     * 如果ban的时间超过 {@link com.aray.core.common.Constants.REQUEST_CONTROL_BAN_TIME}, 重置outOfControl为false,
     * 同时从outofControls中移出当前对象
     */
    protected void timeOutClear()
    { // 重置访问控制状态为不受限制
    if (isTimeOut())
    {
    outofControlObject.remove(this);
    this.outOfControl = false;
    }
    } /**
     * ban的时间是否超过限制
     * 
     * @return 是否超时
     */
    protected boolean isTimeOut()
    {
    return System.currentTimeMillis() - this.time >= Constants.REQUEST_CONTROL_BAN_TIME;
    } private RuquestControlQueue queue; /** 标志当前ip是否已经超出控制 */
    private boolean outOfControl = false; /** 事件发生时间 */
    private long time; private RequestControl()
    {
    this.queue = new RuquestControlQueue(Constants.REQUEST_CONTROL_ALLOW_TIMES);
    } /**
     * 消除警报
     * 
     * @param ip
     */
    public static void clearAlarm(String ip)
    {
    RequestControl control = controls.get(ip); if (control != null)
    control.outOfControl = false;
    } /**
     *重载 {@link java.util.Hashtable#rehash()},在rehash之前,现移除超时的control。
     *如果移除超时的control后,仍然需要rehash,调用HashTable.rehash()
     * 
     * @param <K>
     * @param <V>
     */
    static public class MyHashtable<K, V> extends Hashtable<K, V>
    {
    private static final long serialVersionUID = 1L; /**
     * @param initialCapacity
     * @param loadFactor
     */
    public MyHashtable(int initialCapacity, float loadFactor)
    {
    super(initialCapacity, loadFactor);
    } /**
     * 
     */
    public MyHashtable()
    {
    super(); } /**
     * @param initialCapacity
     */
    public MyHashtable(int initialCapacity)
    {
    super(initialCapacity);
    } /*
     * (non-Javadoc)
     * 
     * @see java.util.Hashtable#rehash()
     */
    @Override
    protected void rehash()
    {
    removeTimeout(); // 移除超时的control后,当前的大小
    int count = this.size(); // 通过反射的到HashMap.threshold
    int threshold = getThreshold(); // 移除后,如果仍然需要扩展,则扩展hashTable
    if (count >= threshold)
    super.rehash();
    } /**
     * 通过反射的到HashMap.threshold
     * 
     * @return
     */
    private int getThreshold()
    {
    int threshold = 0;
    try
    {
    Field field = this.getClass().getSuperclass().getDeclaredField("threshold");
    field.setAccessible(true);
    threshold = (Integer) field.get(this);
    }
    catch (Exception e)
    {
    System.out.println("System Error, probably wrong JRE version");
    e.printStackTrace();
    System.exit(-1);
    }
    return threshold;
    } /**
     * 删除超时的control
     */
    private void removeTimeout()
    {
    ArrayList<K> key = new ArrayList<K>(100); // 得到所有超时的key
    for (Map.Entry<K, V> entry : this.entrySet())
    {
    V value = entry.getValue();
    if (value instanceof RequestControl)
    {
    RequestControl control = (RequestControl) value;
    if (control.isTimeOut())
    {
    key.add(entry.getKey());
    }
    }
    } // 删除对应的control
    for (K k : key)
    {
    this.remove(k);
    }
    }
    }
    }
      

  3.   


    /**
     * @author Aray Chou
     *         Email: Aray(dot)Chou(dot)CN(at)gmail(dot)com
     *         Replace "(dot)" with "." and replace "(at)" with "@"
     */
    package com.aray.core.filter;import com.aray.core.common.Constants;/**
     * 请求控制队列。每有一个请求,移出队列过期的时间戳,然后往此队列里加入当前时间戳.
     * 如果队列已满,则认为超出请求控制的频度要求。
     * <p>
     * 算法:
     * <ul>
     * <li>用一个定长数组存储数据,first指向第一个元素。size表示总元素过数。</li>
     * <li>追加数据时候,有可能发生first+size>data.length的情况。这个时候,折回从数组开始插入数据</li>
     * </ul>
     * </p>
     */
    public class RuquestControlQueue
    {
    /** 第一个元素的下标 */
    protected int first; /** 队列的长度,总元素个数 */
    protected int size; /** 队列的元素 */
    protected long[] data; /**
     * @param length
     *            队列的长度
     */
    public RuquestControlQueue(int length)
    {
    data = new long[length];
    } /**
     * 移出过期数据,追加新的时间戳。
     * 
     * @return 如果队列已满,则追加失败,返回false.
     */
    public boolean appendCurentTime()
    {
    this.removeExpired();
    return this.append(System.currentTimeMillis());
    } /**
     * 追加数据
     * 
     * @param value
     * @return 队列满,这追加失败,返回false
     */
    public synchronized boolean append(long value)
    {
    if (size == data.length)
    return false; int position = (first + size) % data.length;
    data[position] = value;
    size++; return true; } /**
     * 重置队列,清空里面的数据
     */
    public synchronized void reset()
    {
    this.first = 0;
    this.size = 0;
    } /**
     * 删除 {@link com.aray.core.common.Constants.Constants.REQUEST_CONTROL_PERIOD}以前的过期数据。<br />
     */
    private void removeExpired()
    {
    long expired = System.currentTimeMillis() - Constants.REQUEST_CONTROL_PERIOD; // 删除过期的元素
    this.removeLess(expired);
    } /**
     * 移除小于等于value的数据
     * 
     * @param value
     */
    public synchronized void removeLess(long value)
    {
    // 删除过期的元素
    if (this.size > 0)
    {
    int p = this.find(value, this.first, this.size);
    if (p != -1)
    {
    // 第一个元素指针指向p后的元素,移除下标p及其以前的数组元素
    int newStart = (p + 1) % this.data.length;
    // 修改元素个数
    this.size = this.size - (newStart + this.data.length - this.first) % this.data.length;
    this.first = newStart;
    }
    }
    } public static void main(String[] args)
    {
    RuquestControlQueue q = new RuquestControlQueue(20);
    q.first = 19;
    for (int i = 1; i < 14; i++)
    q.append(i); for (int i = 0; i < q.data.length; i++)
    {
    System.out.print(q.data[i]);
    if (i == q.first)
    System.out.print('*');
    System.out.print(' ');
    }
    int xxx = 12;
    System.out.println("\n" + q.find(xxx));
    q.out();
    q.removeLess(xxx);
    q.out(); } /**
     * 在当前队列中,查找小于等于value的最后一个数据
     * 
     * @param value
     * @return 小于等于value的最后一个数据的下标
     */
    protected int find(long value)
    {
    return this.find(value, this.first, this.size);
    } /**
     * 折半查找,从数组position开始的size个数据中,查找最后一个小于等于value的元素
     * 
     * @param value
     * @param position
     *            开始位置
     * @param size
     * @return 最后一个小于等于value的元素的下标
     */
    protected int find(long value, int position, int size)
    {
    if (this.size == 0 || this.data[this.first] > value /* value比第一个还小 */)
    return -1; // value比最后一个值还大
    int lastPosition = (this.first + this.size - 1) % this.data.length;
    if (this.data[lastPosition] <= value)
    return lastPosition; if (size == 1)
    {
    if (this.data[position] <= value)
    return position;
    else
    return -1;
    } int half = size / 2;
    int halfPosition = (position + half) % this.data.length;
    if (this.data[halfPosition] > value)
    return find(value, position, half);
    else
    return find(value, halfPosition, size - half);
    } /**
     * 输出队列,测使用
     */
    protected void out()
    {
    for (int i = 0; i < this.size; i++)
    {
    int p = (this.first + i) % this.data.length;
    System.out.print(this.data[p] + " ");
    }
    System.out.println();
    }
    }
      

  4.   


    ADSL重新拨号换IP,如果换IP都不行,清空IE缓存,cookie这些试验下。