锁服务实现

锁设计目的:

1 当db可以正常连接时,控制当前最大连接线程数量为6,最大的等待线程数为15,接下来线程如何的试图获取锁请求自己报异常。

2 设置锁等待时间,当等待线程超过该时间,自动释放锁。

3 当线程连接时间过长,超过5分钟,可视为db已经发生异常,将db 状态quitemode设为false,后续线程连接直接报异常

4 当db不可用时,直接返回false,且在规定时间段之后可以重试获取锁

锁实现:

1 使用类LockCounter来封装连接线程数,LockCounter放在ThreadLocal,封闭在线程里面。

2 使用reloadConfig定期更新配置

public class ServiceLock {
    protected static final LogContext logger = LogContextFactory.singleton()
            .getLogContext();

    private static final ServiceLock instance = new ServiceLock();

    // record lock information about current thread lock: nest number, locking
    // spending time
    private static ThreadLocal<LockCounter> currentThreadNestCount = new ThreadLocal<LockCounter>();

    // lightweight synchronized, to replace java synchronized
    private static Lock locksync = new ReentrantLock();

    // when quietMode = ture, any locking action will return exception directly,
    // means current SCODS is unavaliable.
    private static volatile boolean quietMode = false;

    // when quietMode = true, retry once in every quietModeRetryInterval time
    // to detect if SCODS is working
    private static long quietModeRetryInterval = 900;//seconds

    // in quiet mode, update quietModeRefreshTimestamp to current time stamp
    // before retry
    private static volatile long quietModeRefreshTimestamp = 0;// seconds

    // the last time when a thread get the lock successfully (means lockCount++)
    private static volatile long lastLockTime = System.currentTimeMillis();

    // the lock number which has already been used in lock pool,
    // when a thread get a lock successfully, the lockCount will increase 1
    // until reach the upper limit: maxLockCount
    public volatile static int lockCount = 0;

    // waitingCount indicate how many threads are waiting for opportunity to get
    // lock from lock pool
    // waitingCount also has a upper limit: maxWaitingCount
    public volatile static int waitingCount = 0;

    // max number of lock could be provided by lock pool
    public static int maxLockCount = 6;

    // max number of threads could be waiting lock to be released into lock pool
    public static int maxWaitingCount = 15;

    // waiting timeout seconds
    public static int waitingTimeout = 30;// seconds

    // if lock failed to be released in <quietModeDetectTime> seconds, that
    // means SCODS is having issue now
    public static int quietModeDetectTime = 300;//seconds

    // refresh configuration interval
    public volatile static int configReloadInterval = 1800; //seconds

    // the last time the configuration being refreshed
    public volatile static long configReloadTimestamp = 0;

    public static ServiceLock getSingle() {
        return ServiceLock.instance;
    }

    /**
     * acquire a lock, if failed, throws
     * ServiceLockAcquireFailedException this method have to be try catch
     *
     * @throws ServiceLockAcquireFailedException
     */
    public static void lock() throws ServiceLockAcquireFailedException {
        ServiceLock.getSingle().locking();
    }

    /**
     * release a lock after using, this method has to be in finally sectionF
     */
    public static void unLock() {
        ServiceLock.getSingle().unLocking();
    }

    public boolean getQuietMode() {
        return quietMode;
    }

