原文链接:https://itnext.io/how-to-use-database-sharding-and-scale-an-asp-net-core-microservice-architecture-22c24916590f

微服务的一大优点是,它们可以独立扩展。本文展示了扩展一个微服务及其数据库的好处和挑战

您将创建一个示例应用程序并手动实现应用程序层分片。它展示了如何根据用例和数据模型选择分片Key。这有助于将相同的原理应用到具有集成扩展(如MongoDB等)的DBMS上。

1.用例和数据模型

示例应用程序由一个User和Post微服务组成。它们通过消息交流:

User微服务处理添加和修改用户。Post微服务处理查看和添加帖子。因为与Post微服务的交互要多得多,所以,当应用程序的负载增加时,Post微服务将成为第一个需要扩展的微服务。

作者的名字是PostService绑定上下文的一部分,因此也是Post微服务的一部分。在User微服务中添加和修改作者。User微服务在添加新用户或更改用户名时发送事件。

PostService的逻辑数据模型

用户可以分类写文章。他们还可以按类别阅读帖子,包括作者姓名。最新的帖子在上面。分类是固定的,很少改变。

基于这些用例,我决定按类别划分数据库分片:

2.实现微服务

创建解决方案并添加名为“PostService”的ASP.NET Core 5 Web API项目。禁用HTTPS并激活OpenAPI支持。

安装以下NuGet软件包:

  • Microsoft.EntityFrameworkCore.Tools

  • MySql.EntityFrameworkCore

  • Newtonsoft.Json

创建实体

Post实体的索引可以加快检索某个类别中最新的帖子:

using Microsoft.EntityFrameworkCore;
using System.ComponentModel.DataAnnotations;namespace PostService.Entities
{[Index(nameof(PostId), nameof(CategoryId))]public class Post{public int PostId { get; set; }public string Title { get; set; }public string Content { get; set; }public int UserId { get; set; }public User User { get; set; }[Required]public string CategoryId { get; set; }public Category Category { get; set; }}
}

User实体中的版本稍后将帮助处理无序消息:

namespace PostService.Entities
{public class User{public int ID { get; set; }public string Name { get; set; }public int Version { get; set; }}
}namespace PostService.Entities
{public class Category{public string CategoryId { get; set; }}
}

创建PostServiceContext

using Microsoft.EntityFrameworkCore;namespace PostService.Data
{public class PostServiceContext : DbContext{private readonly string _connectionString;public PostServiceContext(string connectionString){_connectionString = connectionString;}protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder){optionsBuilder.UseMySQL(_connectionString);}public DbSet<PostService.Entities.Post> Post { get; set; }public DbSet<PostService.Entities.User> User { get; set; }public DbSet<PostService.Entities.Category> Category { get; set; }}
}

在appsettings.Development.json中添加连接字符串(在调试期间将使用两个分片)

{"Logging": {"LogLevel": {"Default": "Information","Microsoft": "Warning","Microsoft.Hosting.Lifetime": "Information"}},"PostDbConnectionStrings": {"Shard0": "server=localhost; port=3310; database=post; user=root; password=pw; Persist Security Info=False; Connect Timeout=300","Shard1": "server=localhost; port=3311; database=post; user=root; password=pw; Persist Security Info=False; Connect Timeout=300"    }
}

添加DataAccess代码

GetConnectionString(string category)计算CategoryId的哈希值。哈希的第一部分将配置的分片数(连接字符串)取模,从而确定给定类别的分片。

InitDatabase删除并重新创建所有分片中的所有表,并插入虚拟用户和类别。

其他方法用于创建和加载帖子。

using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using PostService.Entities;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;namespace PostService.Data
{public class DataAccess{private readonly List<string> _connectionStrings = new List<string>();public DataAccess(IConfiguration configuration){var connectionStrings = configuration.GetSection("PostDbConnectionStrings");foreach(var connectionString in connectionStrings.GetChildren()){Console.WriteLine("ConnectionString: " + connectionString.Value);_connectionStrings.Add(connectionString.Value);}}public async Task<ActionResult<IEnumerable<Post>>> ReadLatestPosts(string category, int count){using var dbContext = new PostServiceContext(GetConnectionString(category));return await dbContext.Post.OrderByDescending(p => p.PostId).Take(count).Include(x => x.User).Where(p => p.CategoryId == category).ToListAsync();}public async Task<int> CreatePost(Post post){using var dbContext = new PostServiceContext(GetConnectionString(post.CategoryId));dbContext.Post.Add(post);return await dbContext.SaveChangesAsync();}public void InitDatabase(int countUsers, int countCategories){foreach (var connectionString in _connectionStrings){using var dbContext = new PostServiceContext(connectionString);dbContext.Database.EnsureDeleted();dbContext.Database.EnsureCreated();for (int i = 1; i <= countUsers; i++){dbContext.User.Add(new User { Name = "User" + i, Version = 1 });dbContext.SaveChanges();}for (int i = 1; i <= countCategories; i++){dbContext.Category.Add(new Category { CategoryId = "Category" + i });dbContext.SaveChanges();}}}private string GetConnectionString(string category){using var md5 = MD5.Create();var hash = md5.ComputeHash(Encoding.ASCII.GetBytes(category));var x = BitConverter.ToUInt16(hash, 0) % _connectionStrings.Count;return  _connectionStrings[x];}}
}

