在ASP.NET Core微服务架构下使用数据库切分和扩展
原文鏈接:https://itnext.io/how-to-use-database-sharding-and-scale-an-asp-net-core-microservice-architecture-22c24916590f
微服務的一大優(yōu)點是,它們可以獨立擴展。本文展示了擴展一個微服務及其數(shù)據(jù)庫的好處和挑戰(zhàn)。
您將創(chuàng)建一個示例應用程序并手動實現(xiàn)應用程序?qū)臃制?。它展示了如何根?jù)用例和數(shù)據(jù)模型選擇分片Key。這有助于將相同的原理應用到具有集成擴展(如MongoDB等)的DBMS上。
1.用例和數(shù)據(jù)模型
示例應用程序由一個User和Post微服務組成。它們通過消息交流:?
User微服務處理添加和修改用戶。Post微服務處理查看和添加帖子。因為與Post微服務的交互要多得多,所以,當應用程序的負載增加時,Post微服務將成為第一個需要擴展的微服務。
作者的名字是PostService綁定上下文的一部分,因此也是Post微服務的一部分。在User微服務中添加和修改作者。User微服務在添加新用戶或更改用戶名時發(fā)送事件。
PostService的邏輯數(shù)據(jù)模型
用戶可以分類寫文章。他們還可以按類別閱讀帖子,包括作者姓名。最新的帖子在上面。分類是固定的,很少改變。
基于這些用例,我決定按類別劃分數(shù)據(jù)庫分片:
2.實現(xiàn)微服務
創(chuàng)建解決方案并添加名為“PostService”的ASP.NET Core 5 Web API項目。禁用HTTPS并激活OpenAPI支持。
安裝以下NuGet軟件包:
Microsoft.EntityFrameworkCore.Tools
MySql.EntityFrameworkCore
Newtonsoft.Json
創(chuàng)建實體
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;?}} }創(chuàng)建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中添加連接字符串(在調(diào)試期間將使用兩個分片)
{"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的哈希值。哈希的第一部分將配置的分片數(shù)(連接字符串)取模,從而確定給定類別的分片。
InitDatabase刪除并重新創(chuàng)建所有分片中的所有表,并插入虛擬用戶和類別。
其他方法用于創(chuàng)建和加載帖子。
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>();}---創(chuàng)建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訪問數(shù)據(jù)庫
安裝Docker Desktop。
創(chuàng)建兩個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個類別的數(shù)據(jù)庫:?
在“Category1”下增加一個帖子:
{"title":?"MyTitle","content":?"MyContent","userId":?1,"categoryId":?"Category1"
}
閱讀“Category1”中排名前10位的帖子:
連接到數(shù)據(jù)庫容器并驗證哪個數(shù)據(jù)庫包含新的帖子。
使用密碼“pw”登錄MySql并讀取帖子:?
第二個實例不包含任何帖子:
4.最后的想法和展望
您創(chuàng)建了一個可工作的應用程序,實現(xiàn)了應用程序?qū)臃制?#xff0c;并使用了分片Key的概念。
這只是一個示例應用程序。您必須調(diào)整代碼才能在生產(chǎn)環(huán)境中使用它。
在第二部分中,您將縮放并運行微服務和數(shù)據(jù)庫的多個容器實例。您將使用docker compose和負載平衡器。然后,您將運行JMeter負載測試,以查看應用程序在使用不同數(shù)量的實例時是如何伸縮的。最后,您將通過RabbitMQ模擬來自用戶微服務的用戶事件。
歡迎關注我的個人公眾號”My IO“
總結
以上是生活随笔為你收集整理的在ASP.NET Core微服务架构下使用数据库切分和扩展的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: WPF实现环(圆)形进度条
- 下一篇: .NET | 多线程下的调用上下文 :