目录
介绍
我需要的
我不需要的东西
Redis
应用程序接口
实现
助手类
Bucket类
Buckets类
测试
创建存储桶
更新存储桶
列出存储桶
获取桶本身
删除桶
结论
- 下载源代码 - 4.3 KB
我有一个非常具体的用例,我需要一个微服务来管理一个简单的内存数据存储,我称之为“存储桶”。令我惊讶的是,我对现有简单解决方案的简短谷歌搜索是空洞的,但这对于课程来说是一样的,因为简单的事情会发展成复杂的事情,而简单的事情最终会被遗忘。
我的具体用例是我需要跟踪谁在使用资源以及使用了多长时间。用户能够指出他们正在使用资源以及何时使用完该资源,并且用户能够添加关于资源使用情况的注释。因此,其他用户可以查看谁在使用资源、他们使用了多长时间以及有关其使用情况的任何注释。鉴于此,我的要求更多是关于我不需要的东西而不是我需要的东西。
我需要的- 将键设置为值的能力。
- 我甚至不需要管理不同存储桶的能力,但不实现该功能似乎有点愚蠢,所以它被实现了。
- 如果服务器重新启动,我不在乎内存是否丢失。这仅适用于将在不同客户端之间共享的信息数据。从技术上讲,前端客户端之一(因为客户端将拥有完整数据)甚至可以为其他所有人恢复数据。
- 我不需要复杂的数据结构,只需要键值对,其中值是某种值类型,而不是结构。
- 我甚至不关心一个用户踩到另一个用户的顶部——在实际使用中,这不会发生,如果发生了,我也不在乎——最后更新密钥的人获胜。
“Redis是一种开源(BSD许可)、内存中数据结构存储,用作数据库、缓存和消息代理。Redis提供诸如字符串、哈希、列表、集合、带有范围查询的排序集合、位图的数据结构、超级日志、地理空间索引和流。Redis具有内置复制、Lua脚本、LRU驱逐、事务和不同级别的磁盘持久性,并通过Redis Sentinel和Redis Cluster自动分区提供高可用性。”
哎呀。对于我需要的东西来说绝对是大材小用。也就是说,请不要用它来代替像Redis这样的东西,除非你的要求真的符合这里的要求。
应用程序接口浏览到http://localhost:7672/swagger/index.html,我们看到:
正如您从Swagger文档中看到的(有点),我们有以下端点:
- 按名称获取存储桶的内容。
- 按名称列出当前内存中的所有存储桶。
- 获取存储桶对象,该对象将包括存储桶的数据和存储桶的元数据,即存储桶名称。
- 删除存储桶。
- 将桶的数据设置为POST正文中的键值对。
- 使用指定的键值更新存储桶的数据。
实现(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()
{
}
///
/// Returns the contents of an existing bucket.
///
[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;
}
}
///
/// Lists all buckets.
///
[HttpGet("List")]
public object GetBucket()
{
lock (locker)
{
return buckets.Select(b => b.Key);
}
}
///
/// Returns the bucket itself.
///
[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;
}
}
///
/// Deletes an existing 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);
}
}
///
/// Creates or gets an existing bucket and replaces its data.
///
[HttpPost("Set")]
public object Post(
[FromQuery, BindRequired] string bucketName,
[FromBody] Dictionary data)
{
lock (locker)
{
var bucket = CreateOrGetBucket(bucketName);
bucket.Data = data;
return data;
}
}
///
/// Creates or gets an existing bucket and updates the specified key with the
/// specified value.
///
[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;
}
}
}
尽量不要敬畏。我真的不认为这需要解释。
Bucket类using System.Collections.Generic;
namespace MemoryBucket.Classes
{
public class Bucket
{
public string Name { get; set; }
public Dictionary Data { get; set; } = new Dictionary();
}
}
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"
}'
我们看到响应如下:
{
"Name": "Marc",
"Resource": "Garlic",
"Note": "For the soup"
}
凯特正在接手做汤:
curl --location
--request POST 'http://localhost:7672/memoryBucket/Update?bucketName=Soup&key=Name&value=Kate'
我们看到响应如下:
{
"Name": "Kate",
"Resource": "Garlic",
"Note": "For the soup"
}
我正在添加另一个存储桶:
curl --location --request POST 'http://localhost:7672/memoryBucket/Set?bucketName=Salad' \
--header 'Content-Type: application/json' \
--data-raw '{
"Name":"Laurie",
"Resource": "Lettuce"
}'
当我列出桶时:
curl --location --request GET 'http://localhost:7672/memoryBucket/List'
我懂了:
[
"Salad",
"Soup"
]
curl --location --request GET 'http://localhost:7672/memoryBucket/Bucket?bucketName=Soup'
我看到:
{
"name": "Soup",
"data": {
"Name": "Marc",
"Resource": "Garlic",
"Note": "For the soup"
}
}
晚餐准备好了:
curl --location --request DELETE '
http://localhost:7672/memoryBucket/Soup'
curl --location --request DELETE '
http://localhost:7672/memoryBucket/Salad'
curl --location --request GET 'http://localhost:7672/memoryBucket/List'
没有更多的桶:
[]
很简单吧?让我想知道为什么我们不再做简单的事情了。
A Simple Key-Value Store Microservice - CodeProject