简易内存数据存储解决方案

在开发过程中,经常会遇到需要跟踪资源使用情况的场景,例如谁在使用某个资源,使用时间长短,以及使用过程中的备注信息等。本文将介绍一个简易的内存数据存储解决方案,称之为“桶”(bucket)。这个解决方案的目的是满足基本的键值对存储需求,同时提供足够的灵活性以适应不同的使用场景。

需求分析

在设计这个解决方案之前,首先明确了需要实现的功能和不需要的功能。

需要的功能:

  • 能够设置键值对。

不需要的功能:

  • 不需要管理不同的桶,但考虑到功能的完整性,还是实现了这个特性。
  • 不需要在服务器重启后保留内存数据,因为这些数据仅用于信息共享,并且前端客户端可以恢复数据。
  • 不需要复杂的数据结构,只需要键值对,其中值是简单的数据类型,而不是结构化数据。
  • 不需要处理用户之间的数据冲突,因为在实际使用中这种情况很少发生,即使发生了,最后更新键的用户的数据为准。

技术选型

在技术选型方面,考虑了Redis,它是一个开源的内存数据结构存储系统,用作数据库、缓存和消息代理。Redis提供了字符串、哈希、列表、集合、有序集合、位图、HyperLogLog、地理空间索引和流等多种数据结构。然而,对于需求来说,Redis显然过于复杂,因此选择了更简单的实现方式。

API设计

API设计如下:

  • 通过名称获取桶的内容。
  • 列出当前内存中的所有桶。
  • 获取桶对象,包括桶的数据和元数据(仅桶名称)。
  • 删除桶。
  • 将桶的数据设置为POST请求体中的键值对
  • 更新桶的指定键的值。

通过访问 http://localhost:7672/swagger/index.html,可以看到Swagger文档中的这些端点。

实现

实现使用了C# ASP.NET Core 3.1,控制器代码总共123行,非常简单。以下是完整的控制器代码:

using System.Collections.Generic; using System.Linq; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.ModelBinding; using MemoryBucket.Classes; namespace MemoryBucket.Controllers { [ApiController] [Route("[controller]")] public class MemoryBucketController : ControllerBase { private static Buckets buckets = new Buckets(); private static object locker = new object(); public MemoryBucketController() { } [HttpGet] public object Get([FromQuery, BindRequired] string bucketName) { lock (locker) { Assertion.That(buckets.TryGetValue(bucketName, out Bucket bucket), $"No bucket with name {bucketName} exists."); return bucket.Data; } } [HttpGet("List")] public object GetBucket() { lock (locker) { return buckets.Select(b => b.Key); } } [HttpGet("Bucket")] public object GetBucket([FromQuery, BindRequired] string bucketName) { lock (locker) { Assertion.That(buckets.TryGetValue(bucketName, out Bucket bucket), $"No bucket with name {bucketName} exists."); return bucket; } } [HttpDelete("{bucketName}")] public void Delete(string bucketName) { lock (locker) { Assertion.That(buckets.TryGetValue(bucketName, out Bucket bucket), $"No bucket with name {bucketName} exists."); buckets.Remove(bucketName); } } [HttpPost("Set")] public object Post([FromQuery, BindRequired] string bucketName, [FromBody] Dictionary data) { lock (locker) { var bucket = CreateOrGetBucket(bucketName); bucket.Data = data; return data; } } [HttpPost("Update")] public object Post([FromQuery, BindRequired] string bucketName, [FromQuery, BindRequired] string key, [FromQuery, BindRequired] string value) { lock (locker) { var bucket = CreateOrGetBucket(bucketName); var data = bucket.Data; data[key] = value; return data; } } private Bucket CreateOrGetBucket(string bucketName) { if (!buckets.TryGetValue(bucketName, out Bucket bucket)) { bucket = new Bucket(); bucket.Name = bucketName; buckets[bucketName] = bucket; } return bucket; } } }

辅助类如下:

// The Bucket Class using System.Collections.Generic; namespace MemoryBucket.Classes { public class Bucket { public string Name { get; set; } public Dictionary Data { get; set; } = new Dictionary(); } } // The Buckets Class using System.Collections.Generic; namespace MemoryBucket.Classes { public class Buckets : Dictionary { } }

测试

可以使用Postman进行一些测试。

创建桶:

curl --location --request POST 'http://localhost:7672/memoryBucket/Set?bucketName=Soup' \ --header 'Content-Type: application/json' \ --data-raw '{ "Name":"Marc", "Resource": "Garlic", "Note": "For the soup" }'

更新桶:

curl --location --request POST 'http://localhost:7672/memoryBucket/Update?bucketName=Soup&key=Name&value=Kate'

列出桶:

curl --location --request GET 'http://localhost:7672/memoryBucket/List'

获取桶本身:

curl --location --request GET 'http://localhost:7672/memoryBucket/Bucket?bucketName=Soup'

删除桶:

curl --location --request DELETE 'http://localhost:7672/memoryBucket/Soup'
沪ICP备2024098111号-1
上海秋旦网络科技中心:上海市奉贤区金大公路8218号1幢 联系电话:17898875485