在开发过程中,经常会遇到需要跟踪资源使用情况的场景,例如谁在使用某个资源,使用时间长短,以及使用过程中的备注信息等。本文将介绍一个简易的内存数据存储解决方案,称之为“桶”(bucket)。这个解决方案的目的是满足基本的键值对存储需求,同时提供足够的灵活性以适应不同的使用场景。
在设计这个解决方案之前,首先明确了需要实现的功能和不需要的功能。
需要的功能:
不需要的功能:
在技术选型方面,考虑了Redis,它是一个开源的内存数据结构存储系统,用作数据库、缓存和消息代理。Redis提供了字符串、哈希、列表、集合、有序集合、位图、HyperLogLog、地理空间索引和流等多种数据结构。然而,对于需求来说,Redis显然过于复杂,因此选择了更简单的实现方式。
API设计如下:
通过访问 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'