在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:

{"title": "MyTitle","content": "MyContent","userId": ${__Random(1,100)},"categoryId": "Category${__Random(1,10)}"
}

它为随机用户(ID 1–100)和类别(1–10)创建一个帖子。

向请求添加内容类型application/json HTTP头: 

随机阅读10篇文章:

  • Servername: localhost

  • Port: 5001

  • HTTP-Request: GET

  • Path: /api/Posts

  • Send Parameters with the Request:

NAME | VALUE | CONTENT-TYPE
category | Category${__Random(1,10)} | text/plain
count | 10 | text/plain

运行测试

在测试运行时查看摘要报告: 

等待一段时间,直到平均值(响应时间)和吞吐量稳定。

修改测试参数

停止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:

{"properties":{},"routing_key":"user.update","payload":"{id:1,name:\"NewName${__counter(TRUE,)}\",version:${__counter(TRUE,)}}","payload_encoding":"string"
}

用户实体有一个version字段来处理无序消息。为了保持测试的简单性,它只更新单个用户并增加version字段的值。性能影响应该保持不变。

将Content-Type和对RabbitMQ的授权添加到HTTP头。 

Content-Type | application/json
Authorization | Basic dGVzdDp0ZXN0

*“dGVzdDp0ZXN0”是base64编码的用户和密码“test”。

运行测试

吞吐量应该与前面的测试类似。

您还可以通过查看控制台输出来识别docker中的活动的RabbitMQ消费者。您可以停止容器,则另一个实例将接管消息处理。

5.最后的想法和展望

您创建了一个微服务架构并实现了应用层数据库分片。然后用多个容器实例扩展应用程序并对其进行负载测试。您还用RabbitMQ处理了用户更改事件。

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

下一步是什么?要获取帖子最多的前10个类别的数据,需要查询多个数据库实例。这可能会导致延迟和性能下降。Redis可以是查询聚合数据的解决方案。我将在下一篇文章中展示它。

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/301170.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

mysql5.5 mysqli_php5.5.38增加mysqli扩展

php5.5.38增加mysqli扩展发布时间&#xff1a;2020-08-28 03:43:17来源&#xff1a;51CTO阅读&#xff1a;1148作者&#xff1a;xingyun2010编译的时候正常&#xff1a;./configure --prefix/usr/local/mysqli --with-php-config/usr/local/php/bin/php-config --with-mysqli/u…

每日一笑 | 周杰伦到底什么时候才发新专辑?

全世界只有3.14 % 的人关注了数据与算法之美&#xff08;图源网络&#xff0c;侵权删&#xff09;

GARFIELD@10-18-2004

子非猫转载于:https://www.cnblogs.com/rexhost/archive/2004/10/18/53799.html

【荐】牛逼的WPF动画库:XamlFlair

【荐】牛逼的WPF动画库&#xff1a;XamlFlair原文链接&#xff1a;https://github.com/XamlFlair/XamlFlair翻译&#xff1a;沙漠尽头的狼(本文未全文翻译&#xff0c;建议阅读原文了解更多)XamlFlairXamlFlair库的目标是简化常见动画的实现&#xff0c;并允许开发人员使用几行…

Python编程系列教程第13讲——隐藏数据和封装

视频地址&#xff1a;http://www.56.com/u88/v_OTM5NjU0MjE.html#fromoutpvidOTM5NjU0MjE 普及网络安全知识&#xff0c;推动信息技术发展。 为祖国的网络安全撑起一片蓝天&#xff0c;为网络安全爱好者构建一方家园。 欢迎来到灰帽程序员论坛&#xff0c;我们的网址是&#xf…

HijackThis日志细解【简明教程增强版】(五)

&#xff08;九&#xff09;组别——O51. 项目说明O5项与控制面板中被屏蔽的一些IE选项相关&#xff0c;一些恶意程序会隐藏控制面板中关于IE的一些选项&#xff0c;这可以通过在control.ini文件中添加相关命令实现。2. 举例O5 - control.ini: inetcpl.cplno 这里隐藏了控制面板…

java字符串如何输出_java字符串如何输出

在Java编程中&#xff0c;我们常常用 System.out.println(); 来输出字符串。System.out.println();System是一个类&#xff0c;继承自根类Objectout是类PrintStream类实例化的一个对象&#xff0c;且是System类的静态成员变量println()是类PrintStream的成员方法&#xff0c;被…

简单易懂的自动驾驶科普知识

全世界只有3.14 % 的人关注了数据与算法之美有不少人问我人工智能和自动驾驶的技术问题&#xff0c;我作为一个主业是后端开发的老码农可是回答不了啊&#xff01;今天转载一篇自动驾驶大拿写的文章&#xff0c;学习一下。先来一张各大车企自动驾驶技术的分级图&#xff0c;大致…

