asp.net core系列 43 Web应用 Session分布式存储(in memory与Redis)

一.概述

HTTP 是无状态的协议。 默认情况下,HTTP 请求是不保留用户值或应用状态的独立消息。 本文介绍了几种保留请求间用户数据和应用状态的方法。下面以表格形式列出这些存储方式,本篇专讲Session会话状态,计划下篇再讲应用状态。

存储方法 存储机制
Cookie HTTP Cookie(可能包括使用服务器端应用代码存储的数据)
Session 状态 HTTP Cookie 和服务器端应用代码
TempData HTTP Cookie 或Session状态
查询字符串 HTTP 查询字符串
隐藏字段 HTTP 窗体字段
HttpContext.Items 服务器端应用代码
Cache 服务器端应用代码
依赖关系注入 服务器端应用代码
1.1 Cookie

Cookie是服务器产生ID的标签。是识别用户,实现持久会话的最好方式。Cookie分为两类:会话Cookie和持久Cookie。二者唯一区别是设置过期时间。
 (1)会话Cookie是临时的,当用户退出浏览器时,就会被删除。
 (2)持久Cookie生存时间更长,存储在硬盘上,当浏览器退出或计算机重启时它们仍然存在。
Cookie跨请求存储数据,每次请求都会发送Cookie。Cookie的大小应该保持在最低限度。理想情况下,仅标识符存储在 Cookie 中。大多数浏览器 Cookie 大小限制为 4096 个字节。每个域仅有有限数量的 Cookie 可用,比如IE6.0每个域cookie个数最多为20个。
下面看Cookie是如何工作的:



(a) 客户端首次Request请求Web站点时,Web服务器对客户端一无所知。
 (b) Web服务器通过Response报文的 Set-Cookie或Set-Cookie2产生标识Cookie id=”34294”,返回给客户端,客户端浏览器会存储该Cookie
 (c) 当客户端再次Request请求时,带上Cookie,Web服务器就能识别该客户端,实现会话。

1.2 Session (会话)介绍

Session数据由缓存支持并被视为临时数据,Session状态是用户浏览Web应用程序时,存储用户数据的ASP.NET Core方案。Session状态使用应用维护的存储,来保存客户端所有请求的数据。ASP.NET Core通过向客户端提供包含Session ID的cookie来维护Session状态,该Session ID随每个请求一起发送到应用程序(Web服务器)。该应用程序使用Session ID来获取Session数据(Session数据存储在Web服务器上)。Session会话状态以下主要行为:
(1) 由于cookie Session是特定于浏览器的,因此不能跨浏览器共享会话。
 (2) 应用在上次请求后保留Session的时间有限。 应用程序可以设置Session超时,或者使用 20 分钟的默认值。
(3) 建议不使用粘性会话, 更好的方法是使用Redis或SQL Server分布式缓存,它不需要粘性会话。

1.3 Session会话选项

若要替代Session默认值,使用 SessionOptions (services.AddSession)。下面是主要选项,以及示例:

选项 说明
Cookie 确定用于创建 Cookie 的设置。名称默认为.AspNetCore.Session
IdleTimeout 空闲多长时间Session重置。 此设置仅适用于Session内容,不适用于 Cookie。 默认为 20 分钟。
IOTimeout 允许从存储加载会话或者将其提交回存储的最大时长。 此设置可能仅适用于异步操作。 可以使用 InfiniteTimeSpan 禁用超时。 默认值为 1 分钟
//选项配置
     services.AddSession(options =>
      {
          options.Cookie.Name = ".AdventureWorks.Session";
          options.IdleTimeout = TimeSpan.FromSeconds(10);
      });
1.4 Session设置和获取值

在 Microsoft.AspNetCore.Session 包中提供中间件来管理Session状态。 若要启用Session中间件,Startup 必须包含如下:
 (1) 任何IDistributedCache内存缓存。该IDistributedCache实现用作Session的后备存储,下面会讲到分布式缓存在 ASP.NET Core 中。
 (2) 对 ConfigureServices 方法中 AddSession 的调用。
(3) 对 Configure 中 UseSession(); 的调用。

二. Session存储 in memory

