您当前的位置: 首页 >  微服务

寒冰屋

暂无认证

  • 1浏览

    0关注

    2286博文

    0收益

  • 0浏览

    0点赞

    0打赏

    0留言

私信
关注
热门博文

一个简单的键值存储微服务

寒冰屋 发布时间:2021-11-03 22:17:19 ,浏览量:1

目录

介绍

我需要的

我不需要的东西

Redis

应用程序接口

实现

助手类

Bucket类

Buckets类

测试

创建存储桶

更新存储桶

列出存储桶

获取桶本身

删除桶

结论

  • 下载源代码 - 4.3 KB

https://www.codeproject.com/KB/Articles/5306500/memoryBucket.png

介绍

我有一个非常具体的用例,我需要一个微服务来管理一个简单的内存数据存储,我称之为“存储桶”。令我惊讶的是,我对现有简单解决方案的简短谷歌搜索是空洞的,但这对于课程来说是一样的,因为简单的事情会发展成复杂的事情,而简单的事情最终会被遗忘。

我的具体用例是我需要跟踪谁在使用资源以及使用了多长时间。用户能够指出他们正在使用资源以及何时使用完该资源,并且用户能够添加关于资源使用情况的注释。因此,其他用户可以查看谁在使用资源、他们使用了多长时间以及有关其使用情况的任何注释。鉴于此,我的要求更多是关于我不需要的东西而不是我需要的东西。

我需要的
  1. 将键设置为值的能力。
我不需要的东西
  1. 我甚至不需要管理不同存储桶的能力,但不实现该功能似乎有点愚蠢,所以它被实现了。
  2. 如果服务器重新启动,我不在乎内存是否丢失。这仅适用于将在不同客户端之间共享的信息数据。从技术上讲,前端客户端之一(因为客户端将拥有完整数据)甚至可以为其他所有人恢复数据。
  3. 我不需要复杂的数据结构,只需要键值对,其中值是某种值类型,而不是结构。
  4. 我甚至不关心一个用户踩到另一个用户的顶部——在实际使用中,这不会发生,如果发生了,我也不在乎——最后更新密钥的人获胜。
Redis

“Redis是一种开源(BSD许可)、内存中数据结构存储,用作数据库、缓存和消息代理。Redis提供诸如字符串、哈希、列表、集合、带有范围查询的排序集合、位图的数据结构、超级日志、地理空间索引和流。Redis具有内置复制、Lua脚本、LRU驱逐、事务和不同级别的磁盘持久性,并通过Redis Sentinel和Redis Cluster自动分区提供高可用性。”

哎呀。对于我需要的东西来说绝对是大材小用。也就是说,请不要用它来代替像Redis这样的东西,除非你的要求真的符合这里的要求。

应用程序接口

浏览到http://localhost:7672/swagger/index.html,我们看到:

https://www.codeproject.com/KB/Articles/5306500/api.png

正如您从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();
  }
}

Buckets类

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

关注
打赏
1665926880
查看更多评论
立即登录/注册

微信扫码登录

0.0529s