让我们尝试构建一个可扩展的系统
我之前写过:
在本文中,我们将了解作为软件工程师构建可扩展系统可以采取的初步步骤。
让我们看看如何将负载测试时间从 187 秒减少到 31 秒
注意:我将使用Node.js,但不要跳过阅读,尝试吸收这个概念,特别是如果你是初学者。
任务如下:
构建一个只有一个请求的服务器,GET
返回 0 - N 之间的最大素数
我的设置
- 我使用纯 Node.js(不是
express.js
)创建我的服务器和路由,你可以自由使用express.js
- 您可以使用任何语言来运用这个想法,因此不要跳过阅读,但您可以跳过代码/代码库。
开始吧!
我把这作为招聘(经验丰富的)开发人员的一项任务。面试环节通常采用结对编程的方式,候选人可以自由使用互联网和自己选择的工具。考虑到我的日常工作,这样的任务安排确实很有帮助。
当你写了一个暴力破解方法
假设你用基本算法创建了服务器来查找素数。以下是一个暴力破解示例:
// just trying the first thought in mind
function isPrime(n) {
for(let i = 2; i <= Math.sqrt(n); i += 1) {
if (n % i === 0){
return false;
}
}
return true;
}
function calculateGreatestPrimeInRange(num) {
const primes = [];
for (let i = 2; i <= num; i += 1) {
if (this.isPrime(i)) primes.push(i);
}
return primes.length ? primes.pop() : -1;
}
你可以试着在你的路线上使用它,GET
就像这样https:localhost:9090/prime?num=20
,它会工作得很好,你会感觉很好。你用一些数字试了一下,?num=10, 55, 101, 1099
你会得到即时响应,感觉很棒 :)
坚持,稍等!
一旦你尝试一个大的数字,num=10101091
你就会感觉到滞后(我已经在浏览器中尝试过了,你可以使用Postman)
由于我们现在没有使用PM2(它可以做很多初学者不知道的事情),您会注意到,当您尝试打开一个新选项卡并尝试一个较小的数字时,您的选项卡将等待上一个选项卡的结果。
你现在可以做什么?
让我们引入并发!
- 集群模式来救援!
以下是集群模式实际运行的代码块。如果您不了解集群模块,请阅读相关内容。
const http = require('http');
const cluster = require('cluster');
const os = require('os');
const routes = require('./routes');
const cpuCount = os.cpus().length;
// check if the process is the master process
if (cluster.isMaster) {
// print the number of CPUs
console.log(`Total CPUs are: ${cpuCount}`);
for (let i = 0; i < cpuCount; i += 1) cluster.fork();
// when a new worker is started
cluster.on('online', worker => console.log(`Worker started with Worker Id: ${worker.id} having Process Id: ${worker.process.pid}`));
// when the worker exits
cluster.on('exit', worker => {
// log
console.log(`Worker with Worker Id: ${worker.id} having Process Id: ${worker.process.pid} went offline`);
// let's fork another worker
cluster.fork();
});
} else {
// when the process is not a master process, run the app status
const server = http.createServer(routes.handleRequests).listen(9090, () => console.log('App running at http://localhost:9090'));
}
瞧!
实施集群模块后,您将看到巨大的变化!
您可以注意到,使用线程后,编号较小的浏览器选项卡将快速获得响应,而另一个选项卡则忙于进行计算(您也可以在 Postman 中尝试一下)
对于那些不使用 Node.js 的人来说,集群模式意味着使用 CPU 中可用的线程以并发模式运行你的应用程序。
现在我们可以稍微放松一下了,但是我们还能做些什么来提高它的性能呢,因为我们的单个请求数量仍然很多,所以它仍然滞后?
算法来拯救你!
我知道这是一个令人难以忘怀的词,但它是一个你不能忽视的重要工具,最终,在实施一种新的算法之后,你就会意识到算法的价值。
对于素数,我们有一个埃拉托斯特尼筛法。
为了适应我们的用例,我们需要对其进行一些调整。完整的代码可以在 repo 中的类中找到Prime
。
让我们看一下负载测试结果
- 暴力破解方法
num=20234456
传递给loadtest 模块的命令:
loadtest -n 10 -c 10 --rps 200 "http://localhost:9090/prime?num=20234456"
结果:
INFO Total time: 187.492294273 s
INFO Requests per second: 0
INFO Mean latency: 97231.6 ms
INFO
INFO Percentage of the requests served within a certain time
INFO 50% 108942 ms
INFO 90% 187258 ms
INFO 95% 187258 ms
INFO 99% 187258 ms
INFO 100% 187258 ms (longest request)
- 使用经过修改的 SOE
num=20234456
传递给loadtest 模块的命令:
loadtest -n 10 -c 10 --rps 200 "http://localhost:9090/prime?num=20234456"
结果:
INFO Total time: 32.284605092999996 s
INFO Requests per second: 0
INFO Mean latency: 19377.3 ms
INFO
INFO Percentage of the requests served within a certain time
INFO 50% 22603 ms
INFO 90% 32035 ms
INFO 95% 32035 ms
INFO 99% 32035 ms
INFO 100% 32035 ms (longest request)
您可以比较上述两个结果,并可以看到 SOE 显然是赢家。
我们能进一步改进它吗?
是的,我们可以,我们可以添加一个缓存,即 Javascript 中的一个普通对象,可以用作HashMap。
使用缓存将存储给定数字 N 的结果,如果我们再次收到 N 的请求,我们可以简单地从存储中返回它,而不是进行计算。
REDIS 在这方面会做得更好
让我们看看结果
- 利用缓存进行暴力破解
num=20234456
INFO Target URL: http://localhost:9090/prime?num=20234456
INFO Max requests: 10
INFO Concurrency level: 10
INFO Agent: none
INFO Requests per second: 200
INFO
INFO Completed requests: 10
INFO Total errors: 0
INFO Total time: 47.291413455000004 s
INFO Requests per second: 0
INFO Mean latency: 28059.6 ms
INFO
INFO Percentage of the requests served within a certain time
INFO 50% 46656 ms
INFO 90% 46943 ms
INFO 95% 46943 ms
INFO 99% 46943 ms
INFO 100% 46943 ms (longest request)
- 使用带有修改和缓存的SOE
num=20234456
INFO Target URL: http://localhost:9090/prime-enhanced?num=20234456
INFO Max requests: 10
INFO Concurrency level: 10
INFO Agent: none
INFO Requests per second: 200
INFO
INFO Completed requests: 10
INFO Total errors: 0
INFO Total time: 31.047955697999996 s
INFO Requests per second: 0
INFO Mean latency: 19081.8 ms
INFO
INFO Percentage of the requests served within a certain time
INFO 50% 23192 ms
INFO 90% 32657 ms
INFO 95% 32657 ms
INFO 99% 32657 ms
INFO 100% 32657 ms (longest request)
时间分析
状况 | 时间 |
---|---|
使用基本算法 | 187.492294273 秒 |
带缓存 | 47.291413455000004 秒 |
有了 SOE | 32.284605092999996 秒 |
带有 SOE 和缓存 | 31.047955697999996 秒 |
最后
我希望您了解以下内容的好处:
- 多线程
- 算法
- 缓存又称为记忆化
希望你喜欢这篇短文,欢迎提出建议。代码仓库如下:find-highest-prime
您可以在Github、LinkedIn和Twitter上找到我
鏂囩珷鏉ユ簮锛�https://dev.to/ashokdey_/building-a-scalable-system-4l41