    private void reloadConfig() {

        boolean needRefreshConfig = false;
        try {
            locksync.lock();
            if ((System.currentTimeMillis() - configReloadTimestamp) > configReloadInterval * 1000) {
                needRefreshConfig = true;
                configReloadTimestamp = System.currentTimeMillis();
            }
        } catch (Throwable t) {
            // do nothing
        } finally {
            locksync.unlock();
        }
        if (needRefreshConfig) {
            debug("ServiceLock reloading config");
            try {
                Map<String, String> config = QuoteProcessFactory.singleton()
                        .create().refreshSCODSConnPoolConfig();
                String value = config.get("PORETRYINTERVAL");
                if (StringUtils.isNotBlank(value)) {
                    quietModeRetryInterval = Integer.valueOf(value.trim());
                    System.out.println("ServiceLock config reload quietModeRetryInterval -> "
                            + quietModeRetryInterval);
                }
                value = config.get("POLOCKPOOLMAX");
                if (StringUtils.isNotBlank(value)) {
                    maxLockCount = Integer.valueOf(value.trim());
                    System.out.println("ServiceLock config reload maxLockCount -> "
                            + maxLockCount);
                }
                value = config.get("POWAITINGMAX");
                if (StringUtils.isNotBlank(value)) {
                    maxWaitingCount = Integer.valueOf(value.trim());
                    System.out.println("ServiceLock config reload maxWaitingCount -> "
                            + maxWaitingCount);
                }
                value = config.get("POWAITTIMEOUT");
                if (StringUtils.isNotBlank(value)) {
                    waitingTimeout = Integer.valueOf(value.trim());
                    System.out.println("ServiceLock config reload waitingTimeout -> "
                            + waitingTimeout);
                }
                value = config.get("POQUIETDECTTIME");
                if (StringUtils.isNotBlank(value)) {
                    quietModeDetectTime = Integer.valueOf(value.trim());
                    System.out.println("ServiceLock config reload quietModeDetectTime -> "
                            + quietModeDetectTime);
                }
                value = config.get("POCONFIGRELOADINTER");
                if (StringUtils.isNotBlank(value)) {
                    configReloadInterval = Integer.valueOf(value.trim());
                    System.out.println("ServiceLock config reload configReloadInterval -> "
                            + configReloadInterval);
                }
            } catch (Throwable e) {
                e.printStackTrace();
            }
        }
    }

