• Spring in action


    传统的会话管理是用一个session表保存会话信息,每次请求时读取、写入该表。

    public function read($sessID) { 
       $hander = is_array($this->hander)?$this->hander[1]:$this->hander;
       $res = mysql_query("SELECT data AS data FROM ".$this->sessionTable." WHERE sid = '$sessID'   AND expire >".time(),$hander); 
       if($res) {
           $row = mysql_fetch_assoc($res);
           return $row['data']; 
       }
       return ""; 
    } 
    
    public function write($sessID,$sessData) { 
       $hander = is_array($this->hander)?$this->hander[0]:$this->hander;
       $expire = time() + $this->lifeTime; 
       mysql_query("REPLACE INTO  ".$this->sessionTable." (  sid, expire, data)  VALUES( '$sessID', '$expire',  '$sessData')",$hander); 
       if(mysql_affected_rows($hander)) 
           return true; 
       return false; 
    } 

    session表的expire是一个时间戳,每次请求时更新。使用此栏位可以清理会话。

    public function gc($sessMaxLifeTime) { 
       $hander = is_array($this->hander)?$this->hander[0]:$this->hander;
       mysql_query("DELETE FROM ".$this->sessionTable." WHERE expire < ".time(),$hander); 
       return mysql_affected_rows($hander); 
    } 

    Redis的处理方式

    首先看一个JAVA方法:

    public void updateToken(Jedis conn, String token, String user, String item) {
        long timestamp = System.currentTimeMillis() / 1000;
        conn.hset("login:", token, user);
        conn.zadd("recent:", timestamp, token);
        if (item != null) {
            conn.zadd("viewed:" + token, timestamp, item);
        }
    }

    login是一个哈希表,它的目的是记录所有在线用户,等同session表的记录。

    recent是一个有序集合,目的是记录会话的最新访问时间,类似session表的expire字段。使用这个集合,可以对长时间没有请求的会话做清理。

    viewed也是一个有序集合,存放访问历史,与session表的data字段作用相同。

    如果viewed集合不清理,随着用户浏览的商品越多,集合会不断的增大,必须做清理处理。这里的处理方式是保持viewed集合只有25笔,其它的都删除,按照浏览时间排序,保存最新的25笔。看看修改后的updateToken()方法:

    public void updateToken(Jedis conn, String token, String user, String item) {
        long timestamp = System.currentTimeMillis() / 1000;
        conn.hset("login:", token, user);
        conn.zadd("recent:", timestamp, token);
        if (item != null) {
            conn.zadd("viewed:" + token, timestamp, item);
            conn.zremrangeByRank("viewed:" + token, 0, -26);  //只保留最后25笔
        }
    }

    随着时间的推移,会话越来越多,为了限制会话数量,我们决定只保留最新的1000万个会话。rencent集合记录了每个会话的最后访问时间,可以以此为清理条件。

    public class CleanSessionsThread
            extends Thread
    {
        private Jedis conn;
        private int limit;
        private boolean quit;
    
        public CleanSessionsThread(int limit) {
            this.conn = new Jedis("localhost");
            this.conn.select(15);
            this.limit = limit;
        }
    
        public void quit() {
            quit = true;
        }
    
        public void run() {
            while (!quit) {
                long size = conn.zcard("recent:");
                if (size <= limit){
                    try {
                        sleep(1000);
                    }catch(InterruptedException ie){
                        Thread.currentThread().interrupt();
                    }
                    continue;
                }
    
                // 每次最多清理100个会话
                long endIndex = Math.min(size - limit, 100);
                
                // recent是按照访问时间从小到大排列的
                Set<String> tokenSet = conn.zrange("recent:", 0, endIndex - 1);
                
                String[] tokens = tokenSet.toArray(new String[tokenSet.size()]);
    
                ArrayList<String> sessionKeys = new ArrayList<String>();
                for (String token : tokens) {
                    sessionKeys.add("viewed:" + token);
                }
    
                conn.del(sessionKeys.toArray(new String[sessionKeys.size()]));
                conn.hdel("login:", tokens);
                conn.zrem("recent:", tokens);
            }
        }
    }

    有时候我们会统计访问最多的商品,每次访问商品时做一个计数。使用有序集合的特性,将所有商品加入到有序集合,访问一次该商品将就该商品的权值-1,访问越多,权值就越小。有序集合会按照权值从小到大排序,访问最多的商品就会排在最前面。

    public void updateToken(Jedis conn, String token, String user, String item) {
        long timestamp = System.currentTimeMillis() / 1000;
        conn.hset("login:", token, user);
        conn.zadd("recent:", timestamp, token);
        if (item != null) {
            conn.zadd("viewed:" + token, timestamp, item);
            conn.zremrangeByRank("viewed:" + token, 0, -26);  //只保留最后25笔
            conn.zincrby("viewed:", -1, item); // 商品访问有序集合,访问越多,权值越小,在集合中月靠前
        }
    }

    同理,viewed集合也需要定时的清理,不然该集合会不断的增大,最后将把所有的商品都包含在里面。我们单独开一个进程来清理viewed集合。

    public class CleanViewedThread
            extends Thread
    {
        private Jedis conn;
        private int limit;
        private boolean quit;
    
        public CleanViewedThread(int limit) {
            this.conn = new Jedis("localhost");
            this.conn.select(15);
            this.limit = limit;
        }
    
        public void quit() {
            quit = true;
        }
    
        public void run() {
            while (!quit) {
                long size = conn.zcard("viewed:");
                if (size <= limit){
                    try {
                        sleep(1000);
                    }catch(InterruptedException ie){
                        Thread.currentThread().interrupt();
                    }
                    continue;
                }
    
                // 每次最多清理100笔
                long endIndex = Math.min(limit + 100, size);
                
                Set<String> viewedSet = conn.zrange("viewed:", limit, endIndex - 1);
                conn.zrem("viewed:", viewedSet.toArray(new String[viewedSet.size()]));
            }
        }
    }
  • 相关阅读:
    break和continue
    while循环嵌套
    while循环语句
    SDUT 2766-小明传奇2(母函数)
    那些奇妙的&quot;大师&quot;是怎样炼成的(科学、迷信、心理)
    深入理解Linux字符设备驱动
    [从头学数学] 第162节 锐角三角函数
    iOS将数组中的内容分拼接成字符串
    win10 UWP 全屏
    杂(三)-The type java.lang.Object cannot be resolved It is indirectly referenced ...
  • 原文地址:https://www.cnblogs.com/eastson/p/5951493.html
Copyright © 2020-2023  润新知