在 ASP.NET Core中,分布式缓存无论使用in memory、SQL Server、Redis都需要应用程序使用IDistributedCache接口与缓存进行交互。Session存储在in memory中,称为”分布式内存缓存”(AddDistributedMemoryCache)。是框架提供的IDistributedCache实现。注意:分布式内存缓存不是实际的分布式缓存,该缓存是存储在运行应用程序的服务器上。
运用场景一般用在开发或测试中。也可以用在生产环境下,但必须是内存消耗不高并且应用程序是单个节点(没有Web分发负载)。

2.1 Startup文件配置session
public class Startup
{
    public void ConfigureServices(IServiceCollection services)
    {
           //..
            services.AddDistributedMemoryCache();
            services.AddHttpContextAccessor();
            services.AddSession(options =>
            {
                //空闲10秒后, session自动清空
                options.IdleTimeout = TimeSpan.FromSeconds(10);
                options.Cookie.HttpOnly = true;
            });
            services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
    }

    public void Configure(IApplicationBuilder app, IHostingEnvironment env)
    {
       //..
       //顺序很重要,必须放在UseMvc之前。配置后HttpContext.Session可用
        app.UseSession();
        app.UseMvc();
    }
}
2.2 使用属性 HttpContext.Session 从 Razor Pages PageModel 类或 MVC 控制器类访问会话状态。
public class HomeController : Controller
    {
        private readonly IHttpContextAccessor _accessor;
        public HomeController( IHttpContextAccessor httpContextAccessor)
        {
            this._accessor = httpContextAccessor;
        }

        public IActionResult Index()
        {
            HttpContext.Session.SetString("SessionTest", "Ben Rules!");
            return View();
        }
        
        public IActionResult Privacy()
        {
            ViewData["SessionTest"] = _accessor.HttpContext.Session.GetString("SessionTest");
            return View();
        }
    }

先运行index页设置Session后,再运行Privacy页读取该Session。如下图:Session会话设置和获取值成功,再查看浏览器中的Cookies名称默认为.AspNetCore.Session。


2.3 Session数据序列化

必须对所有Session数据进行序列化以启用分布式缓存方案,即使是在使用in memory缓存的时候。对于字符串和数据序列化以由ISession 扩展方法实现了。对于复杂类型,添加以下扩展方法以设置和获取可序列化的对象。

public static class SessionExtensions
{
    public static void Set<T>(this ISession session, string key, T value)
    {
        session.SetString(key, JsonConvert.SerializeObject(value));
    }

    public static T Get<T>(this ISession session, string key)
    {
        var value = session.GetString(key);

        return value == null ? default(T) : 
            JsonConvert.DeserializeObject<T>(value);
    }
}
//以下示例演示如何使用扩展方法设置和获取可序列化的对象:
    HttpContext.Session.Set<DateTime>(SessionKeyTime, currentTime);

三.Session存储Redis

Redis缓存比SQL Server缓存提供更高的吞吐量和更低的延迟。这里不在演示SQL Server存储,如果要用SQL Server存储,建议为分布式缓存提供专用的SQL Server实例(与应用程序的数据库实例分开)。

3.1 Redis服务器

关于redis的安装,这里不在介绍。

#启动redis服务成功
[root@localhost bin]# ./redis-server redis.conf

#启动redis客户端测试成功
[root@localhost bin]# ./redis-cli -h 172.168.18.200  -a 123456
127.0.0.1:6379> set msg "hello"
OK
127.0.0.1:6379> get msg
"hello"
3.2 asp.net core应用端

(1)演示项目是基于上面的in memory案例,先安装redis中间件。

 PM> Install-Package Microsoft.Extensions.Caching.Redis

(2)将in memory改为redis存储,代码改动如下:

//services.AddDistributedMemoryCache();
            services.AddDistributedRedisCache(options =>
            {
                options.Configuration = "172.168.18.200:6379,allowAdmin=true,password=123456,defaultdatabase=0";
            });
            services.AddHttpContextAccessor();
            services.AddSession(options =>
            {
                options.IdleTimeout = TimeSpan.FromSeconds(10);
                options.Cookie.HttpOnly = true;
            });
            services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
(3)设置和获取Session

在HomeController类中,设置和获取Session,代码不变,可参考上面2.2。运行index页后,再运行Privacy页,Session会话设置和获取值成功,再使用redis工具查看已存储到了redis服务中(空闲10秒后,redis存储的session将自动清空)如下图所示:



 参考资料

ASP.NET Core 中的会话和应用状态

Redis可视化管理

分布式缓存在 ASP.NET Core 中

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

推荐阅读更多精彩内容