Nginx 中 map 模块的使用及性能测试
admin
2023-07-31 01:46:11
0

背景

最近我操刀了leetcode的论坛迁移,整个过程持续了几周的时间,总算暂时告了一个段落。常使用leetcode论坛的用户应该已经发现论坛已经大变样了吧~

期间遇到了不少坑坑洼洼,将来也还会有好多问题等待去一一解决。关于这个迁移过程中的收货,这篇文章中就不细说了,有时间再另开一篇博文。这篇文章主要关注在url-mapping以及它的性能问题。

:url-mapping的问题从何而来呢?

旧的论坛和新的论坛是两个不同的discuss框架。前者是phpbb,现在是nodebb。两者的 url routing 完全不一样,比如说同一个topic,在原来的url是 http://hostname/discuss//,在新的论坛中是 http://hostname/topic//(这里就不讨论两者甚至连topic_id都不一样的问题了)。

而在广袤的互联网海洋中,旧论坛的url可能到处都存在。我们不希望在论坛迁移后,用户点那些链接就失效了。我们希望的是用户访问旧的url可以被重定向到新论坛的某个地址。所以就产生了url-mapping的问题。

方法

生成url-mapping

感谢nodebb-plugin-import提供了数据迁移以后自动生成url-mapping的方式,省了我自己写脚本生成这些mapping的时间。每一条mapping大致是这样的:

~^/discuss/questions/oj/add-two-numbers\\b(\\?[^/]*)*/?$  /category/10/add-two-numbers;

其中的slugid的mapping是由插件生成的。regular expression是为了匹配url中如果有param添加的。

Nginx Map

官方文档的demo可能对于刚想上手的同学来说不是那么友好,还是直接看现成的配置学得快:

