dot 快速的未来正在您所在的城市举行活动。

加入我们参加 Redis 发布会

什么是会话状态?

会话状态是捕获用户与应用程序(例如网站或游戏)交互的当前状态的数据。

典型的 Web 应用程序会为每个连接的用户维护一个会话,只要用户登录,该会话就会一直存在。会话状态是应用程序如何记住用户身份、登录凭据、个性化信息、最近操作、购物车等。

每次用户交互时读写会话数据必须在不影响用户体验的情况下进行。在幕后,会话状态是特定用户或应用程序的缓存数据,它允许对用户操作做出快速响应。因此,在用户会话处于活动状态时,不需要往返中央数据库。

会话状态生命周期的最后一步发生在用户断开连接时。某些数据将被持久化到数据库以备将来使用,但瞬态信息可以在会话结束时丢弃。


会话状态的挑战和最佳实践

了解会话状态的最佳实践是评估和解决常见会话相关问题的关键,例如隔离、易失性和持久性。

在会话处于活动状态时,应用程序专门从内存中的会话存储读取和写入数据。这意味着更快的写入操作,但也无法容忍数据丢失。由于会话存储数据不是来自另一个数据库的简单快照,因此它必须具有高度耐用性并始终可用。

会话状态类似于缓存,但它具有不同的读写生命周期:缓存是容忍数据丢失的,并且可以随时从主数据库恢复。写入缓存还需要写入底层数据库。相反,会话状态只能在用户会话启动时从主数据源恢复,并且仅在会话结束时持久化回数据源。

会话状态可以是易失性的或永久性的,这意味着数据可以在用户会话结束时被丢弃或持久化到磁盘存储。易失性会话数据的示例可能是公司内部网中的页面导航历史记录 - 没有必要保留它。相反,电子商务应用程序中的持久购物车对于业务至关重要,必须保存在永久存储中。

会话状态存储为一个 键值 对,其中用户标识符作为键,会话数据作为值。这确保用户会话不会访问彼此的信息。

将会话状态存储在快速的内存中缓存中允许某些在线分析场景,这些场景否则会惩罚事务数据库。这些应用程序包括实时分析和仪表板、推荐引擎和欺诈检测。

我们如何使它快速

  • Redis 企业版基于一个 无共享的对称架构,它允许数据集大小线性增长,并且无需更改应用程序代码即可无缝扩展。
  • Redis 企业版提供高可用性和地理分布的多种模型,在需要时为您的用户提供本地延迟。
  • 多种持久化选项(每次写入或每秒 AOF 以及快照),不会影响性能,确保您无需在故障后重建数据库服务器。
  • 支持 超大型数据集,使用智能分层访问内存(DRAM、持久内存或 Flash)确保您可以根据用户的需求扩展数据集,而不会显着影响性能。

如何将我们用于会话管理

考虑一个使用 MySQL 作为关系数据库、Node.js 作为后端服务器技术和 Redis 企业版进行会话管理的文本聊天应用程序。前端由两个页面组成:主页,用户在此登录,以及聊天页面,用户在此输入并发送消息。

为了简单起见,我们将在这里仅显示服务器代码。它将解释如何在 Node.js 中实现会话状态生命周期。我们还省略了 HTML 视图页面和应用程序的其余部分。

首先,应用程序加载依赖项,包括会话、Redis 对象和 MySQL 客户端

var express = require("express");
var session = require('express-session');
var mysql = required("mysql");
var redis = require("redis");
var redisStore = require('connect-redis')(session);
Var redisClient = redis.createClient();
//more dependencies are loaded here....

上面的声明创建了用于管理 Web 路由、会话、数据库、缓存和会话 Node.js 库的对象。然后将 Redis 设置为会话存储

app.use(session({
    secret: 'mysecret',
    // create new redis store.
    store: new redisStore({
        host: 'localhost',
        port: 6379,
        client: redisClient
    }),
    saveUninitialized: false,
    resave: false
}));

接下来,为主页和聊天页面配置 Node.js Express 路由,以及对来自客户端的 AJAX 请求的支持,包括登录、注销和发送评论。

当用户请求主页时,服务器会将他们重定向到 chat.html 页面,或者根据用户是否已登录显示登录页面。html。以下代码段显示了 /get Web 路由的处理程序代码