图像处理工具包ImagXpress中如何设置上下文菜单

ImagXpress 是世界上最先进的彩色映像和照片图像处理工具包&#xff0c;有着.NET、COM、VC三种组件形式。ImagXpress可以为开发者构建的应用程序提供图像浏览、编辑、打印、TWAIN扫描、文件格式转换等优秀的功能。本文先来看看它的一些基础设置&#xff0c;如何设置上下文菜单。…

Educational Codeforces Round 157 (Rated for Div. 2)

Educational Codeforces Round 157 (Rated for Div. 2) A 模拟 #include <bits/stdc.h>using namespace std;const int N 3e5 10;void solve() {int x , y , k;cin >> x >> y >> k;if(y < x){cout << x << endl;}else if(x k >…

WPF TextBox限制只能输入数字的两种方法

文本框中只能输入数字&#xff0c;一个常见的功能喽&#xff0c;今天就来看看如何实现它~下面就看看代码思路都写在xaml里面了&#xff0c;MainWindow.xaml:<Window x:Class"wpfcore.MainWindow"xmlns"http://schemas.microsoft.com/winfx/2006/xaml/present…

r.java没有生成_R.java 常见问题(R.java文件没有生成 )

1、选择菜单 Project >> Clean &#xff0c;前提是勾选上 Bulid Automatically(自动构建部署) &#xff0c; 点Clean后会重新构建项目&#xff0c;因为一般情况下&#xff0c;R.java文件在这个时候会重新更新生成一边&#xff0c;如果工程有错&#xff0c;就不会自动生成…

SmartPart事件

姑且把这样的一件事情叫做事件&#xff01; SmartPart是一个非常好的Sharepoint用户控件包装器&#xff08;kaneboy开发的包装控件也非常好用&#xff09;&#xff0c;我们知道&#xff0c;在WSS2.0/SPS2003平台上只有两种方法创建你自己的WebPart&#xff1a; 1、使用MS官方提…

国家特级数学教授李毓佩:我们欠孩子真正的数学阅读 !

▲数据汪特别推荐点击上图进入玩酷屋说到数学&#xff0c;我想起了13年一场轰动行业各界的“数学无用论”&#xff01;那时微博上有个话题叫做#让数学滚出高考#&#xff0c;超过7成网友都表示支持&#xff0c;这可怕的比例就能说明在中国由于数学差导致命运被洗牌的真不在少数……

NET问答:null != variable 和 variable != null 到底有什么区别?

咨询区 mr_georg&#xff1a;在 C# 中&#xff0c;下面的两种写法在执行效率上是否存在差异&#xff1f;if (null ! variable) ... if (variable ! null) ...因为最近我经常看到这种非自然的写法 null ! variable&#xff0c;这让我感到很好奇&#xff0c;因为我觉得第二种写法…

分清词类

要学好英语&#xff0c;分清词类至关重要&#xff0c;每学一个词&#xff0c;要知道他的词类&#xff0c;是名词还是动词&#xff0c;这至关重要。每个词类都有其自己的特点&#xff0c;只有知道这个词&#xff0c;属于哪个词类&#xff0c;才能正确的使用它。同时还要弄清各个…

centos 6 x64 mysql_CentOS 6.x版本升级Mysql

首先确定一下自己的DNS vi /etc/resolv.conf 我一般为114.114.114.114#-----------------------------CentOS 6.x版本升级Mysql ------------------#! /bin/sh#1.关闭selinuxcp -rp /etc/selinux/config /etc/selinux/config.baksetenforce 0sed -i 7s/enforcing/disabled/…

关于Hibernate 3

Hibernate 3作了一些改进&#xff0c;改进了一些原来很显而易见的缺点。例如加了抽象语法树&#xff0c;但是在Hibernate 3.0 Beta1中&#xff0c;感觉还是有些不大成熟。从代码可以看出&#xff0c;Hibernate 3.0 Beta1的HQL AST使用了antlr&#xff0c;我向来不大喜欢这种使用…

程序员都想,却不敢做的事?我来!

一个 “实用” 的好命令&#xff0c;我不得试试&#xff1f;大家好&#xff0c;我是鱼皮。在编程届&#xff0c;有一个家喻户晓的实用 Linux 命令&#xff1a;rm -rf / 。据说&#xff0c;此命令一旦执行成功&#xff0c;就会给人带来快乐&#xff0c;是一个善良的好命令&#…

也来谈谈这致命的手机充电器

这两天有很多关于因使用iPhone在充电时打电话被电死的讨论&#xff0c;因此这里也来谈谈几点。 手机充电器的工作原理 刚好前段时间拆了两个充电器&#xff0c;看下里面的电路就明白了。鉴于网络上不明真相出来误导人的特别多&#xff0c;很多网站竟然还有文章说手机充电器里没…