搜索
您的当前位置:首页正文

connect-memory

来源:二三娱乐

背景

这是express-session的默认存储媒介,没错,就是内存。在上一节,介绍了媒介redis,现在就来讲一下内存,更有意思。这里需要注意的是,因为依赖于内存,所以一旦重启或者意外终止程序,所有的session记录将会清空。

源码分析

原型定义
function MemoryStore() {
  Store.call(this)
  this.sessions = Object.create(null) // key是id, value是session详情
}

上面的代码无需多讲了,主要是初始化父类以及sessions值,用来存放所有用户的session信息(对象类型)

getSession方法
// 根据sessionid来获取相应的session,如果过期或者不存在,都返回空值
function getSession(sessionId) {
  var sess = this.sessions[sessionId] 
  if (!sess) {
    return   // 从未访问
  }
  // parse
  sess = JSON.parse(sess)
  var expires = typeof sess.cookie.expires === 'string'
    ? new Date(sess.cookie.expires)
    : sess.cookie.expires
  // destroy expired session
  if (expires && expires <= Date.now()) {   // 过期了
    delete this.sessions[sessionId]
    return
  }
  return sess
}

代码也挺简单,不多说了

MemoryStore.all
// 获取全部有效的session,并作为第二个参数传入callback函数
MemoryStore.prototype.all = function all(callback) {
  var sessionIds = Object.keys(this.sessions)   // 所有key(id)
  var sessions = Object.create(null)   // 用来存储所有有效的session,并且返回
  for (var i = 0; i < sessionIds.length; i++) {
    var sessionId = sessionIds[i]
    var session = getSession.call(this, sessionId)  // 判断该session是否有效,并且返回有效的session
    if (session) {
      sessions[sessionId] = session;
    }
  }
  callback && defer(callback, null, sessions)
}

在调用getSession.call(this, sessionId)方法的时候,会自动将过期的session清掉。

MemoryStore.clear
MemoryStore.prototype.clear = function clear(callback) {
  this.sessions = Object.create(null)
  callback && defer(callback)
}

将所有的session都清空掉

MemoryStore.destroy
MemoryStore.prototype.destroy = function destroy(sessionId, callback) {
  delete this.sessions[sessionId]
  callback && defer(callback)
}

清空某个session,一般用于登出用途

MemoryStore.get
MemoryStore.prototype.get = function get(sessionId, callback) {
  defer(callback, null, getSession.call(this, sessionId))
}

直接调用已经存在的getSession方法,若过期,自动清理

MemoryStore.set
// 设置某个session值
MemoryStore.prototype.set = function set(sessionId, session, callback) {
  this.sessions[sessionId] = JSON.stringify(session)
  callback && defer(callback)
}

过程无需复杂,直接覆盖旧值就可以了

MemoryStore.length
MemoryStore.prototype.length = function length(callback) {
  this.all(function (err, sessions) {
    if (err) return callback(err)
    callback(null, Object.keys(sessions).length)
  })
}

为了防止某些session过期,所以this.all方法会调用getSession方法,所以返回的sessions是当前有效的记录,再求大小即可。

MemoryStore.touch
MemoryStore.prototype.touch = function touch(sessionId, session, callback) {
  var currentSession = getSession.call(this, sessionId)
  if (currentSession) {
    // update expiration
    currentSession.cookie = session.cookie
    this.sessions[sessionId] = JSON.stringify(currentSession)
  }
  callback && defer(callback)
}

当前currentSession存在的时候,才有更新,所以需要获取当前session。

源码就这么少了,分析完了。下面来聊聊这个代码有哪些地方值得学习的(思路和实现技巧)

小结

  1. 根据id获取相应session,这是一个很常见的动作,所以需要提取成一个共公用方法。但是考虑到可能会有过期现象,所以还要计算过期时间,如果过期了,则直接清理就好。所以,这个方法完全对外屏蔽了细节,给我id即可,至于过不过期,我等下告诉你,不用你管

  2. Store.call(this)这里用得不错(确实,大家也这么做的)。初始化父类的时候,这么做是最好用了。

  3. 上面的getSession方法,明明不是MemoryStore所有,怎么可以直接使用this呢?技巧就在这里了。当一个方法不想或者无需暴露给外界的时候,我们可以在文件内定义函数,而不是某个对象的方法。但是,往往有时候我们需要用到对象的某些属性,例如this.sessions,这个时候就可以使用getSession.call(this, sessionId),动态改变this,就可以使用啦啦啦。这个写法,我感觉不错。

  4. 最后一个,感觉也是相当有意思的。因为session有过期时间。很多人第一印象就是,要不要用一个定时器来刷新过期时间啊。之前我接触令牌桶算法的时候,我也以为需要这么做。很正常啊,一秒钟刷新一次,不然没法计算过期时间。但是,如果真是这么做了,会导致定时器满天飞,性能大大下降。在这里,是利用当前访问时间和初次访问时间之差来确定是否过期,轻易地省去了定时器。

    好了,说完了,不错的一个插件项目

Top