AsyncWeb原理分析(八)——session的实现

12 篇文章 0 订阅
thomescai http://blog.csdn.net/thomescai(转载请保留)
    概要:session流程,sessionId的生成,session的过期
session的流程:
    主类DefaultSessionAccessor包括SecureRandomKeyFactory(生成sessionId) BasicSessionStore(保持sessionId),(1)在AbstractHttpServiceContext中通过调用DefaultSessionAccessor的getSession(),(2)在DefaultSessionAccessor中执行CookieIdentifier的getSessionKey(),获取请求中的cookies,(3)匹配cookie,当cookie中存在name为sessionKey,(4)返回该cookie的Value,即sessionId。(5)执行locateSession(),在BasicSessionStore查询sessionMap中是否存在改sessionId,如果不存在(6)createNewSession()生成新的sessionId。
流程图如下:
类图如下:
sessionId的生成:
     sessionId是通过SecureRandom(随机数生成器(RNG) 算法),把16个字节的随机byte生成32个字符串的sessionId。代码如下。
 public String createSessionKey() {
        byte[] keyBytes = new byte[16];
        synchronized (secureRandom) {
            secureRandom.nextBytes(keyBytes);
        }
        return bytesToSessionKey(keyBytes);
    }
    private static String bytesToSessionKey(byte[] bytes) {
        char[] keyChars = new char[bytes.length * 2];
        for (int i = 0; i < bytes.length; ++i) {
            byte b1 = (byte) ((bytes[i] & 0xf0) >> 4);
            byte b2 = (byte) (bytes[i] & 0x0f);
            keyChars[2 * i] = toKeyStringChar(b1);
            keyChars[2 * i + 1] = toKeyStringChar(b2);
        }
        return new String(keyChars);
    }
    
session的过期:
     每个PermitEntry记录了session的时间,实现了renew()和cancel()。当同一个session请求后,renew()会修改PermitEntry中的 expiryTime(结束时间)=System.currentTimeMillis() + lifetime。
    LinkedPermitIssuer是一个链表,里面记录了所有的PermitEntry。对于频繁增加节点,和更新节点来说,链表是个好方法。(刷新该节点,会将节点移到末尾。所以在LinkedPermitIssuer中保存的节点的expiryTime是递增)。


LinkedPermitIssuer的实现:
    1.初始化:在LinkedPermitIssuer中初始化了一个线程ExpiryNotifier
private class ExpiryNotifier implements Runnable {

        public void run() {
            try {
                while (processHeadEntry()) {
                    continue;
                }
            } catch (RuntimeException e) {
                LOG.error("Unexpected exception on expiry notifier", e);
            }

        }

   2.当链表为空是,lock.wait(),进入休眠状态。
private boolean processHeadEntry() {
            PermitEntry toExpire = null;
            try {
                synchronized (lock) {
                    while (!isClosed && isEmpty()) {
                        lock.wait();
                    }
                    if (isClosed) {
                        return false;
                    } else {
                        toExpire = processFirst();
                    }
                }
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            if (toExpire != null) {
                notifyExpiry(toExpire.getTarget());
            }
            return true;
        }
    3.当加入一个节点:
    如果为空,lock。notify(),唤醒之前被wait的方法;如果不为空,增加节点。
 public TimedPermit issuePermit(Object o) {
        PermitEntry permit;
        synchronized (lock) {
            permit = new PermitEntry(o);
            if (isEmpty()) {
                head = tail = permit;
                lock.notify(); // notify when move from empty to non empty
            } else {
                tail.entryAfter = permit;
                permit.entryBefore = tail;
                tail = permit;
            }
        }
        return permit;
    }
    4.当链表中有节点:
    获取第一个节点。得到timeToExpiry,并lock。wait(timeToExpiry)。因为节点的timeToExpiry是递增的。如果在链表中的节点timeToExpiry的时间分别是T1 < T2 <T3。  执行的效果相当于  wait(T1) ->> wait(T2-T1)->>wait(T3—T1—T2)。3个节点,就wait了3次。直到为空,进入wait()状态。
 private PermitEntry processFirst() {
            PermitEntry entry = head;
            boolean expired = false;
            long timeToExpiry = entry.timeToExpiry();
            while (!isClosed && !entry.isCancelled() && timeToExpiry > 0) {
                try {
                    if (LOG.isDebugEnabled()) {
                        LOG.debug("Waiting for head entry to expire: "
                                + timeToExpiry + "ms");
                    }
                    lock.wait(timeToExpiry);
                } catch (InterruptedException e) {
                    throw new RuntimeException("Unexpected interrupt");
                }
                timeToExpiry = entry.timeToExpiry();
            }

            if (entry.isCancelled()) {
                LOG.debug("Head entry is cancelled. Removing");
                removeHead();
            } else if (!isClosed) {
                LOG.debug("Head entry has expired");
                entry.markCancelled();
                removeHead();
                expired = true;
            }
            return expired ? entry : null;
        }
     5.当节点被renew后:
    同个sessoin的请求后,该节点的expiryTime就需要更新(增加),并且,该节点会被移到末尾。如果该节点是原head节点。移动之后,需要notify()。因为head节点移除后,原来wait的时间已经不准确了。调整后即:wait(after.timeToExpiry)。
private void moveToBack(PermitEntry entry) {
        boolean movedHead = entry == head;
        if (entry != tail) { // nothing to move / no need to notify if already at back
            PermitEntry previous = entry.entryBefore;
            PermitEntry after = entry.entryAfter;

            tail.entryAfter = entry;
            entry.entryBefore = tail;
            entry.entryAfter = null;
            tail = entry;

            after.entryBefore = previous;
            if (!movedHead) {
                previous.entryAfter = after;
            } else {
                head = after;
                lock.notify();
            }
        }
    }
总结:
  通过链表结构:LinkedPermitIssuer,保存session的结束时间。很适用于session的增加和更新。



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值