前言
今天在Windows下压测openresty的时候,发现吞吐量很低,而且有异常,显然不对劲。
一看error
日志,一堆maximum number of descriptors supported by select() is 1024 while connecting to upstream
报错。
看报错大概能知道原因,应该是FD
被限制了,大概率是修改配置文件可以解决的,但是再仔细一查,发现windows不能通过修改配置文件调整这个限制,需要在编译时指定。
之后具体看了下openresty的官方文档,因为nginx在Windows下实现还是select模型
的IO多路复用,并没有使用Windows下新的I/O Completion Ports(IOCP)模型。select模型底层维护了一个FD_SIZE
大小的FD数组,而这个大小在64位系统一般都会限制到1024。这样做的原因,我觉得有2个原因,第一个是为了防止内存分配过大导致内存溢出;第二个是因为select模型
是对FD进行线性遍历的,过大会导致遍历速度变慢,性能反而下降。所以官方也不建议nginx在Windows环境下用于很高并发的场景。
这里尝试修改FD_SIZE
后重新进行编译,看看放松限制后的性能表现,同时也对比下Linux下的表现。
环境准备
使用官网推荐的MSYS2
环境编译,源码去官网下载。MSYS2
的安装,可以使用scoop install msys2
直接安装,不了解scoop
使用的看这篇文章scoop。
安装好之后,可以把它配置到Windows的终端里面,方便使用,配置命令行如下:
# 指定 -mingw64,编译64位
D:/Application/Scoop/apps/msys2/current/msys2_shell.cmd -mingw64 -defterm -no-start
进入msys2
环境,安装以下依赖:
pacman -S patch
pacman -S mingw-w64-x86_64-toolchain
pacman -S dos2unix
pacman -S zip
注意,使用的gcc
一定要是mingw
的,否则编译openssl
的时候会编译失败。
下载strawberryperl并解压好,之后打包的时候需要用到。
最后,把下载好的源码解压,然后在MSYS2
环境中进入源码目录。
编译打包
进入uitl
文件夹,可以看到 编译脚本build-win32.sh
和 打包脚本package-win32.sh
,但是不能直接使用,会有不少问题,下面贴出我修改后可以正常运行的脚本。
编译脚本package-win32.sh
:
#!/bin/bash
PCRE=pcre-8.45
ZLIB=zlib-1.2.13
OPENSSL=openssl-1.1.1t
JOBS=12
# 第一次运行的时候,可以把这个注释放开
# wget https://www.openssl.org/source/openssl-1.1.1t.tar.gz
# wget https://github.com/madler/zlib/releases/download/v1.2.13/zlib-1.2.13.tar.gz
# wget https://sourceforge.net/projects/pcre/files/pcre/8.45/pcre-8.45.tar.gz
rm -rf objs || exit 1
mkdir -p objs/lib || exit 1
cd objs/lib || exit 1
ls ../../..
tar -xf ../../$OPENSSL.tar.gz || exit 1
tar -xf ../../$ZLIB.tar.gz || exit 1
tar -xf ../../$PCRE.tar.gz || exit 1
cd ../..
cd objs/lib/$OPENSSL || exit 1
#patch -p1 < ../../../patches/openssl-1.1.0j-parallel_build_fix.patch || exit 1
patch -p1 < ../../../patches/openssl-1.1.1f-sess_set_get_cb_yield.patch || exit 1
#patch -p1 < ../../../patches/openssl-1.1.1d-win_fix.patch || exit 1
#patch -p1 < ../../../patches/openssl-1.1.1e-sess_set_get_cb_yield.patch || exit 1
cd ../../..
#--with-openssl-opt="no-asm" \
# 把默认的 DFD_SETSIZE=1024 修改了
./configure \
--with-cc=gcc \
--prefix= \
--with-cc-opt='-DFD_SETSIZE=32768' \
--sbin-path=nginx.exe \
--with-pcre-jit \
--without-http_rds_json_module \
--without-http_rds_csv_module \
--without-lua_rds_parser \
--with-ipv6 \
--with-stream \
--with-stream_ssl_module \
--with-stream_ssl_preread_module \
--with-http_v2_module \
--without-mail_pop3_module \
--without-mail_imap_module \
--without-mail_smtp_module \
--with-http_stub_status_module \
--with-http_realip_module \
--with-http_addition_module \
--with-http_auth_request_module \
--with-http_secure_link_module \
--with-http_random_index_module \
--with-http_gzip_static_module \
--with-http_sub_module \
--with-http_dav_module \
--with-http_flv_module \
--with-http_mp4_module \
--with-http_gunzip_module \
--with-select_module \
--with-luajit-xcflags="-DLUAJIT_NUMMODE=2 -DLUAJIT_ENABLE_LUA52COMPAT" \
--with-pcre=objs/lib/$PCRE \
--with-zlib=objs/lib/$ZLIB \
--with-openssl=objs/lib/$OPENSSL \
-j$JOBS || exit 1
make -j$JOBS || exit 1
exec make install
打包脚本package-win32.sh
:
#!/bin/bash
mingw32=/c/msys64/mingw32
info=`uname -a`
if [[ "$info" == MINGW64* ]]; then
arch="win64";
else
if [[ "$info" == MINGW32* ]]; then
arch="win32";
else
echo "Unknown architecture: $info" > /dev/stderr
exit 1
fi
fi
echo $arch
name=`pwd|perl -e '$d=<>;$d=~s{.*?/}{}g;$d=~s/$//g;print $d'`
name="$name-$arch"
echo $name
if [ -d $name ]; then
rm -rf $name
fi
mkdir $name || exit 1
cp -r resty restydoc restydoc-index nginx.exe luajit.exe lua51.dll lua include lualib html conf logs pod $name/ || exit 1
cp COPYRIGHT $name/ || exit 1
if [[ "$arch" == "win32" ]]; then
cp $mingw32/bin/libgcc_s_dw2-1.dll $name/ || exit 1
cp $mingw32/bin/libwinpthread-1.dll $name/ || exit 1
fi
cd $name || exit 1
# PATH=/f/Tools/strawberry-perl-5.36.1.1/perl/bin:$PATH cmd /c 'pl2bat.bat resty' || exit 1
# PATH=/f/Tools/strawberry-perl-5.36.1.1/perl/bin:$PATH cmd /c 'pl2bat.bat restydoc' || exit 1
# PATH=/f/Tools/strawberry-perl-5.36.1.1/perl/bin:$PATH cmd /c 'pl2bat.bat restydoc-index' || exit 1
# 上面的写法,在Windows终端下会出现问题,进入cmd后不能继续执行,所以修改为下面的方式
# strawberry配置为上面配置好的路径
/f/Tools/strawberry-perl-5.36.1.1/perl/bin/pl2bat.pl resty
/f/Tools/strawberry-perl-5.36.1.1/perl/bin/pl2bat.pl restydoc
/f/Tools/strawberry-perl-5.36.1.1/perl/bin/pl2bat.pl restydoc-index
cp ../README-windows.txt README.txt
unix2dos conf/* html/*.html resty || exit 1
cd .. || exit 1
zip -r $name.zip $name || exit 1
echo $name.zip
修改完脚本后,在根目录下执行编译脚本。
./util/build-win32.sh
编译成功之后,执行打包指令。
./util/package-win32.sh
执行完成后,已经打包在根目录了。
解压后,进入目录,执行nginx -V
,修改成功,解除原来1024限制。
测试
再次压测,接口内容是根据id去数据库查询用户信息后返回,1000并发,循环50次,openresty版本是当前最新的1.21.4.2
,这次windows下没有再出现maximum number of descriptors supported by select() is 1024 while connecting to upstream
错误了,压测结果如下:
下面是在Linux下的压测结果:
性能上确实受限于模型,比不上在Linux下使用epoll
模型。