在ASP.NET Core微服务架构下使用数据库切分和扩展, 并用JMeter进行负载测试
原文鏈接:https://itnext.io/how-to-scale-an-asp-net-core-microservice-and-sharded-database-load-test-with-jmeter-1a8c7292e7e3
現在,您將擴展應用程序并運行多個微服務和數據庫的容器實例。您將使用Docker Compose和HAProxy負載均衡器:?
然后運行JMeter負載測試,以查看應用程序在使用不同數量的實例時如何伸縮。最后,您還將發布和接收來自RabbitMQ的消息。
1.運行多個數據庫和微服務實例
將微服務容器化
使用上一篇文章中的代碼和環境作為基礎。
將Visual Studio解決方案資源管理器中的文件“Dockerfile”重命名為“dockerfile”(第一個字符小寫)。然后右鍵單擊Docker文件并選擇“創建Docker鏡像”。這也會將鏡像推送到docker。
在Docker中運行應用程序
創建docker-compose.yml文件:
version:?'2' services:webservice:image:?'postservice:latest'???cpus:?0.5scale:?4environment:-?PostDbConnectionStrings__Shard0=server=database0;?port=3306;?database=post;?user=root;?password=pw;?Persist?Security?Info=False;?Connect?Timeout=300??????-?PostDbConnectionStrings__Shard1=server=database1;?port=3306;?database=post;?user=root;?password=pw;?Persist?Security?Info=False;?Connect?Timeout=300???#??-?PostDbConnectionStrings__Shard2=server=database2;?port=3306;?database=post;?user=root;?password=pw;?Persist?Security?Info=False;?Connect?Timeout=300???loadbalancer:image:?dockercloud/haproxylinks:-?webservicevolumes:-?/var/run/docker.sock:/var/run/docker.sockports:-?5001:80database0:image:?'mysql:5.6'cpus:?0.5ports:-?3312:3306environment:-?MYSQL_ROOT_PASSWORD=pwdatabase1:image:?'mysql:5.6'cpus:?0.5ports:-?3313:3306environment:-?MYSQL_ROOT_PASSWORD=pwdatabase2:image:?'mysql:5.6'cpus:?0.5ports:-?3314:3306environment:-?MYSQL_ROOT_PASSWORD=pw????docker文件配置3個數據庫容器和4個Post服務實例。目前,Post服務實例只使用了2個數據庫。稍后可以刪除注釋以使用第三個數據庫。HAProxy負載均衡器公開端口5001上的Post服務容器。
每個容器有0.5cpu的限制,以幫助在本地機器上進行實際的負載測試。在我的12核筆記本上,仍然有未使用的資源,因此添加更多的服務和數據庫實例可以帶來好處。
啟動應用程序
C:\dev>docker-compose?up?-d初始化數據庫
打開瀏覽器訪問http://localhost:5001/swagger/index.html
初始化至少有100個用戶和10個類別的數據庫。
您可以創建更多的用戶和類別,但由于CPU的限制,這需要一些時間。
2.縮放應用程序并使用JMeter進行負載測試
創建JMeter測試計劃
安裝并打開JMeter。
創建測試計劃和線程組:?
32個線程是一個很好的開始。在線程的每個循環上,它添加一個帖子并讀取10個帖子。
添加HTTP請求以創建帖子:?
Servername: localhost
Port: 5001
HTTP-Request: POST
Path: /api/Posts
Body Data:
它為隨機用戶(ID 1–100)和類別(1–10)創建一個帖子。
向請求添加內容類型application/json HTTP頭:?
隨機閱讀10篇文章:
Servername: localhost
Port: 5001
HTTP-Request: GET
Path: /api/Posts
Send Parameters with the Request:
運行測試
在測試運行時查看摘要報告:?
等待一段時間,直到平均值(響應時間)和吞吐量穩定。
修改測試參數
停止JMeter中的測試。
您可以更改測試計劃中的線程。將它們提升到64或128個線程?;蛘邔⒕€程數減少到16甚至1。
在編輯docker-compose.yml之前關閉應用程序:
C:\dev>docker-compose?down您可以通過“scale”屬性更改Post服務實例的數量。更改數據庫數的“environment”屬性(添加/刪除注釋):
更改后啟動應用程序:
C:\dev>docker-compose?up?-d數據庫服務器運行需要一段時間。并記住初始化數據庫:?
3.試驗結果示例
在我的電腦上,兩個Post服務與一個數據庫的比率會產生很好的效果。我可以將它擴展到六個服務和三個數據庫,直到我的硬件達到極限。平均時間保持在500ms以下。增加高于64的線程會產生錯誤。
結果取決于我的環境和CPU限制。它們在你的機器上是不同的。
每秒吞吐量與實例數成比例:?
在CPU受限的容器中,每秒305個請求,每天大約有2500萬個請求。這將允許100萬用戶每天寫10篇帖子和閱讀帖子。當然,現實生活中的應用程序會更復雜,但我希望示例能夠顯示基本的思想。
4.微服務間通信和復制用戶更改
Post服務通過Rabbitmq消息從User微服務接收對用戶的更改:?
你也可以用JMeter來模擬。
創建RabbitMQ容器
發出以下命令(在控制臺窗口中)以啟動具有管理UI的RabbitMQ容器
C:\dev>docker?run?-d??-p?15672:15672?-p?5672:5672?-e?RABBITMQ_DEFAULT_USER=test?-e?RABBITMQ_DEFAULT_PASS=test?--hostname?my-rabbit?--name?some-rabbit?rabbitmq:3-management該命令將“test”配置為用戶和密碼,而不是默認的Guest賬戶。Guest僅限于localhost,但在docker中,容器位于不同的主機上。
修改后微服務
在Visual Studio中安裝RabbitMQ.Client NuGet包。
添加IntegrationEventListenerService類:
using?Microsoft.Extensions.Hosting; using?Newtonsoft.Json.Linq; using?PostService.Data; using?PostService.Entities; using?RabbitMQ.Client; using?RabbitMQ.Client.Events; using?System; using?System.Collections.Generic; using?System.Text; using?System.Threading; using?System.Threading.Tasks;namespace?PostService {public?class?IntegrationEventListenerService?:?BackgroundService{private?async?Task?ListenForIntegrationEvents(CancellationToken?stoppingToken){try{ConnectionFactory?factory?=?new?ConnectionFactory{UserName?=?"test",Password?=?"test"};var?endpoints?=?new?System.Collections.Generic.List<AmqpTcpEndpoint>?{new?AmqpTcpEndpoint("host.docker.internal"),new?AmqpTcpEndpoint("localhost")};var?connection?=?factory.CreateConnection(endpoints);var?channel?=?connection.CreateModel();var?consumer?=?new?EventingBasicConsumer(channel);var?arguments?=?new?Dictionary<String,?object>{{?"x-single-active-consumer",?true?}};channel.QueueDeclare("user.postservicesingleactiveconsumer",?false,?false,?false,?arguments);channel.ExchangeDeclare("userloadtest",?"fanout");channel.QueueBind("user.postservicesingleactiveconsumer",?"userloadtest",?"");consumer.Received?+=?(model,?ea)?=>{var?body?=?ea.Body.ToArray();var?message?=?Encoding.UTF8.GetString(body);Console.WriteLine("IntegrationEvent?{0}",?message);var?data?=?JObject.Parse(message);var?type?=?ea.RoutingKey;var?user?=?new?User(){ID?=?data["id"].Value<int>(),Name?=?data["name"].Value<string>(),Version?=?data["version"].Value<int>()};if?(type?==?"user.add"){_dataAccess.AddUser(user);}else?if?(type?==?"user.update"){_dataAccess.UpdateUser(user);}channel.BasicAck(ea.DeliveryTag,?false);};channel.BasicConsume(queue:?"user.postservicesingleactiveconsumer",autoAck:?false,consumer:?consumer);try{await?Task.Delay(Timeout.Infinite,?stoppingToken);}catch?(OperationCanceledException){Console.WriteLine("Shutting?down.");}}catch?(Exception?e){Console.WriteLine(e.ToString());}}private?readonly?DataAccess?_dataAccess;public?IntegrationEventListenerService(DataAccess?dataAccess){_dataAccess?=?dataAccess;}protected?override?async?Task?ExecuteAsync(CancellationToken?stoppingToken){while?(!stoppingToken.IsCancellationRequested){await?ListenForIntegrationEvents(stoppingToken);}}} }后臺服務使用“test”帳戶訪問RabbitMQ和host.docker.internal以及localhost作為主機。這允許從容器和Visual Studio調試器內部進行連接。
單個活動的消費者保證只有一個Post-service實例接收消息。
如果活動的實例崩潰,那么下一個實例將接管。稍后可以通過停止docker中當前接收的實例來嘗試。
如果exchange和管道尚不存在,則代碼將創建它們。它使用手動確認。
修改Startup.cs以運行IntegrationEventListenerService:
public?void?ConfigureServices(IServiceCollection?services){services.AddControllers();services.AddSwaggerGen(c?=>{c.SwaggerDoc("v1",?new?OpenApiInfo?{?Title?=?"PostService",?Version?=?"v1"?});});services.AddSingleton<DataAccess>();services.AddSingleton<IntegrationEventListenerService>();services.AddHostedService<IntegrationEventListenerService>(provider?=>?provider.GetService<IntegrationEventListenerService>());}在Docker中運行已更改的微服務
關閉docker中的應用程序:
C:\dev>docker-compose?down編譯Post服務,將其容器化并發布到docker。
更改后啟動應用程序:
C:\dev>docker-compose?up?-d數據庫服務器運行需要一段時間。并記住初始化數據庫:?
修改JMeter測試
在JMeter測試計劃中,添加一個只有一個線程的線程組:?
添加計時器:
恒定計時器將測試限制為每秒一條消息。每秒一條消息非常常見。即使半年內有100萬用戶注冊,每分鐘也只有4個新用戶。
添加HTTP請求以將消息發布到RabbitMQ:?
Servername: localhost
Port: 15672
HTTP-Request: POST
Path: /api/exchanges/%2F/userloadtest/publish
Body Data:
用戶實體有一個version字段來處理無序消息。為了保持測試的簡單性,它只更新單個用戶并增加version字段的值。性能影響應該保持不變。
將Content-Type和對RabbitMQ的授權添加到HTTP頭。?
Content-Type?|?application/json Authorization?|?Basic?dGVzdDp0ZXN0*“dGVzdDp0ZXN0”是base64編碼的用戶和密碼“test”。
運行測試
吞吐量應該與前面的測試類似。
您還可以通過查看控制臺輸出來識別docker中的活動的RabbitMQ消費者。您可以停止容器,則另一個實例將接管消息處理。
5.最后的想法和展望
您創建了一個微服務架構并實現了應用層數據庫分片。然后用多個容器實例擴展應用程序并對其進行負載測試。您還用RabbitMQ處理了用戶更改事件。
這只是一個示例應用程序。您必須調整代碼才能在生產環境中使用它。
下一步是什么?要獲取帖子最多的前10個類別的數據,需要查詢多個數據庫實例。這可能會導致延遲和性能下降。Redis可以是查詢聚合數據的解決方案。我將在下一篇文章中展示它。
歡迎關注我的個人公眾號”My IO“
總結
以上是生活随笔為你收集整理的在ASP.NET Core微服务架构下使用数据库切分和扩展, 并用JMeter进行负载测试的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 使用IQueryable扩展方法实现复杂
- 下一篇: 【荐】牛逼的WPF动画库:XamlFla