抱歉,您的浏览器无法访问本站

本页面需要浏览器支持(启用)JavaScript


了解详情 >

前言

今天在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模型。

评论