Koa系列3:持久化用户session状态

1. session背景知识:

session也被称为“会话控制”,顾名思义,它用于控制网络会话,如用户登录信息、购物车中的商品
session中的数据是保存在服务器端的,在服务器端有很多种存储方式,既可以直接保存在内存中,也可以保存在Redis、MongoDB、Mysql等数据库中。但是session中的数据一般都是在短时间内高频访问的,需要保证性能(在内存中缓存,读写快),同时这样容易丢失数据,如遇到服务器重启的情况。因此比较好的方式是内存缓存配合外部数据库对session做一个持久化,方便丢失数据后的数据找回。

2. 将用户session信息持久化:

Koa中有不少中间件提供了session数据的持久化功能,这里我通过两种方法来实现,大家根据自己的需求来取用:

(1). 将session存放在Mysql数据库中

需要用到中间件:
koa-session-minimal 适用于koa2 的session中间件,提供存储介质的读写接口 。
koa-mysql-sessionkoa-session-minimal中间件提供Mysql数据库的session数据读写操作。
示例代码:

const Koa = require('koa')
const session = require('koa-session-minimal')
const MysqlSession = require('koa-mysql-session')

const app = new Koa()

// 配置存储session信息的mysql
let store = new MysqlSession({
  user: 'root',
  password: 'yourpassword',
  database: 'app_database',
  host: '127.0.0.1',
})

// 存放sessionId的cookie配置,根据情况自己设定
let cookie = {
  maxAge: 20*60*1000, // cookie有效时长(ms)
  expires: '',  // cookie失效时间
  path: '', // 写cookie所在的路径
  domain: '', // 写cookie所在的域名
  httpOnly: true, // 是否只用于http请求中获取
  overwrite: true,  // 是否允许重写
  secure: '',
  sameSite: '',
  signed: true,
}

// koa-session-minimal需要一个options对象参数:
/***
 * key:会话cookie名称和商店密钥前缀
   store:会话外部存储
   cookie:cookie选项,可以是对象(静态cookie选项)或返回对象的函数(动态cookie选项)。
   只有maxAge,path,domain,secure,httpOnly是支持的。
 */
app.use(session({
  key: 'SESSION_ID',
  store: store,
  cookie: cookie
}))

//下面就可以在自己的路由中间件中来定义session信息了
//每次请求路由接口,都会对session信息进行更新,请自行验证
app.use( async ( ctx ) => {
  // 设置session
  if ( ctx.url === '/set' ) {
    ctx.session = {
      user_id: Math.random().toString(36).substr(2),
      count: 0
    }
    ctx.body = ctx.session
  } else if ( ctx.url === '/' ) {

    // 读取session信息
    ctx.session.count = ctx.session.count + 1
    ctx.body = ctx.session
  } 

})

app.listen(3000)
console.log('[demo] session is starting at port 3000')

查看数据库,多了一个_mysql_session_store表:

image.png

(2). 将session存放在Redis中:

需要用到的中间件
redis
koa-redis
koa-session-minimal
示例代码:

const session = require('koa-session-minimal');
const redisStore = require('koa-redis');
const Koa = require('koa');
const redis = require('redis');
//实例化redis数据库,并连接,生成redis客户端对象
const client = redis.createClient(6379, "127.0.0.1");

const app = Koa ();
app.keys = ['keys', 'keykeys'];

const options = {client: client, db: 1};
const store = redisStore(options);
app.use(session({
  key: 'SESSION_ID',
  store: store,
  cookie: cookie //与上面的示例代码的参数一样
}));

//以上是为session的外部存储配置了redis的一个客户端,不像前面的koa-mysql-session,
//已经封装了存入session的操作,这里需要在不同的路由中间中通过store.client对象
//的一些原生方法对session数据进行向redis的增删改查
app.use(function *() {
  switch (this.path) {
    case '/get':
      get.call(this);
      break;

    case '/testKV':
      // 保存key value
      if (this.query.adminId) {
        yield store.client.set("test1", this.query.adminId);
      }
      //同步读取key value
      this.body = yield store.client.get("test1");
      break;

    case '/testHM':
      //操作hashmap
      var result = yield store.client.hmset("hosts", "mjr", "123", "another", "23", "home", "1234");
      console.log(result);

      var obj = yield store.client.hgetall("hosts")
      console.dir(obj);
      //获取hashmap key的值
      this.body = yield store.client.hget("hosts", "home");
      break;

    case '/testSet':
      //保存set
      var key = "key1";
      store.client.sadd("key1", "v1");
      store.client.sadd("key1", "v2");
      store.client.sadd("key1", "v3");
      //读取set
      store.client.multi()
        .sismember(key, 'v1')
        .smembers(key)
        .exec(function (err, replies) {
          console.log("MULTI got " + replies.length + " replies");
          replies.forEach(function (reply, index) {
            console.log("Reply " + index + ": " + reply.toString());
          });
        });
      //读取set
      this.body = yield store.client.smembers("key1");
      break;

    case '/testList':
      //保存list
      store.client.rpush("mylist", "bbb")
      store.client.rpush("mylist", "ccc")
      store.client.lpush("mylist", "aaa")
      this.body = yield store.client.rpop("mylist");
      break;

    case '/remove':
      remove.call(this);
      break;

    case '/regenerate':
      yield regenerate.call(this);
      break;
  }
});

function get() {
  var session = this.session;
  session.count = session.count || 0;
  session.count++;
  var test = store.client.get("test");
  console.log(test);
  this.body = session.count;
}

function remove() {
  this.session = null;
  this.body = 0;
}

function *regenerate() {
  get.call(this);
  yield this.regenerateSession();
  get.call(this);
}

app.listen(8080);

以上是将session信息持久化到mysql和redis中的具体代码,发送回浏览器的cookie信息还是会在指定失效时间失效,只是在过期之前一旦发生session数据的意外丢失,可以通过mysql或redis对session的存储来恢复数据。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,921评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,635评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,393评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,836评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,833评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,685评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,043评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,694评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,671评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,670评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,779评论 1 332
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,424评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,027评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,984评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,214评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,108评论 2 351
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,517评论 2 343

推荐阅读更多精彩内容

  • 前言 在上一篇文章中,介绍了Redis内存模型,从这篇文章开始,将依次介绍Redis高可用相关的知识——持久化、复...
    Java架构阅读 2,293评论 3 21
  • 一、Redis高可用概述 在介绍Redis高可用之前,先说明一下在Redis的语境中高可用的含义。 我们知道,在w...
    空语阅读 1,593评论 0 2
  • 觉察是成为自己的旁观者,成为自己的见证者,看到自己的情绪反应,看到身体的自动化反应,看到你自己念头的自动化反应。 ...
    心中境地阅读 126评论 0 0
  • 20160813早 天气晴·没有一丝一缕风的闷热 前一阵,小伙伴推荐了简书,前几天,看到喜欢的姑凉用简书发表了小文...
    Joyyyyy阅读 154评论 0 0
  • 积雨空林静,凝霜倦鸟稀。 径斜山断目,风过露沾衣。 独立怀乡远,徒悲别梦违。 遥望寒日落,羁旅不同归。
    尘埃落定1阅读 443评论 12 29