在ASP.NET Core微服务架构下使用数据库切分和扩展
原文链接: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微服务架构下使用数据库切分和扩展相关推荐
- 在ASP.NET Core微服务架构下使用数据库切分和扩展, 并用JMeter进行负载测试
原文链接:https://itnext.io/how-to-scale-an-asp-net-core-microservice-and-sharded-database-load-test-with ...
- 分布式 java 应用:基础与实践_单集群数据超1000亿,微服务架构下分布式数据库应用实践...
如今,大型企业的应用平台正在向微服务架构进行转型.在微服务架构下,应用程序和数据库等底层平台的关系将会被重构. 作为新一代分布式数据库,其架构与功能特性需要保证在与传统数据库全兼容的基础上,拥抱微服务 ...
- javaweb k8s_K8S微服务核心架构学习指南 ASP.NET Core微服务基于K8S 架构师必备Kubernetes教程...
K8S微服务核心架构学习指南 ASP.NET Core微服务基于K8S 架构师必备Kubernetes教程 课程内容是关于Kubernetes微服务架构学习课程,基于K8S开展ASP.NET核心进行微 ...
- .Net Core微服务架构技术栈的那些事
一.前言 大家一直都在谈论微服务架构,园子里面也有很多关于微服务的文章,前几天也有一些园子的朋友问我微服务架构的一些技术,我这里就整理了微服务架构的技术栈路线图,这里就分享出来和大家一起探讨学习,同时 ...
- ASP.NET Core 微服务初探[1]:服务发现之Consul
ASP.NET Core 微服务初探[1]:服务发现之Consul 在传统单体架构中,由于应用动态性不强,不会频繁的更新和发布,也不会进行自动伸缩,我们通常将所有的服务地址都直接写在项目的配置文件中, ...
- Asp.Net Core微服务再体验
Asp.Net Core微服务再体验 原文:Asp.Net Core微服务再体验 ASP.Net Core的基本配置 .在VS中调试的时候有很多修改Web应用运行端口的方法.但是在开发.调试微服务应用 ...
- python角谷猜想递归实现_全新.NET Core平台开发逆袭 重新认知.NET Core微服务架构视频教程 架构师级课程...
全新.NET Core平台开发逆袭课程,将带领同学们重新认知.NET Core微服务架构,是真正的架构师级别的开放课程.课程为同学们打造了一个非常好的框架的起点,重点内容包括了容器环境下配置注入的最佳 ...
- .Net Core微服务架构
目录 一.前言 二.技术栈 2.1 工欲善其事,必先利其器 2.2 微服务 2.3 微服务开源框架 2.4 ORM框架 2.5 分布式跟踪系统 2.6 系统日志集成 2.7 消息队列 2.8 任务调度 ...
- 微服务架构下,解决数据一致性问题的实践
Pic by Alibaba Tech on Facebook 随着业务的快速发展,应用单体架构暴露出代码可维护性差.容错率低.测试难度大和敏捷交付能力差等诸多问题,微服务应运而生.微服务的诞生一方面 ...
最新文章
- solr 3.5 配置及服务器设置
- POJ 3216 Repairing Company【二分图最小路径覆盖】
- c51单片机时钟c语言程序,89c51 C语言单片机 时钟程序
- 诺亚面向语音语义的深度学习研究进展
- UA MATH564 概率论I 求离散型随机变量的分布1
- Android之ButterKnife--View注入框架
- python在windows配置_Python在windows平台的多版本配置
- 爬虫练习:爬豆瓣读书的短评
- 异步tcp通信——APM.Core 服务端概述
- rabbitmq页面出现/etc/rabbitmq/rabbitmq.config(not found)解决方法
- 伊利洛伊大学厄巴纳-香槟分校计算机专业,伊利诺伊大学厄巴纳香槟分校信息管理专业怎么样?...
- uniapp全端应用商城系统,应用市场APP,软件库APP,葫芦芥子博客
- 12.1 hashlib--安全的哈希计算和签名库
- 在线APP设计平台,APP在线开发工具有哪些?
- 中国现代书画家——鞠宗霖
- 智慧路灯点亮新型城市
- oracle 01405 提取的值为null,ORA-01405 : fetched column value is NULL
- selenium java框架_自动化测试框架selenium+java+TestNG——配置篇
- 了解“新基建”、读罢IDC报告后,还请查收来自浪潮的硬核实力!
- Thebrain12官方正式版来了,Beta拜拜!
热门文章
- CCNA,CCNP资料
- java contains 通配符_java删除文件支持通配符
- java dateutil 获取时间戳_java DateUtil工具类时间戳类型转换详解
- python截取关键字后的字符串_使用正则表达式获取python中特定字符串之后的所有内容...
- 基于.NET2.0的System.Net.Mail发送邮件Demo
- [Docker]记一次使用jenkins将镜像文件推送到Harbor遇到的问题
- 基于GDAL库,读取海洋风场数据(.nc格式)c++版
- sublime text3:提示 There are no packages available installation 解决方案
- 刚接触git,提交文件时,遇到no changes added to commit
- [ 转载 ] Java面试精选【Java基础第一部分】