http {
  ...

  map_hash_max_size 204800;
  map_hash_bucket_size 204800;
  map $request_uri $new {
     include /path/of/your/map/file;
  }

  include /etc/nginx/conf.d/*.conf;
  include /etc/nginx/sites-enabled/*;

  ...
}
server {
  ...

  if ($new) {
    rewrite ^ https://discuss.leetcode.com$new redirect;
  }

  location / {
    ...
  }
  ...
}

在server规则匹配中,$new值不为空,说明当前要访问的url已经在http模块的mapping文件中匹配到了,这个时候就不走各种location模块了,直接rewrite成新的地址。:这里要是做成proxy_pass也行,后面的测试中就采用了proxy_pass。但线上的环境,担心nginx的压力太大了,就采用了rewrite方式给它减减压。

测试

考虑到mapping的条目有点多,几万量级,又都是正则匹配。每个请求来的时候都会先去看看mapping中有没有,即使mapping使用的是hash的方式也不免会让我对它的性能产生一些担忧,所以性能测试就必须要来一发了。

测试方案:

  1. 在新机器上跑helloworld

  2. 自动生成随机100个url-mapping,都重定向到helloworld

  3. 使用abtest分别对helloworld和随机url作压测

  4. 增大url-mapping的条目,重复1,2

压测机器

临时租了两台阿里云服务器(因为是临时的,所以我也就不在意在后文暴露ip了),配置都是:1核,2048M内存,40G硬盘。一台用作nginx和helloworld程序,一台专门做abtest。

:abtest也在阿里云执行只要是为了在一个数据中心降低网络延迟。最后发现效果真不错,rps从100多直接飙升到2700多。

helloworld

采用了nodejs的helloworld:

var http = require(\'http\');
var i = 0;
http.createServer(function (req, res) {
  console.log(i++);
  res.writeHead(200, {\'Content-Type\': \'text/plain\'});
  res.end(\'Hello World\\n\');
}).listen(1337, \"0.0.0.0\");
console.log(\'Server running at http://0.0.0.0:1337/\');

url-mapping

生成urlmapping写了一个python脚本:

import hashlib

m2 = hashlib.md5()
current = \"hello world\"
f = open(\'./url.map\', \'w\')

for i in range(100):
    m2.update(current)
    current = m2.hexdigest()
    f.write(\'~^/hello/world/\' + current + \'\\\\b(\\?[^/]*)*/?$\\t/;\\n\')

f.close()

nginx配置:

server {
  listen 80;
  server_name 120.26.138.197;

  location ^~ /{
    if ($new) {
      proxy_pass http://120.26.138.197:1337$new;
      break;
    }

    return 404;
  }
}

abtest

rps测试(request per second):并发压测使用100000次请求,并发100个用户的方式。

# 不走nginx
ab -n100000 -c100 120.26.138.197:1337/
# 走nginx
ab -n100000 -c100 120.26.138.197/hello/world/5eb63bbbe01eeed093cb22bb8f5acdc3/
mapping条目 直接访问(rps) map第一条url(rps) map最后一条url(rps) 不存在的url(rps)
100 2829.44 1819.63 1765.25 9740.53
1000 1816.00 1509.52 4094.68
10000 1813.22 514.24 658.32
100000 1836.02 62.40 65.80

跟预想的一样,mapping的条目确实会对请求效率产生影响。而且几万条的映射在较高并发的情况下已经到了勉强能用的临界了。还好以后mapping的条目不会再增加了,并且论坛的并发很难到100的量级。

tpr测试(time per request):因为考虑到服务器比较稳定,减少压测总数。同时把并发用户减为1个。

# 不走nginx
ab -n1000 -c1 120.26.138.197:1337/
# 走nginx
ab -n1000 -c1 120.26.138.197/hello/world/5eb63bbbe01eeed093cb22bb8f5acdc3/
mapping条目 直接访问(ms) map第一条url(ms) map最后一条url(ms) 不存在的url(ms)
100 0.690 0.922 0.933 0.507
1000 0.925 1.043 0.648
10000 0.921 2.340 1.915
100000 0.926 16.321 15.469

在并发不是很高的时候mapping的条目可以更多。100000个条目大概只会影响整个请求15ms左右,可以忽略不计。如果说150ms的延迟是可以接受的,那么在一个并发不是很高的情况下,mapping最多可以有100w条,还是很多的。

测试中的不足

  1. 压测的url请求并不随机

  2. 所有的url都被重定向到一个地方。不过从结果来看,nginx确实是根据条目一个个请求的。这点倒没有什么影响

  3. 没有测试http://hostname/path?param=xxx这样类型的url

上一篇:VS Code 配置

下一篇:Python科学计算之NumPy

相关内容

热门资讯

Mobi、epub格式电子书如... 在wps里全局设置里有一个文件关联,打开,勾选电子书文件选项就可以了。
定时清理删除C:\Progra... C:\Program Files (x86)下面很多scoped_dir开头的文件夹 写个批处理 定...
scoped_dir32_70... 一台虚拟机C盘总是莫名奇妙的空间用完,导致很多软件没法再运行。经过仔细检查发现是C:\Program...
500 行 Python 代码... 语法分析器描述了一个句子的语法结构,用来帮助其他的应用进行推理。自然语言引入了很多意外的歧义,以我们...
小程序支付时提示:appid和... [Q]小程序支付时提示:appid和mch_id不匹配 [A]小程序和微信支付没有进行关联,访问“小...
pycparser 是一个用... `pycparser` 是一个用 Python 编写的 C 语言解析器。它可以用来解析 C 代码并构...
微信小程序使用slider实现... 众所周知哈,微信小程序里面的音频播放是没有进度条的,但最近有个项目呢,客户要求音频要有进度条控制,所...
65536是2的几次方 计算2... 65536是2的16次方:65536=2⁶ 65536是256的2次方:65536=256 6553...
Apache Doris 2.... 亲爱的社区小伙伴们,我们很高兴地向大家宣布,Apache Doris 2.0.0 版本已于...
项目管理和工程管理的区别 项目管理 项目管理,顾名思义就是专注于开发和完成项目的管理,以实现目标并满足成功标准和项目要求。 工...