Redis Storage Session

Source: Internet
Author: User

Objective

The ASP. NET Core changed the previous closure, now open source and open, let's use Redis storage session to do a simple test, or middleware (middleware).

For the session is mixed, many people directly say do not use, there are many people in use, this also does not have absolute this righteousness, individual think as long as do not shadow what and can be convenient to realize the thing is can use, now not can be used as a gesture, we only care about the realization.

Class Library Reference

This is a lot easier than the previous. NET, and you need to add the following to the Dependencies node in Project.json:

    "Stackexchange.redis": "1.1.604-alpha",    "Microsoft.AspNetCore.Session": "1.1.0-alpha1-21694"
Redis implementation

This is not my realization, but the borrowing of https://github.com/aspnet/Caching/tree/dev/src/ Microsoft.Extensions.Caching.Redis code to implement, do not know why there is this class library, and now NuGet is not, in order to not affect the future upgrade my namespace also use Microsoft.Extensions.Caching.Redis

We can see that there are four classes in Microsoft, in fact we only need three, and the fourth one will be wrong:

Using system;using system.threading.tasks;using microsoft.extensions.caching.distributed;using Microsoft.extensions.options;using stackexchange.redis;namespace microsoft.extensions.caching.redis{public class  Rediscache:idistributedcache, IDisposable {//keys[1] = = key//argv[1] = Absolute-expiration-ticks As Long ( -1 for none)//argv[2] = sliding-expiration-ticks as Long ( -1 for none)//argv[3] = relative-e Xpiration (long, in seconds,-1 for none)-Min (Absolute-expiration-now, sliding-expiration)//argv[4] = data-                Byte[]//This order should not change LUA script depends on it private const string setscript = (@ " Redis.call (' Hmset ', keys[1], ' absexp ', argv[1], ' sldexp ', argv[2], ' data ', argv[4]) if argv[3] ~= '        -1 ' then Redis.call (' EXPIRE ', keys[1], argv[3]) end return 1 ");      Private Const string Absoluteexpirationkey = "Absexp";  Private Const string Slidingexpirationkey = "Sldexp";        Private Const string DataKey = "Data";        Private Const LONG notpresent =-1;        Private Connectionmultiplexer _connection;        Private Idatabase _cache;        Private ReadOnly rediscacheoptions _options;        Private readonly string _instance;            Public Rediscache (ioptions<rediscacheoptions> optionsaccessor) {if (optionsaccessor = = null)            {throw new ArgumentNullException (Nameof (optionsaccessor));            } _options = Optionsaccessor.value;            This allows partitioning a, backend cache for use with multiple apps/services. _instance = _options. InstanceName?? String.        Empty; } public byte[] Get (string key) {if (key = = null) {throw new Argument            Nullexception (nameof (key));        } return Getandrefresh (key, getdata:true); } public asYnc task<byte[]> Getasync (string key) {if (key = = null) {throw new Arg            Umentnullexception (nameof (key));        } return await Getandrefreshasync (key, getdata:true); } public void Set (string key, byte[] value, distributedcacheentryoptions options) {if (key = = Nu            LL) {throw new ArgumentNullException (nameof (key));            } if (value = = null) {throw new ArgumentNullException (nameof (value));            } if (options = = null) {throw new ArgumentNullException (nameof (options));            } Connect ();            var creationtime = Datetimeoffset.utcnow;            var absoluteexpiration = getabsoluteexpiration (creationtime, Options); var result = _cache.        Scriptevaluate (Setscript, new rediskey[] {_instance + key}, new redisvalue[] {                Absoluteexpiration?. Ticks?? Notpresent, Options. SlidingExpiration?. Ticks?? Notpresent, Getexpirationinseconds (CreationTime, absoluteexpiration, options)??        Notpresent, value});  Public Async Task Setasync (string key, byte[] value, distributedcacheentryoptions options) {if            (key = = null)            {throw new ArgumentNullException (nameof (key));            } if (value = = null) {throw new ArgumentNullException (nameof (value));            } if (options = = null) {throw new ArgumentNullException (nameof (options));            } await ConnectAsync ();            var creationtime = Datetimeoffset.utcnow;            var absoluteexpiration = getabsoluteexpiration (creationtime, Options); Await _cache. Scriptevaluateasync (Setscript, new rediskey[] {_instanCe + key}, new redisvalue[] {absoluteexpiration?. Ticks?? Notpresent, Options. SlidingExpiration?. Ticks?? Notpresent, Getexpirationinseconds (CreationTime, absoluteexpiration, options)??        Notpresent, value}); } public void Refresh (string key) {if (key = = null) {throw new Argume            Ntnullexception (nameof (key));        } getandrefresh (key, Getdata:false);  Public Async Task Refreshasync (string key) {if (key = = null) {throw            New ArgumentNullException (Nameof (key));        } await Getandrefreshasync (key, Getdata:false); } private void Connect () {if (_connection = = null) {_connection = Con Nectionmultiplexer.connect (_options.            Configuration);    _cache = _connection.            Getdatabase ();                }} Private Async Task ConnectAsync () {if (_connection = = null) { _connection = await Connectionmultiplexer.connectasync (_options.                Configuration); _cache = _connection.            Getdatabase ();                 }} private byte[] Getandrefresh (string key, bool GetData) {if (key = = null) {            throw new ArgumentNullException (nameof (key));            } Connect ();            This also resets the LRU status as desired. Todo:can This is done with one operation on the server side?            Probably, the trick would just be the DateTimeOffset math.            Redisvalue[] results; if (getData) {results = _cache.            Hashmemberget (_instance + key, Absoluteexpirationkey, Slidingexpirationkey, DataKey); } else {results = _cache. HashmemberGet (_instance + key, Absoluteexpirationkey, Slidingexpirationkey); }//Todo:error handling if (results.                Length >= 2) {//Note we always get the back results, even if they is all null.                These operations would no-op in the null scenario. DateTimeOffset?                absexpr; TimeSpan?                sldexpr;                Mapmetadata (results, out absexpr, out sldexpr);            Refresh (Key, absexpr, sldexpr); } if (results. Length >= 3 && results[2].            HasValue) {return results[2];        } return null; } Private Async Task<byte[]> Getandrefreshasync (string key, bool GetData) {if (key = = NULL            ) {throw new ArgumentNullException (nameof (key));            } await ConnectAsync ();            This also resets the LRU status as desired. Todo:can This is done with one operation on the server side?            Probably, the trick would just be the DateTimeOffset math.            Redisvalue[] results; if (getData) {results = await _cache.            Hashmembergetasync (_instance + key, Absoluteexpirationkey, Slidingexpirationkey, DataKey); } else {results = await _cache.            Hashmembergetasync (_instance + key, Absoluteexpirationkey, Slidingexpirationkey); }//Todo:error handling if (results.                Length >= 2) {//Note we always get the back results, even if they is all null.                These operations would no-op in the null scenario. DateTimeOffset?                absexpr; TimeSpan?                sldexpr;                Mapmetadata (results, out absexpr, out sldexpr);            Await Refreshasync (key, absexpr, sldexpr); } if (results. Length >= 3 && results[2].  HasValue)          {return results[2];        } return null; } public void Remove (string key) {if (key = = null) {throw new Argumen            Tnullexception (nameof (key));            } Connect (); _cache.            Keydelete (_instance + key);            Todo:error handling} public Async Task Removeasync (string key) {if (key = = null)            {throw new ArgumentNullException (nameof (key));            } await ConnectAsync (); Await _cache.            Keydeleteasync (_instance + key); Todo:error handling} private void Mapmetadata (redisvalue[] results, out DateTimeOffset? absoluteexpirat Ion, out TimeSpan?            slidingexpiration) {absoluteexpiration = null;            slidingexpiration = null; var absoluteexpirationticks = (long?)            Results[0]; if (Absoluteexpirationticks.hasvalue &&amP Absoluteexpirationticks.value! = notpresent) {absoluteexpiration = new DateTimeOffset (absolutee            Xpirationticks.value, TimeSpan.Zero); } var slidingexpirationticks = (long?)            RESULTS[1]; if (slidingexpirationticks.hasvalue && slidingexpirationticks.value! = notpresent) {SLI            Dingexpiration = new TimeSpan (slidingexpirationticks.value);  }} private void Refresh (string key, DateTimeOffset absexpr, TimeSpan sldexpr) {if (key            = = null) {throw new ArgumentNullException (nameof (key));            }//Note Refresh has no effect if there are just an absolute expiration (or neither). TimeSpan?            expr = null; if (sldexpr.hasvalue) {if (absexpr.hasvalue) {var relexpr =                    Absexpr.value-datetimeoffset.now; Expr = relexpr <= SLDExpr.value?                relexpr:sldexpr;                } else {expr = sldexpr; } _cache.                Keyexpire (_instance + key, expr); Todo:error handling}} Private Async Task Refreshasync (string key, DateTimeOffset? absexpr, TimeSpan?            SLDEXPR) {if (key = = null) {throw new ArgumentNullException (nameof (key));            }//Note Refresh has no effect if there are just an absolute expiration (or neither). TimeSpan?            expr = null; if (sldexpr.hasvalue) {if (absexpr.hasvalue) {var relexpr =                    Absexpr.value-datetimeoffset.now; Expr = relexpr <= sldexpr.value?                relexpr:sldexpr;                } else {expr = sldexpr; } await _cache. Keyexpireasync (_insTance + key, expr); Todo:error handling}} private static Long? Getexpirationinseconds (DateTimeOffset creationtime, DateTimeOffset? absoluteexpiration, Distributedcacheentryoptions options) {if (Absoluteexpiration.hasvalue && options). Slidingexpiration.hasvalue) {return (long) math.min ((Absoluteexpiration.valu E-creationtime). TotalSeconds, Options.            SlidingExpiration.Value.TotalSeconds); } else if (Absoluteexpiration.hasvalue) {return (long) (ABSOLUTEEXPIRATION.VALUE-CR Eationtime).            TotalSeconds; } else if (options. Slidingexpiration.hasvalue) {return (long) options.            SlidingExpiration.Value.TotalSeconds;        } return null; } private static DateTimeOffset? Getabsoluteexpiration (DateTimeOffset creationtime, distributedcacheentryoptIons options) {if (options). Absoluteexpiration.hasvalue && Options.                    Absoluteexpiration <= CreationTime) {throw new ArgumentOutOfRangeException ( Nameof (distributedcacheentryoptions.absoluteexpiration), options.            Absoluteexpiration.value, "The absolute expiration Value must is in the future."); } var absoluteexpiration = options.            absoluteexpiration; if (options. Absoluteexpirationrelativetonow.hasvalue) {absoluteexpiration = CreationTime + options.            Absoluteexpirationrelativetonow;        } return absoluteexpiration; public void Dispose () {if (_connection! = null) {_connection.            Close (); }        }    }}
Using Microsoft.extensions.options;namespace microsoft.extensions.caching.redis{//<summary>///    Configuration options for <see cref= "Rediscache"/>.    </summary> public    class rediscacheoptions:ioptions<rediscacheoptions>    {//        < Summary>//The        configuration used to connect to Redis        . </summary> public        string Configuration {get; set;}        <summary>//The        Redis instance name.        </summary> public        string InstanceName {get; set;}        Rediscacheoptions Ioptions<rediscacheoptions>. Value        {            get {return this;}}}    }
Using system.threading.tasks;using stackexchange.redis;namespace microsoft.extensions.caching.redis{Internal Static class Redisextensions {Private Const string hmgetscript = (@ "Return Redis.call (' Hmget ', keys[1], unpack (        ARGV)) ");            Internal Static redisvalue[] Hashmemberget (this idatabase cache, string key, params string[] members) { var result = cache. Scriptevaluate (Hmgetscript, new rediskey[] {key}, Getredismembers (members)            );            Todo:error checking?        Return (redisvalue[]) result; } internal static Async task<redisvalue[]> Hashmembergetasync (this idatabase cache, St Ring key, params string[] members) {var result = await cache. Scriptevaluateasync (Hmgetscript, new rediskey[] {key}, Getredismembers (mem            bers));            Todo:error checking? Return (REdisvalue[]) result; } private static redisvalue[] Getredismembers (params string[] members) {var redismembers = new R Edisvalue[members.            Length]; for (int i = 0; i < members. Length;            i++) {Redismembers[i] = (redisvalue) members[i];        } return redismembers; }    }}
Configure Enable session

We configureservices increase in startup

          Services. Addsingleton<idistributedcache> (                serviceprovider =                    new Rediscache (new rediscacheoptions                    {                        Configuration = "192.168.178.141:6379",                        INSTANCENAME = "Sample:"                    });            Services. Addsession ();           

Configure increase in Startup

App. Usesession (New Sessionoptions () {IdleTimeout = Timespan.fromminutes (30)});

By the end of this configuration, we can test if it's written in Redis.

Validation results

In the MVC project, let's implement the following code

            if (string. IsNullOrEmpty (HttpContext.Session.GetString ("D")))            {                var d = DateTime.Now.ToString ();                HttpContext.Session.SetString ("D", d);                HttpContext.Response.ContentType = "Text/plain";                Await HttpContext.Response.WriteAsync ("Hello first timer///" + D);            }            else            {                HttpContext.Response.ContentType = "Text/plain";                Await HttpContext.Response.WriteAsync ("Hello old timer///" + HttpContext.Session.GetString ("D"));            }

Run we found that the first time there is a "hello" timer word, after the refresh of the word Hello old timer, proof that the session success, and then look at the Redis see, there is value, such a distributed session has been successfully implemented.

For the above example I put the source on the: Https://github.com/hantianwei/Microsoft.Extensions.Caching.Redis

And also on the NuGet upload a copy, easy to use directly, Tianwei.Microsoft.Extensions.Caching.Redis, just ID added Tianwei The space name or the Microsoft.Extensions.Caching.Redis

From the above example we find that Microsoft is really open this time, which also means that if we use some classes are not comfortable or inappropriate when you can write self-extension

Redis Storage Session

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.