入坑Koa2 SESSION 机制实现和持久化

Koa cyanprobe 8年前 (2016-10-28) 20724次浏览 已收录 4个评论

前言:

前天开始已经换了V7,VSCODE,准备开撸Koa2,koa-generator 直接生成Koa2的样板项目了,然后各种熟悉API。在项目启动时果断一排红字”koa deprecated Support for generators will been removed in v3“,KoaV3将移除generator函数。由于Node Current 7.0 的发布,我们可以直接  runtimeArgs添加:–harmony-async-await 支持async。node一波更新要炸,据说V8最近的更新将会推动async迅速稳定。
koa

正文:

因为不想用CO模块和generator直接安装了Koa@2,然后有个convert中间件能够保证CO和generator这两种方案可以继续运行(ps:目前大量包还木有更新),具体支持列表看这里 https://github.com/koajs/koa/wiki
首先,先考虑折腾session那里,于是下载了Koa-session,以及Koa-session-mongo。(PS:最后才知道koa2不支持),各种折腾各种报错,在群里猫神的帮助下,终于找到了koa-session2这货,下载了V6+版本的免bable版本后终于跑起来了。由于用redis做持久化,之前的中间件都是自己集成了,这货官方给出了写法,然后我就去翻koa-session2的模块目录。我靠,尽然能看懂,也不是很麻烦啊。于是贴码吧。
每次客户新建对话,后端新建session就会设置cookie,客户端发送cookieNmae(Uid),服务端就能找到对应的讯息。
关于SESSION持久化: 之前在群里问中间件问题的时候,有人反问为什么要持久化。其实除了不可预料的宕机之外,还能防止HttpSession炸内存。还有就是node单线程的问题,多个实例共享session的最佳解决方案就是通过数据库持久化。
 

//index.js
"use strict";
const Store = require("./libs/store.js");
module.exports = (opts = {}) => {
    opts.key = opts.key || "koa:sess";
    opts.store = opts.store || new Store();
    return  (ctx, next) => {
            //获得cookie
        let id = ctx.cookies.get(opts.key, opts);
        let promise = Promise.resolve();
        let old = {};
        if(id) {
            promise = opts.store.get(id).then(session => {
                ctx.session = session;
                // check session should be a no-null object
                if(typeof ctx.session != "object" || ctx.session == null) {
                    ctx.session = {};
                }
            });
        } else {
            ctx.session = {};
        }
        return promise.then(() => {
            old = JSON.stringify(ctx.session);
            return next();
        }).then(() => {
            // no modify
            if(old == JSON.stringify(ctx.session)) return;
            return Promise.resolve().then(() => {
                // destory old session
                if(id) {
                    id = null;
                    return opts.store.destroy(id);
                }
            }).then(() => {
                if(ctx.session && Object.keys(ctx.session).length) {
                    // set new session
                    return opts.store.set(ctx.session, Object.assign({}, opts, {sid: id})).then(sid => {
                        //创建cookie
                        ctx.cookies.set(opts.key, sid, opts)
                    });
                }
            });
        });
    }
};

临时session

//store.js
"use strict"
const uid = require("uid-safe");
class Store {
    constructor() {
        this.session = {};
    }
    decode(string) {
        if(!string) return "";
        let session = "";
        try{
            //存在session
            session = new Buffer(string, "base64").toString();
        } catch(e) {}
        return JSON.parse(session);
    }
    encode(obj) {
        //滚成buffer
        return new Buffer(obj).toString("base64");
    }
    getID(length) {
        //获得Uid
        return uid.sync(length);
    }
    get(sid) {
        return Promise.resolve(this.decode(this.session[sid]));
    }
    set(session, opts) {
        opts = opts || {};
        let sid = opts.sid;
        if(!sid) {
            //Uid
            sid = this.getID(24);
        }
        this.session[sid] = this.encode(JSON.stringify(session));
        return Promise.resolve(sid);
    }
    destroy(sid) {
        delete this.session[sid];
        return Promise.resolve();
    }
}
module.exports = Store;

结合Redis持久化数据(PS:TTL没写我知道了。。)这是官方的栗子,可以看出持久化也是很方便的能够完成。

const Redis = require("ioredis");
const Store = require("koa-session2/libs/store");
class RedisStore extends Store {
    constructor() {
        super();
        this.redis = new Redis({
             port: 6379,
             host: '127.0.0.1',
             family: 4,
             password: '',
             db: 0
        });
    }
    async get(sid) {
        let data = await this.redis.get(`SESSION:${sid}`);
        return JSON.parse(data);
    }
    async set(session, opts) {
        if(!opts.sid) {
            opts.sid = this.getID(24);
        }
        await this.redis.set(`SESSION:${opts.sid}`, JSON.stringify(session));
        return opts.sid;
    }
    async destroy(sid) {
        return await this.redis.del(`SESSION:${sid}`);
    }
}
module.exports = RedisStore;

后记:

只是用这个例子记录下,有些东西并不是很难,只要愿意去想 ,去发现,还是能够有所进步的。


CyanProbe , 版权所有丨如未注明 , 均为原创丨本网站采用BY-NC-SA协议进行授权
转载请注明原文链接:入坑Koa2 SESSION 机制实现和持久化
喜欢 (2)
发表我的评论
取消评论

表情 贴图 加粗 删除线 居中 斜体 签到

Hi,您需要填写昵称和邮箱!

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址
(4)个小伙伴在吐槽
  1. 如果不用redis 要用mongodb怎么做持久化,有什么模块?
    zxr2017-04-19 23:07 回复
    • cyanprobe
      mongo-session
      凶恶的方块2017-04-28 13:41 回复
    • 为啥用mongodb,session不需要永久持久化,会有一段时间生命周期,用redis可以用ttl管理,用mongodb你怎么做过期session的注销?
      路过2018-01-08 12:24 回复
      • cyanprobe
        ttl是有的,session为什么不做持久化呢,多个线程需要共享缓存
        凶恶的方块2018-02-01 20:09 回复