.Net Redis实战——使用Redis构建Web应用

示例介绍

示例1:借助Redis实现购物车功能

示例2:Redis实现网页缓存和数据缓存

 

借助Redis实现购物车功能

每个用户的购物车都是一个散列,散列存储了商品ID与商品订购数量之间的映射。订购商品时,对购物车进行更新:如果用户订购某件商品的数量大于0,那么将这件商品的ID以及用户订购该商品的数量添加到散列里面,如果用户购买的商品已经存在散列里面,那么新的订购数量会覆盖已有的订购数量;相反,如果用户订购某件商品数量不大于0,那么程序将从散列里面移除该条目。

 

由于整个示例分Controller和WebApi两部分,在不考虑封装Redis操作的情况下提取Redis连接字符串到appSetting.json中

"ConnectionStrings": {
    "Redis": "localhost,abortConnect=false"
  },

 

保存用户购物查数据示例代码:

[HttpGet("UpdateCart")]
        public string UpdateCart(string token,string item,int count)
        {
            var key = $"cart:{token}";
            using (var redis = ConnectionMultiplexer.Connect(redisConnectionStr))
            {
                var db = redis.GetDatabase();
                //更新购物车数据
                if (count <= 0)
                {
                    db.HashDeleteAsync(key, item);
                }
                else
                {
                    db.HashSetAsync(key, item,count);
                }
            }
            
            return "更新购物车成功";
        }

 

Redis实现网页缓存和数据缓存

针对不会频繁更新得动态网页或Ajax返回的数据可以缓存在Redis中,减少与数据库的交互提升系统效应效率。

该示例只是为了演示借助Reids实现页面缓存和数据缓存效果,实际使用中可通过ResponseCache特性实现缓存。

详细介绍:https://docs.microsoft.com/en-us/aspnet/core/performance/caching/response?view=aspnetcore-5.0

 

网页数据缓存

新建示例项目时为WebApi项目,修改Startup类添加Web Mvc支持。

ConfigureServices方法中修改services.AddControllers()services.AddControllersWithViews()

Configure方法中app.UseEndpoints修改为如下代码

app.UseEndpoints(endpoints =>
            {
                endpoints.MapControllerRoute(
                    name: "default",
                    pattern: "{controller=Home}/{action=Index}/{id?}");
            });

 

定义中间件类RedisCacheMiddleware,用来缓存网页页面

public class RedisCacheMiddleware
    {

        private readonly RequestDelegate _next;

        public IConfiguration Configuration { get; }

        public RedisCacheMiddleware(RequestDelegate next, IConfiguration configuration)
        {
            _next = next;
            Configuration = configuration;
        }

        public async Task InvokeAsync(HttpContext context)
        {

            var path = context.Request.Path.Value.ToLower();
            
            if (path == "/home" || path == "/home/index")
            {

                var responseContent = "";

                //Copy a pointer to the original response body stream
                var originalBodyStream = context.Response.Body;

                using (var redis = ConnectionMultiplexer.Connect(Configuration.GetConnectionString("Redis")))
                {
                    var db = redis.GetDatabase();
                    if (db.KeyExists(path))
                    {
                        responseContent = db.StringGet(path);
                        await RespondWithIndexHtml(context.Response, responseContent);
                        return;
                    }
                    else
                    {
                        using (var responseBody = new MemoryStream())
                        {
                            //...and use that for the temporary response body
                            context.Response.Body = responseBody;

                            //Continue down the Middleware pipeline, eventually returning to this class
                            await _next(context);

                            //Format the response from the server
                            responseContent = await FormatResponse(context.Response);

                            //Copy the contents of the new memory stream (which contains the response) to the original stream, which is then returned to the client.
                            await responseBody.CopyToAsync(originalBodyStream);
                        }

                        db.StringSet(path, responseContent, expiry: TimeSpan.FromSeconds(30));
                    }
                }

            }

            // Call the next delegate/middleware in the pipeline
            await _next(context);
        }

        private async Task RespondWithIndexHtml(HttpResponse response,string html)
        {
            response.StatusCode = 200;
            response.ContentType = "text/html";

            await response.WriteAsync(html, Encoding.UTF8);
        }


        private async Task<string> FormatResponse(HttpResponse response)
        {
            //We need to read the response stream from the beginning...
            response.Body.Seek(0, SeekOrigin.Begin);

            //...and copy it into a string
            string text = await new StreamReader(response.Body).ReadToEndAsync();

            //We need to reset the reader for the response so that the client can read it.
            response.Body.Seek(0, SeekOrigin.Begin);

            //Return the string for the response
            return text;
        }

    }

添加中间件调用

app.UseMiddleware<RedisCacheMiddleware>();

运行效果如图:

网页上的时间会每30秒更新一次。

.Net Core提供的响应缓存中间件:https://docs.microsoft.com/zh-cn/aspnet/core/performance/caching/middleware?view=aspnetcore-5.0

Ajax数据缓存

本例只是简单的数据存储,实际使用中可编写单独的服务,让这个服务将指定的数据缓存到Redis里面,并不定期地对这些缓存进行更新。

 示例代码

[HttpGet("GetDateTime")]
        public string GetDateTime()
        {
            var dateTime = "";
            var key = "data:datetime";
            using (var redis = ConnectionMultiplexer.Connect(redisConnectionStr))
            {
                var db = redis.GetDatabase();
                if (db.KeyExists(key))
                {
                    dateTime = db.StringGet(key);
                }
                else
                {
                    dateTime = DateTimeOffset.Now.ToString("yyyy-MM-dd HH:mm:ss");
                    db.StringSet(key, dateTime,expiry:TimeSpan.FromSeconds(5));
                }
            }

            return dateTime;
        }

 

参考资料

Using Middleware in .NET 5.0 to Log Requests and Responses

如何在 asp.net core 的中间件中返回具体的页面

Swashbuckle.AspNetCore.ReDoc/ReDocMiddleware.cs

posted @ 2021-01-23 21:05  Stacking  阅读(564)  评论(0编辑  收藏  举报