在Startup.cs中将DataAccess注册为单例

public class Startup
{...public void ConfigureServices(IServiceCollection services){services.AddControllers();services.AddSwaggerGen(c =>{c.SwaggerDoc("v1", new OpenApiInfo { Title = "PostService", Version = "v1" });});services.AddSingleton<DataAccess>();}---

创建PostController

它使用DataAccess类

using Microsoft.AspNetCore.Mvc;
using PostService.Data;
using PostService.Entities;
using System.Collections.Generic;
using System.Threading.Tasks;namespace PostService.Controllers
{[Route("api/[controller]")][ApiController]public class PostsController : ControllerBase{private readonly DataAccess _dataAccess;public PostsController(DataAccess dataAccess){_dataAccess = dataAccess;}[HttpGet]public async Task<ActionResult<IEnumerable<Post>>> GetLatestPosts(string category, int count){return await _dataAccess.ReadLatestPosts(category, count);}[HttpPost]public async Task<ActionResult<Post>> PostPost(Post post){await _dataAccess.CreatePost(post);return NoContent();}[HttpGet("InitDatabase")]public void InitDatabase([FromQuery] int countUsers, [FromQuery] int countCategories){_dataAccess.InitDatabase(countUsers, countCategories);}}
}

3. 用PostService访问数据库

安装Docker Desktop。

创建两个MySql容器

C:\dev>docker run -p 3310:3306 --name=mysql1 -e MYSQL_ROOT_PASSWORD=pw -d mysql:5.6
C:\dev>docker run -p 3311:3306 --name=mysql2 -e MYSQL_ROOT_PASSWORD=pw -d mysql:5.6

在Visual Studio中启动Post服务。浏览器在打开http://localhost:5001/swagger/index.html

使用swagger UI与服务交互:

初始化包含100个用户和10个类别的数据库:

在“Category1”下增加一个帖子:

{"title": "MyTitle","content": "MyContent","userId": 1,"categoryId": "Category1"
}

阅读“Category1”中排名前10位的帖子:

连接到数据库容器并验证哪个数据库包含新的帖子

C:\dev>docker container exec -it mysql1 /bin/sh

使用密码“pw”登录MySql并读取帖子:

第二个实例不包含任何帖子:

C:\dev>docker container exec -it mysql2 /bin/sh

4.最后的想法和展望

您创建了一个可工作的应用程序,实现了应用程序层分片,并使用了分片Key的概念。

这只是一个示例应用程序。您必须调整代码才能在生产环境中使用它。

在第二部分中,您将缩放并运行微服务和数据库的多个容器实例。您将使用docker compose和负载平衡器。然后,您将运行JMeter负载测试,以查看应用程序在使用不同数量的实例时是如何伸缩的。最后,您将通过RabbitMQ模拟来自用户微服务的用户事件。

欢迎关注我的个人公众号”My IO“

在ASP.NET Core微服务架构下使用数据库切分和扩展相关推荐

  1. 在ASP.NET Core微服务架构下使用数据库切分和扩展, 并用JMeter进行负载测试

    原文链接:https://itnext.io/how-to-scale-an-asp-net-core-microservice-and-sharded-database-load-test-with ...

  2. 分布式 java 应用:基础与实践_单集群数据超1000亿,微服务架构下分布式数据库应用实践...

    如今,大型企业的应用平台正在向微服务架构进行转型.在微服务架构下,应用程序和数据库等底层平台的关系将会被重构. 作为新一代分布式数据库,其架构与功能特性需要保证在与传统数据库全兼容的基础上,拥抱微服务 ...

  3. javaweb k8s_K8S微服务核心架构学习指南 ASP.NET Core微服务基于K8S 架构师必备Kubernetes教程...