    public void locking() throws ServiceLockAcquireFailedException {
        System.out.println("ServiceLock Thread[" + Thread.currentThread() + "("
                + Thread.currentThread().getId() + ")] is acquiring lock...");

        reloadConfig();

        LockCounter count = currentThreadNestCount.get();
        if (count == null) {// the first time current thread is acquiring lock,
                            // not nest invoking
            count = new LockCounter();
            currentThreadNestCount.set(count);
        } else if (count.getCount() > 0) {// if count>0, means nest invoking,
                                            // don't need to acquire new lock
            count.inc();
            return;
        }
        boolean waiting = false;// this is a flag to indicate if this locking
                                // request has to wait when lock pool is empty
        try {
            locksync.lock();

            if (!quietMode
                    && lockCount > 0
                    && ((System.currentTimeMillis() - lastLockTime) > quietModeDetectTime * 1000)) {
                // if lock failed to be released to lock pool
                // timely(>quietModeDetectTime), SCODS maybe experiencing issue,
                // our lock pool needs to become quiet mode, which means any
                // locking request will get failed to avoid high DB connection
                // disaster.
                quietMode = true;
                quietModeRefreshTimestamp = System.currentTimeMillis();
                System.out.println("ServiceLock Thread[" + Thread.currentThread()
                        + "(" + Thread.currentThread().getId()
                        + ")] is going into quiet mode. lastLockTime -> "
                        + lastLockTime + " currentTimeMillis -> "
                        + System.currentTimeMillis());
            }

            if (quietMode
                    && ((System.currentTimeMillis() - quietModeRefreshTimestamp) > quietModeRetryInterval * 1000)) {
                // in quiet mode, have to detect if SCODS is working again in
                // each quietModeRetryInterval seconds
                lockCount++;
                count.inc();
                lastLockTime = System.currentTimeMillis();
                quietModeRefreshTimestamp = System.currentTimeMillis();
                System.out.println("ServiceLock Thread[" + Thread.currentThread()
                        + "(" + Thread.currentThread().getId()
                        + ")] is retrying once in quiet mode");
                return;
            } else if (quietMode) {
                throw new ServiceLockAcquireFailedException(
                        "ServiceLock is in quiet mode");
            }

            if (waitingCount >= maxWaitingCount) {
                // waiting pool is full, return exception directly
                throw new ServiceLockAcquireFailedException(
                        "ServiceLock over max waiting count");
            }

            if (waitingCount > 0 || lockCount >= maxLockCount) {
                // lock pool is empty, thread has to wait until lock is released
                waitingCount++;
                waiting = true;
            } else {
                // thread is getting lock successfully
                lockCount++;
                count.inc();
                lastLockTime = System.currentTimeMillis();
                System.out.println("ServiceLock Thread[" + Thread.currentThread()
                        + "(" + Thread.currentThread().getId()
                        + ")] has got lock successfully");
                return;
            }

        } catch (ServiceLockAcquireFailedException e) {
            throw e;
        } catch (Throwable t) {
            throw new RuntimeException(t);
        } finally {
            locksync.unlock();
        }

        if (waiting) {
            System.out.println("ServiceLock Thread[" + Thread.currentThread() + "("
                    + Thread.currentThread().getId() + ")] is waiting lock...");
            long waitingStartTime = System.currentTimeMillis();
            for (; !quietMode;) {// loop to get available lock, unless getting
                                    // into quiet mode
                try {
                    locksync.lock();
                    if (lockCount < maxLockCount) {
                        lockCount++;
                        count.inc();
                        lastLockTime = System.currentTimeMillis();
                        waitingCount--;
                        if (waitingCount < 0)
                            waitingCount = 0;
                        System.out.println("ServiceLock Thread["
                                + Thread.currentThread() + "("
                                + Thread.currentThread().getId()
                                + ")] fianlly got lock after waiting");
                        return;
                    }
                } catch (Throwable t) {
                    // do nothing
                } finally {
                    locksync.unlock();
                }

                if ((System.currentTimeMillis() - waitingStartTime) % 10000 > 2000) {
                    // to avoid high CPU consuming, the loop sequence will keep
                    // high in 2 seconds and lower in next 8 seconds
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        // do nothing
                    }
                } else {
                    // don't sleep, full speed loop;
                }

                if ((System.currentTimeMillis() - waitingStartTime) > waitingTimeout * 1000) {
                    // waiting timeout, throw exception in the end
                    break;
                }
            }

            waitingCount--;
            if (waitingCount < 0)
                waitingCount = 0;
            throw new ServiceLockAcquireFailedException(
                    "ServiceLock wait timeout to aquire lock");

        }

    }

    public void unLocking() {
        LockCounter count = currentThreadNestCount.get();
        if (count != null && count.getCount() > 0) {
            // current thread actually got lock successfully
            long spendTime = count.dec();
            if (count.getCount() == 0) {
                try {
                    locksync.lock();
                    lockCount--;
                    if (quietMode && spendTime < quietModeDetectTime * 1000) {
                        // in quiet mode, if lock is released in time (within
                        // quietModeDetectTime seconds), means SCODS is working
                        // fine now. So cancel quiet mode, clear lock pool
                        quietMode = false;
                        quietModeRefreshTimestamp = System.currentTimeMillis();
                        lockCount = 0;
                        System.out.println("ServiceLock Thread["
                                + Thread.currentThread()
                                + "("
                                + Thread.currentThread().getId()
                                + ")] released lock and set quietMode false successfuly");
                    } else if (quietMode) {
                        System.out.println("ServiceLock Thread["
                                + Thread.currentThread()
                                + "("
                                + Thread.currentThread().getId()
                                + ")] released lock but can't set quietMode false");
                    }
                    System.out.println("ServiceLock Thread["
                            + Thread.currentThread() + "("
                            + Thread.currentThread().getId()
                            + ")] released lock");
                } catch (Throwable t) {
                    // do nothing
                } finally {
                    locksync.unlock();
                }
            }
        }
    }

    class LockCounter {
        // the number re-acquire lock in one thread
        private int count = 0;
        // the time stamp when lock is acquired
        private long startTime = System.currentTimeMillis();

        public int getCount() {
            return count;
        }

        public void setCount(int count) {
            this.count = count;
        }

        // increasing count
        public void inc() {
            if (count == 0) {
                startTime = System.currentTimeMillis();
            }
            this.count++;
        }

        // decreasing count, return spending time after start locking time
        public long dec() {
            this.count--;
            if (this.count < 0) {
                this.count = 0;
            }
            return System.currentTimeMillis() - startTime;
        }
    }

    // private void log() {
    // System.out.println("lockCount: " + ServiceLock.lockCount
    // + " waitingCount: " + ServiceLock.waitingCount + " ");
    // }

}

posted @ 2015-03-11 14:45  xiongjianjun  阅读(219)  评论(0编辑  收藏  举报