app.get('/', function (req, res) {
    // create new session object.
    if (req.session.key) {
        // user is already logged in
        res.redirect('/chat');
    } else {
        // no session found, go to login page
        res.render("login.html");
    }
});

当用户提交登录表单数据(包含电子邮件和密码)时,客户端 JavaScript AJAX 会将表单数据发送到服务器。在本示例中,它调用 executeLoginDbCommand 函数(此处未显示),该函数会对 MySQL 数据库执行 SQL 查询,并返回一个包含用户先前保存的会话数据的对象。

如果登录成功,来自 MySQL 的用户数据将保存到由 Redis 会话存储支持的 Web 会话中,并且客户端 JavaScript 代码会将用户重定向到聊天页面

app.post('/login', function (req, res) {
    // SQL Query will compare login and password
    // from HTTP request body to user table data
    executeLoginDbCommand(req.body.Email, req.body.Password, function (dbResult) {
        //
        if (!dbResult) {
            res.json({
                "success": false,
                "message": "Login failed ! Please register"
            });
        } else {
            req.session.key = dbResult;
            res.json({
                "success": true,
                "message": "Login success."
            });
        }
    });
});

应用程序聊天页面允许用户读取和发布消息给登录到应用程序的其他用户。由于用户只能看到他们自己与其他人的消息交互,因此服务器返回的聊天页面请求数据会因用户而异。最重要的是,访问此页面仅限于登录用户。检查会话密钥会显示用户是否已登录

app.get('/chat', function (req, res) {
    if (req.session.key) {
        //user is already logged in,
        //so let's render the chat page with the user email
        res.render("chat.html", {
            email: req.session.key["UserEmail"],
            name: req.session.key["UserName"]
        });
    } else {
        // no session found, go to login page
        res.redirect("/");
    }
});

当用户从聊天页面提交新评论时,客户端 JavaScript AJAX 会将表单数据发送到服务器。如果用户已登录,则评论将插入到 MySQL UserComments 表中。我们通过调用 executeSendCommmentDbCommand 函数(此处未显示)来完成此操作。

app.post("/sendComment", function (req, res) {
    // This SQL command will insert a new comment in
    // users table
    if (req.session.key) {
        executeSendCommmentDbCommand(req.body.Email, req.body.Recipient, req.body.Comment, function (dbResult) {
            if (!dbResult) {
                res.json({
                    "success": true,
                    "message": "Comment has been sent successfully."
                });
            } else {
                res.json({
                    "success": false,
                    "message": "SendComment failed!"
                });
            }
        });
    } else {
        res.json({
            "success": false,
            "message": "Please login first."
        });
    }
});

当用户注销时,会话对象会被销毁,用户会被重定向到登录页面。但首先, executePersistSessionDbCommand (此处未显示)会将内存中的用户会话保存到 MySQL 数据库

app.get('/logout', function (req, res) {
    // user is logged in, so let's destroy the session
    // and redirect to login page.
    if (req.session.key) {
        executePersistSessionDbCommand(req.session, function (dbResult) {
            if (!dbResult) {
                req.session.destroy(function () {
                        res.redirect('/');
                    } else {
                        res.json({
                            "success": false,
                            "message": "Session persistence failed!"
                        });
                    }
                });
        });
    } else {
        res.redirect('/');
    }
});

这些代码段只是使用 Redis 作为会话存储的真实应用程序的表面。但是它们说明了 Redis 如何管理与 MySQL 等永久数据库存储结合的内存中会话状态生命周期。


相关资源

帖子

2017 年 11 月 15 日

缓存与会话存储

在保持低延迟的同时进行全球扩展,并更有效地缓存以降低成本:单击此处与 Redis 企业版团队联系。俗话说,“在硅谷……”

帖子

2019 年 4 月 30 日

使用 Redis 进行查询缓存

确定您的缓存是否为企业级,并了解如何:在保持低延迟的同时进行全球扩展,并更有效地缓存以降低成本。当我刚开始的时候……

帖子

2019 年 5 月 29 日

缓存、承诺和锁

Instagram 最近在其工程博客上发布了一篇关于承诺缓存值的理念的文章。这个想法是,在缓存未命中时,需要一段时间……