    K8S微服务核心架构学习指南 ASP.NET Core微服务基于K8S 架构师必备Kubernetes教程 课程内容是关于Kubernetes微服务架构学习课程,基于K8S开展ASP.NET核心进行微 ...

  4. .Net Core微服务架构技术栈的那些事

    一.前言 大家一直都在谈论微服务架构,园子里面也有很多关于微服务的文章,前几天也有一些园子的朋友问我微服务架构的一些技术,我这里就整理了微服务架构的技术栈路线图,这里就分享出来和大家一起探讨学习,同时 ...

  5. ASP.NET Core 微服务初探[1]:服务发现之Consul

    ASP.NET Core 微服务初探[1]:服务发现之Consul 在传统单体架构中,由于应用动态性不强,不会频繁的更新和发布,也不会进行自动伸缩,我们通常将所有的服务地址都直接写在项目的配置文件中, ...

  6. Asp.Net Core微服务再体验

    Asp.Net Core微服务再体验 原文:Asp.Net Core微服务再体验 ASP.Net Core的基本配置 .在VS中调试的时候有很多修改Web应用运行端口的方法.但是在开发.调试微服务应用 ...

  7. python角谷猜想递归实现_全新.NET Core平台开发逆袭 重新认知.NET Core微服务架构视频教程 架构师级课程...

    全新.NET Core平台开发逆袭课程,将带领同学们重新认知.NET Core微服务架构,是真正的架构师级别的开放课程.课程为同学们打造了一个非常好的框架的起点,重点内容包括了容器环境下配置注入的最佳 ...

  8. .Net Core微服务架构

    目录 一.前言 二.技术栈 2.1 工欲善其事,必先利其器 2.2 微服务 2.3 微服务开源框架 2.4 ORM框架 2.5 分布式跟踪系统 2.6 系统日志集成 2.7 消息队列 2.8 任务调度 ...

  9. 微服务架构下,解决数据一致性问题的实践

    Pic by Alibaba Tech on Facebook 随着业务的快速发展,应用单体架构暴露出代码可维护性差.容错率低.测试难度大和敏捷交付能力差等诸多问题,微服务应运而生.微服务的诞生一方面 ...

最新文章

  1. solr 3.5 配置及服务器设置
  2. POJ 3216 Repairing Company【二分图最小路径覆盖】
  3. c51单片机时钟c语言程序,89c51 C语言单片机 时钟程序
  4. 诺亚面向语音语义的深度学习研究进展
  5. UA MATH564 概率论I 求离散型随机变量的分布1
  6. Android之ButterKnife--View注入框架
  7. python在windows配置_Python在windows平台的多版本配置
  8. 爬虫练习:爬豆瓣读书的短评
  9. 异步tcp通信——APM.Core 服务端概述
  10. rabbitmq页面出现/etc/rabbitmq/rabbitmq.config(not found)解决方法
  11. 伊利洛伊大学厄巴纳-香槟分校计算机专业,伊利诺伊大学厄巴纳香槟分校信息管理专业怎么样?...
  12. uniapp全端应用商城系统,应用市场APP,软件库APP,葫芦芥子博客
  13. 12.1 hashlib--安全的哈希计算和签名库
  14. 在线APP设计平台,APP在线开发工具有哪些?
  15. 中国现代书画家——鞠宗霖
  16. 智慧路灯点亮新型城市
  17. oracle 01405 提取的值为null,ORA-01405 : fetched column value is NULL
  18. selenium java框架_自动化测试框架selenium+java+TestNG——配置篇
  19. 了解“新基建”、读罢IDC报告后,还请查收来自浪潮的硬核实力!
  20. Thebrain12官方正式版来了,Beta拜拜!

热门文章

  1. CCNA,CCNP资料
  2. java contains 通配符_java删除文件支持通配符
  3. java dateutil 获取时间戳_java DateUtil工具类时间戳类型转换详解
  4. python截取关键字后的字符串_使用正则表达式获取python中特定字符串之后的所有内容...
  5. 基于.NET2.0的System.Net.Mail发送邮件Demo
  6. [Docker]记一次使用jenkins将镜像文件推送到Harbor遇到的问题
  7. 基于GDAL库,读取海洋风场数据(.nc格式)c++版
  8. sublime text3:提示 There are no packages available installation 解决方案
  9. 刚接触git,提交文件时,遇到no changes added to commit
  10. [ 转载 ] Java面试精选【Java基础第一部分】