LNMP生产服务器技术改进
2021-06-21曾棕根
曾 棕 根
(宁波职业技术学院电子信息工程学院 浙江 宁波 315800)
0 引 言
LNMP架构免费开源、功能强大,是当前最流行的Web服务器架构,与大数据和人工智能服务器存在密切的关系。由于LNMP架构技术牵涉面广、安装与配置非常复杂且可借鉴的高端经验少,按照默认配置无法发挥该架构稳定、安全和高效的特征。LNMP服务器出现安全漏洞、在访问高峰期网页卡死甚至数据库崩溃成为常态,因此对LNMP生产服务器进行技术改进成为当前亟待解决的问题。
历经七年对LNMP生产服务器的管理与深入研究,解决了LNMP架构安装、运行中遇到的各种问题,对该架构进行了全方位的技术改进,使得该架构安全、快速、抗拥塞。本文系统地论述了该架构在上述三方面所做的深度技术改进。
1 LNMP架构的组成
LNMP架构是指由Linux内核的操作系统、Nginx服务器、MariaDB数据库服务器和PHP脚本服务器组成的PHP动态网站运行架构,组成该架构的四个软件都是免费开源的[1]。生产环境下,推荐使用的Linux操作系统是CentOS-7 (1908),其稳定、快速、安全;Nginx是一款高性能低开销的Web服务器,目前最新的稳定版本是1.16.2[2];MariaDB是MySQL数据库的一个分支,由开源社区维护,代码更新速度最为快速,目前最新稳定版本为10.3.11; PHP网页服务器则是用来编译和运行PHP脚本,目前最新版本为7.2.24。图1为LNMP架构数据流动示意图。
图1 LNMP架构数据流动示意图
用户总是在浏览器中发出PHP网页查看请求,Nginx收到用户的PHP程序调用请求后,将此请求发送给PHP-FPM服务器,PHP-FPM服务器则调用PHP进程去编译和运行PHP程序,PHP程序则通过SQL指令从MariaDB数据库中去存取所需信息,MariaDB将SQL指令运行后以二维表的形式将结果返回给PHP进程,PHP进程则将记录以HTML语法进行包装,PHP-FPM服务器会将此HTML文本传送给Nginx服务器,Nginx服务器则将此HTML文本返回给客户端浏览器,客户端浏览器则解释此HTML文本中的HTML标签,用户即可从浏览器中看到此PHP程序的运行结果。
由图1可见,数据的流动是以单一、固定的路径传输的,数据总是从客户端浏览器中发出,经过Nginx、PHP-FPM、MariaDB三项服务,最后按原路返回,构成一个闭环。因此,要提高LNMP架构的安全性和性能,就必须对LNMP架构每一项服务进行技术改进。
2 安全性改进
确保在互联网环境下的访问安全是服务器建设中最重要的问题。LNMP架构服务器安全措施包括磁盘规划、以编译方式安装最新源代码、开放有限端口、限定登录账户、规划文件读取权限和使用HTTPS访问协议等。
2.1 磁盘规划
服务器上应该使用多个物理磁盘,构建某种磁盘阵列,某个磁盘损坏时可以直接用新磁盘替换,从而不影响服务器的运行。例如RAID 5,服务器上有3个以上硬盘即可做此模式,一份数据分成3份写,如果一个硬盘坏了,其他数据不会受损失,抽出坏的硬盘再插入新的硬盘即可[3]。
在安装CentOS操作系统时,应该将系统所需的不同的目录分别安装在不同的物理磁盘分区上,以避免某一个目录出现错误时殃及其他目录或整个磁盘。表1列出了不同分区和所需的磁盘空间,其中/boot引导分区用来存储Linux内核,/根分区用来存储CentOS操作系统所需的其他文件,而服务器中所安装的其他软件如Nginx、MariaDB、PHP及其需要保存的数据则放在/opt分区内。还可以进一步细化,将/根分区中的用户目录和临时目录等放于不同的分区中。这样确保了运行中出现的问题限定在某一个分区内,能最大限度地减少损失。
如果LNMP服务器配有专用的大容量磁盘阵列服务器时,可以使用网线将二者的网卡直连,两块直连网卡使用192.168.0.x地址组建成一个局域网,在LNMP服务器中采用mount命令将磁盘阵列服务器挂载到CentOS操作系统的一个目录中,如mount 192.168.0.100:/vol1/mnt/array,最后将CentOS操作系统中LNMP架构的用户数据或者备份数据直接写在/mnt/array即磁盘阵列设备上,这样就确保了LNMP架构在物理层面上的安全性。
2.2 编译安装最新源码
LNMP架构中Nginx、MariaDB和PHP都是开源软件,官方一直在做功能、性能或安全性上的改进,因此不宜采用LNMP一键安装包或RPM安装包,而应去官网下载最新的适用于Linux操作系统的源代码包,直接在CentOS操作系统上现场配置、编译和安装。这样可以确保得到最新的稳定版本,而且可以生成最适合服务器CPU运行的二进制代码。同时,管理员也清楚每个软件都安装到哪个文件夹中,便于日后的升级和维护。
CentOS操作系统安装好后,应该立即使用yum-y update命令对操作系统进行一次全面更新,而且要定期使用这个命令更新系统。系统更新后,采用rpm-q kernel查看是否升级了内核,若升级,则需重启操作系统,重启后会自动运行最新内核。这时,使用uname-a查看正在运行的内核,并用rpm-e将旧版本的内核删除,否则分配给内核的200 MB磁盘空间会不够用,下次将无法升级新版内核。
还要立即升级CentOS操作系统中的gcc、cmake和libzip,这三个软件都是编译其他软件(如Nginx、MariaDB和PHP)所必须的。
通过g++ --version命令,可以看到CentOS中g++的版本为g++ (GCC) 4.8.5 20150623 (Red Hat 4.8.5-39),由于该版本太老,因此须到官网(https://gcc.gnu.org/)上下载gcc-9.2.0.tar.gz源代码编译安装。安装完成后,必须要把/usr/lib64/中的libstdc++.so.6链接到新版本的/usr/local/lib64/libstdc++.so.6.0.27文件,否则g++编译器将无法使用gcc-9.2.0。
从cmake官方网站(https://cmake.org/download/)上下载最新的cmake-3.15.1.tar.gz源代码包,编译安装完成后,调用cmake-version时,会出现“段错误 (core dumped)”而无法使用,执行hash -r命令可以解决此错误。
libzip是编译PHP所必须的支持包,CentOS默认没有安装libzip,要去官网(https://libzip.org/)下载最新版本的libzip-1.5.2.tar.gz源代码包,编译安装后,运行libzip时会出现找不到 zipconf.h的错误,还需复制/usr/local/lib/libzip/include/zipconf.h到/usr/local/include/zipconf.h位置。
Nginx、MariaDB和PHP都需要从官网上下载最新源代码,然后在本机上现场编译安装,Nginx安装在/opt中,而MariaDB和PHP安装在/usr/local/文件夹中。要定期关注这三个软件的更新情况,并要随官方的修订及时更新它们,以确保最近发现的漏洞能被及时修复。
特别地,以前PHP使用Oracle官方发布的MySQL驱动libmysql与MariaDB服务进行通信,为了避免版权问题和程序的非可控性,PHP已自行开发了mysqlnd本地驱动替代libmysql,所以不再需要先安装MariaDB再安装PHP了,传统的安装PHP的方式中,在编译PHP时,需要指定以下几项:
--with-mysql=/usr/local/mysql
--with-mysqli=/usr/local/mysql/bin/mysql_config
--with-pdo-mysql=/usr/local/mysql
现在使用mysqlnd驱动,编译PHP时,上述本条配置应该改为:
--with-mysql=mysqlnd
--with-mysqli=mysqlnd
--with-pdo-mysql=mysqlnd
安装完成后,如果在phpinfo输出的mysqli项中发现Client API library version为mysqlnd 5.0.12-dev-20150407,说明mysqlnd驱动mysqlnd 5.0.12-dev-20150407已经安装成功,PHP脚本可以使用pdo_mysql和mysqli这两种API Extensions通过mysqlnd驱动与MariaDB数据库服务器通信。
CentOS操作系统中各种软件都采用OpenSSL函数库进行加密,但OpenSSL在2014年4月8日曝光了一个名为heartbleed的漏洞。利用该漏洞,约30%以https开头网址的用户登录账号密码通过网络被窃取[4]。所以必须从OpenSSL官网(https://www.openssl.org)下载,将OpenSSL 1.0.2k-fips 26 Jan 2017升级为OpenSSL 1.1.1d 10 Sep 2019。同时,使用Open-SSL加密函数库的Nginx、PHP和OpenSSH都必须采用新安装的OpenSSL加密函数库重新编译安装,以确保网站访问安全和服务器SSH远程访问安全。
2.3 服务器访问限制
LNMP服务器编译安装好后,为确保服务器在互联网上的访问安全,必须在开放有限端口、限定登录账户、规划文件读取权限等方面作出限制。
通常情况下,服务器只开放80号Web服务端口、22号SSH远程访问端口和443号https访问端口,同时关闭不需要的服务,这样可以减少暴露在互联网中的漏洞。同时,删除firewalld.service防火墙,安装iptables-services防火墙,这样易于使用。
同时,在CentOS操作系统中,要禁止在ssh中使用root直接登录,要求用户先用普通账号登录CentOS操作系统,再通过su命令输入root账号密码才可以登录root账号,这样可以防止针对root账号的远程暴力破解。因此,首先要在CentOS中创建一个普通账号,再在/etc/ssh/sshd_config中设置PermitRootLogin no即可。
在PHP-FPM服务器的配置文件php-fpm.conf中,设置user=www和group=www,使得只有www组的www用户才可能访问PHP-FPM服务。
MariaDB数据库服务必须指定允许来自某台客户机的某个账号来连接自己。在MySQL数据库的user表中,只留下Host为localhost和127.0.0.1和User为root的这两条记录,以此只允许来自当前服务器的root账号连接MariaDB数据库服务,从而确保了MariaDB数据库服务的安全。
对CentOS操作系统中每个文件和文件夹,都可以通过chgrp命令设置其隶属于哪个组,通过chown命令来设置它隶属于哪个用户,并通过chmod命令来设置所有者、所属组其他用户和其他组对它的操作权限。操作权限用4、2、1数字来代表,4表示读取权限,2表示写入权限,1表示执行权限[5]。如775这三个数字代表拥有者、组用户、其他用户的权限分别为7=4+2+1、7=4+2+1、5=4+1,第一个数字7表示所有者具有读取、写入和执行权限,第二个数字7表示与所有者同组的用户具有读取、写入和执行权限,第三个数字5表示其他用户具有读取和执行权限。对访问用户、访问组和其他组的操作权限限定,使得CentOS操作系统中的文件访问得到有效地保护。
2.4 使用HTTPS访问协议
Nginx默认采用HTTP协议与客户端浏览器通信,该协议采用明文方式传输网页和用户账号密码,容易受到网络中间人的窃密,所以极不安全。Nginx服务器必须采用HTTPS安全传输协议与用户端浏览器通信。用户在首次访问Nginx服务器时,Nginx服务器会将使用私钥加密的包含对应公钥的数字证书CA发送到客户端浏览器中,以后用户发送访问请求给Nginx服务器时,先用浏览器中的数字证书中的公钥对信息进行加密,再发送给Nginx服务器;Nginx服务器在接收到用户的访问信息后,用服务器上的对应的私钥解开信息;在完成用户提交的请求后,将相应网页用服务器中对应的私钥加密后,再发送给客户端浏览器;客户端浏览器接收到信息后,会用浏览器中该Nginx服务器对应的数字证书中的公钥解密,完成双向加密通信。
将私钥和包含对应公钥的数字证书拷贝到Nginx服务器上后,只需在Nginx的配置文件中编写如下配置即可:
server {
listen 443 ssl;
server_name localhost;
ssl_certificate /etc/pki/tls/certs/server.cer;
ssl_certificate_key /etc/pki/tls/private/server.key;
重启Nginx服务器后,客户端浏览器就可以采用https://方式来访问网站了,这样确保了通信安全。
3 处理速度改进
对软件的运行方式进行优化,才可以充分发挥服务器硬件性能,使得服务器处理Web请求的速度大大加快,从而避免服务器横向扩展,一方面降低了服务器购置成本,另一方面降低了软件管理上的复杂度。可以从如下几个方面进行技术改进来加快LNMP服务器的处理速度:用内存代替磁盘、使用PHP缓存、多进程并行处理、优化InnoDB存储引擎性能和压缩后再传输。
3.1 用内存代替磁盘
内存的存取速度远高于磁盘的存取速度,因此把网站的程序文件和数据库直接放在内存中,将会大大提高网站的访问速度。目前最大容量内存是单条128 GB,一块CPU可以插8条内存,也就是1 TB。如果是双路CPU,最大内存可达8 TB。网站代码磁盘占用量一般在1 GB以内,MariaDB数据库的数据文件一般在20 GB以内,因此一台64 GB的服务器就可以将网站和数据库全部放入内存直接存取。
将磁盘文件放入内存的方法,就是使用TMPFS文件系统。TMPFS,即临时文件系统,是最好的基于RAM的文件系统,是由Linux内核的VM子系统管理的。TMPFS初始容量默认是物理内存大小的一半[6]。指定的TMPFS内存空间并不会被锁定独占,只有挂载存储文件后才会占用相应大小的空间。
使用TMPFS文件系统最大的风险是意外掉电后该文件系统中的文件会全部消失。为了防止服务器意外掉电,确保数据安全,一方面定期自动备份数据库,另一方面,确保服务器上的双电源都有效。使用通信型UPS不间断电源通过联机USB线实时监测市电,一旦发现断电,立即会切换到电源供电,并自动将内存中的文件保存到磁盘中,再自动关闭CentOS服务器,最后自动关闭UPS。整个处理过程只需5分钟,所以UPS只需600 W的功率,如LADIS H1000 600 W型UPS。
3.2 使用PHP缓存
使用PHP缓存,可以大大减少同一网页再次被访问时PHP的编译时间开销,从而大大加快网页访问速度。
PHP是解释型语言而非编译型语言,每次调用PHP文件时,翻译器先将PHP文件翻译成易于执行的中间代码(即opcode字节码)后再执行,执行完后所占用的内存马上被释放,基本上所有数据包括opcode字节码在此时都被销毁。opcode字节码并非目标机器代码,不能直接在硬件上运行。该PHP文件第二次被调用时,同样还是会被重新转换为字节码,但很多时候文件内容几乎是一样的,比如静态HTML文件生成字节码后其内容很久都不会改变,这样就非常浪费时间。
为了避免上述问题,PHP开发了Opcache组件。该组件的前身是Optimizer+,它是PHP的官方公司Zend开发的一款闭源但可以免费使用的PHP优化加速组件,2013年3月中旬Optimizer+改名为Opcache,在PHP源代码中已经包含了该组件。启用PHP的Opcache组件后,PHP会缓存PHP字节码,使得再次调用相同的PHP文件时,无须编译,直接从Opcache缓存中获取字节码去运行;同时,Opcache还应用了一些代码优化模式,使得代码执行更快,从而加速PHP的执行,使CPU消耗少了许多,PHP网页的响应时间也大幅缩短。图2是PHP程序使用Opcache缓存后的生命周期示意图。
可以看到,传统的PHP的整个运行过程为:Request请求(Nginx等)→Zend引擎读取.php文件→扫描其词典和表达式→解析文件→创建要执行的计算机代码(称为Opcode) →执行Opcode→ Response返回。使用了Opcache组件后,如果PHP网页没有变化,就直接从Opcache缓存中读取中间代码,再去执行,避免了CPU的重复计算。
3.3 多进程并行处理
为了充分利用CPU多核特征,Nginx和PHP都设计为多进程的工作方式,以提高处理效率。从图3所示的LNMP架构示意图中可以看出,客户端浏览器与Nginx管理进程交互后,Nginx监控进程就会把具体的工作任务分配给一个空闲的Nginx工作进程,对于用户的PHP网页请求,Nginx工作进程会把PHP的编译和运行工作交给PHP-FPM管理进程,PHP-FPM则把具体的工作任务分配给一个空闲的PHP FastCGI工作进程,PHP FastCGI会把要访问的PHP网页编译并运行,并存取MariaDB数据库,最后把结果组织成HTML超文本,回传给用户端浏览器。
图3 LNMP架构示意图
因此,为Nginx和PHP-FPM设置合适的工作进程数量和工作方式,可以加快LNMP整体架构的处理速度。Nginx通过worker_processes指令本配置开启多少个工作进程,其数量一般为CPU的逻辑核心数量(即CPU超线程总量)。例如,双CPU共16个物理核心,每个物理核心上有2个超线程,那么worker_processes应该设置为32。
PHP-FPM进程池(pm)有static(静态)、dynamic(动态)和ondemand(按需)三种PHP FastCGI工作进程启动方式。
如果将pm设置为static,那么PHP FastCGI工作进程始终保持pm.max_children个数目。
如果将pm设置为dynamic,那么PHP-FPM管理进程首先启动pm.start_servers个工作进程,遇到访问高峰时,会自动增加工作进程数量,确保任何时候都有pm.min_spare_servers个空闲进程存在。访问高峰过后,如果空闲进程多于pm.max_spare_servers个时,多余的空闲进程会被杀掉;而同时能存活的最大工作进程总量为pm.max_children个。
如果将pm设置为ondemand,那么刚开始时php-fpm管理进程不会创建任何工作进程,当有请求时才会创建;当进程在pm.process_idle_timeout秒后还处于空闲状态就会被杀掉,默认时长为10秒。因此,在完全空闲时,只有一个PHP-FPM管理进程存在。在此模式下,能同时存活的工作进程最大数量为pm.max_children个。
要根据具体情况来选择这三种PHP-FPM的进程管理方式。如果内存足够大,可以选用static方式,按一个PHP进程占用30 MB内存的标准来确定PHP进程设置数量,由于不用在使用时临时创建PHP进程,因此这种方式效率最高,作为首选方式。如果服务器内存不宽裕,则可以选用dynamic方式,动态应付访问高峰,能兼顾内存与效率。ondemand方式把节约内存放在首位,其缺点是遇到访问高峰或者如果pm.process_idle_timeout的值太短的话,服务器会频繁创建进程,用户会感受到明显的网络延迟。
3.4 优化InnoDB存储引擎性能
MariaDB的存储引擎采用插件式工作方式,而InnoDB存储引擎作为事务型数据库的首选引擎,支持事务安全表(ACID),能与MariaDB数据库系统完美结合。MariaDB系统接收的用户SQL请求和传输的数据最后都要通过InnoDB存储引擎与磁盘设备交互。LNMP架构中用户请求数据链最后都要经过InnoDB存储引擎,因此InnoDB数据库的存取性能决定了LNMP架构的性能。MariaDB中的InnoDB实际上是XtraDB,它是InnoDB的增强版,但MariaDB的配置文件中仍然标记为InnoDB。InnoDB存储引擎采用缓冲池来缓存数据,最后按设定的时间间隔将数据写到磁盘中去持久化。
设置InnoDB的缓存池的大小innodb_buffer_pool_size为所有内存的80%以上。当然,需要除去CentOS操作系统、PHP进程和Nginx进程的内存开销,余下的都设置为InnoDB的缓存。在64 GB的内存下,一般16 GB InnoDB缓存就足够使用了,其中操作系统、TMPFS文件系统、Nginx进程和PHP进程分配16 GB内存,其余32 GB全部分配给MariaDB的线程。
innodb_log_files_in_group日志文件分为3组就足够了[7]。此外,为了避免在日志文件覆盖时产生不必要的缓冲池刷新活动,每个日志文件的大小应该正好占缓存大小的25%。这样,innodb_log_file_size正好设置为innodb_buffer_pool_size的1/12。
假如InnoDB缓冲的大小为16 GB时,每个innodb_log_file_size就是1 365 MB了。
InnoDB内核中允许的线程数量依赖服务器硬件性能,设置太高会导致线程抖动。一般将innodb_thread_concurrency设置为16即可。
InnoDB存储引擎中用来同步操作系统I/O的写线程innodb_write_io_threads和读线程innodb_read_io_threads在UNIX/Linux操作系统上都设置为8个。
innodb_flush_log_at_trx_commit的值可以设置为0、1或2。设置为2时,每个交易在提交时都会写日志到文件缓存中,且大约每秒将日志文件缓存刷新到磁盘中去持久化,这样使得InnoDB的日志存盘时间开销达到最少,又兼具数据安全。
3.5 压缩后再传输
经过前面的LNMP服务器性能优化后,处理PHP页面的速度得到了加快,访问LNMP架构的性能瓶颈就落在网页从Nginx服务器传输到用户浏览器的网络带宽上了。HTTP协议使用gzip压缩网页内容,能极大减少传输内容的大小,从而使网页访问速度得到大幅提升。
gzip是GNUzip的缩写,它是一个GNU自由软件的文件压缩程序,由Jean-loup Gailly和Mark Adler一起开发。其工作原理是:在一个文本文件中找出类似的字符串,并临时替换它们,使整个文件变小。这种形式的压缩对Web来说非常适合, 因为HTML和CSS文件通常包含大量的重复的字符串,例如空格、标签。检测表明,Nginx启用gzip压缩后,网页字节数缩小为原先的20%,其压缩率高达80%,有效减少了网络带宽开销,大幅提高了网页的浏览速度。
gzip的压缩过程如下:
(1) 浏览器发送HTTP Request给Web服务器,Request中有Accept-Encoding: gzip、deflate字符串,它告诉服务器,当前的浏览器支持gzip压缩。
(2) Web服务器接到Request后, 生成原始的Response,其中有原始的Content-Type和Content-Length。
(3) Web服务器通过gzip,来对Response进行编码, 编码后header中有Content-Type和Content-Length(压缩后的大小),并且增加了Content-Encoding:gzip字符串,然后把Response发送给浏览器。
(4) 浏览器接到Response后,根据Content-Encoding:gzip字符串来对Response进行解码,获取到原始Response后,显示出网页。
Nginx服务器是通过ngx_http_gzip_module、ngx_http_gzip_static_module、ngx_http_gunzip_module三个模块对指令进行解析处理。其中:ngx_http_gzip_module模块对Response进行压缩;ngx_http_gzip_static_module负责搜索和发送经过gzip处理的数据;ngx_http_gunzip_module用于对后端服务器或者预压缩数据解压,为了防止浏览器不能解压,用此模块解压。
Nginx服务器中,gzip的压缩级别gzip_comp_level可以设置为1~9级,级别越高,压缩率更高,但服务器的CPU开销也越大,访问高峰时,会影响LNMP架构的整体性能。同时,由于压缩级别越高,压缩比提高并不是非常明显,所以需要权衡压缩比与CPU开销增长之间的关系,当gzip_comp_level设置为6时,性价比最好。
Nginx通过在nginx.conf配置文件中添加gzip on指令来启用gzip压缩功能。为了避免对于访问同一个文件时被重复压缩,可以开启静态压缩模块ngx_http_gzip_static_module, 在使用gzip_static on 指令后,Nginx会直接读取之前已经压缩好的文件(文件名为*.gz),而不是现场动态压缩,这样再次加快了网页访问速度。
4 抗拥塞改进
上文对LNMP架构各个环节的优化最大可能地缩短了服务器处理延迟和线路处理延迟,使服务器能在0.5秒左右完成单个用户的HTTP请求处理的全部环节,用户获得了极快的网页响应体验。
但当LNMP架构遇到瞬间大量的HTTP请求时,服务器就会出现响应缓慢、卡死甚至数据库服务器崩溃的典型现象。因此,对服务器进行抗拥塞改进成为整个LNMP架构服务器技术改进的重中之重。
实践表明,遇到访问高峰时,服务器出现拥塞的原因在于:(1) MariaDB数据库默认采用了实验室模式的线程模型,使得访问高峰时响应缓慢;(2) PHP工作进程数过多,导致MariaDB连接数同步变大,造成数据库服务崩溃。
4.1 MariaDB采用线程池模型
PHP与MariaDB建立连接后,MariaDB通过工作线程来处理来自PHP的请求。由于MariaDB数据库默认的线程模型是one-thread-per-connection,即会为每个PHP-MariaDB连接创建一个独立的线程,处理PHP请求的任务完成后,该线程的生命周期就结束了。遇访问高峰时,MariaDB创建的线程数量激增,导致操作系统频繁的上下文切换和对系统资源的竞争,造成访问阻塞,LNMP架构吞吐量急剧下降,直到无法正常提供HTTP服务。
Oracle公司的MySQL 5.5商用版采用线程池插件(Thread Pool Plugin)有效地解决了这个问题。由开源社区维护的MariaDB从5.5版本开始内建了线程池(Thread Pool),先将连接分配到不同的组的队列里,以语句(statement)为最小单位,排队处理用户的SQL请求,不光减少了同时运行的线程的数量,还减少了上下文切换的次数;尤其是通过优先队列和普通队列运行机制,保证了最短时间内完成对一个事物(transaction)的处理,减少了并行处理事务的机率,最大可能地防止了对热点资源的竞争产生死锁;同时,线程的复用降低了创建和销毁线程的CPU开销,使得在访问高峰时MariaDB数据库能保持恒定的最高的吞吐量,有效解决了阻塞问题[8]。
为了适应不同的运行场景,MariaDB线程池提供了以下8个可调节参数:
thread_handling=pool-of-threads
thread_pool_size=32
thread_pool_priority=auto
thread_pool_prio_kickup_timer=1 000
thread_pool_stall_limit=60 ms
thread_pool_oversubscribe=10
thread_pool_max_threads=65 536
thread_pool_idle_timeout=500
通常只需要在MariaDB的配置文件my.cnf中设置thread_handling=pool-of-threads线程池就能很好地运行了,其余7个参数MariaDB默认会直接复制CentOS操作系统的线程池运行参数。
线程池通过使用分组的方法来限制和平衡并发,隔离了用户连接和SQL请求语句,高优先级队列使一个完整的事务能尽快完成,减少了并行事务的数量,最大可能降低事务对热点资源的抢占,同时使用Timer定时线程防止线程组运行停滞,实现了高并发下平稳的高吞吐量,防止LNMP架构访问高峰时的阻塞现象。
Thread pool取得了良好的应用效果,根据Oracle MySQL官方的性能测试:在并发达到128个连接以后,没有线程池的MySQL性能会迅速降低。使用线程池以后,性能不会出现波动,会一直保持在较好的状态运行。在读写模式下,128个连接以后,有线程池的MySQL比没有线程池的MySQL性能高出60倍。在只读模式下,512个连接以后,有线程池的MySQL比没有线程池的MySQL性能高出18倍。
4.2 固定PHP进程数量
在LNMP架构中,来自客户端的请求通过Nginx传达到PHP-FPM服务器中,PHP-FPM服务器将这些用户请求排队发送给PHP工作进程去处理,每个PHP工作进程都会与MariaDB建立一个连接,MariaDB则通过某种线程模型去处理来自PHP连接的SQL请求。通过观察发现,PHP工作进程的数量与MariaDB所创建的连接connection数量是相等。也就是说,如果PHP进程模型采用的是dynamic(动态)方式的话,同时有1 000个用户来连接LNMP服务器,那么PHP-FPM服务器将创建1 024个PHP工作进程,此时,MariaDB数据库服务器也将创建1 024个connection连接,并同时将这1 000个连接交给自己的工作线程去处理。在如此大的连接处理的压力下,MariaDB的工作线程,无论是one-thread-per-connection每个连接一个线程的模式还是pool-of-threads进程池处理模式,都会无力运转。因此,固定PHP进程数量,成为解决问题的关键。
所以,PHP应该采用static(静态)工作方式,设置为64个固定的静态进程并行处理就可以了,MariaDB同时建立的connection连接数最大就是64,这很好地保护了后端的MariaDB数据库服务器的正常运行。
通常,可以根据先MariaDB最大允许的连接数量反过来确定PHP工作进程设置的最大数量。例如,MariaDB以每个连接42.2 MB的最大内存用量来计算的话,如果分配32 GB的内存用量给MariaDB连接使用,那么MariaDB的最大连接数max_connections可以设置为776,也就是说PHP工作进程数量也可以设置为776。但考虑到每个PHP工作进程最大30 MB的内存开销和LNMP架构整体CPU计算、内存开销情况,一般PHP设置为64个进程同时运行就足够了。以此很好地保护了MariaDB服务器,即使遇到访问高峰,MariaDB数据库服务器承受的计算压力也稳定在一个可承受水平上,不至于被压垮。
5 LNMP服务器测试
5.1 服务器配置
被检测的LNMP服务器是闪电Moodle服务器https://mood.nbpt.edu.cn/;服务器CPU型号及数量是Intel(R) Xeon(R) CPU E5-2640 v3 @ 2.60 GHz,2块,共16核心32线程;内存型号及容量是DDR 3,64 GB;硬盘是SCSI硬盘,转速15 000 r/min,共4块300 GB的硬盘,建了Raid 5磁盘阵列。
5.2 压缩比测试
Nginx中网页传输采用gzip压缩后,经过http://tool.chinaz.com/Gzips/Default.aspx?q=mood.nbpt.edu.cn网站统计,压缩率(估计值)达到了73.07%,即减少了73.07%的数据传输的体积。
5.3 安全性检测
经国内某知名的网络安全公司对该LNMP服务器进行的权威安全性检测,没有发现紧急、高危和中危漏洞,通过了ITSS(信息技术服务标准)、ITIL v3(信息技术基础设施库)、ISO/IEC 20000(IT服务管理国际标准体系)、ISO/IEC 27000(信息技术-安全技术-信息安全管理体系-要求)系列服务标准下的安全评测。使用的检测软件是WebScan(V6.0.1.9),Engine Version是V6.1.79, Policy Version是V6.1.109。
5.4 性能测试
对LNMP服务器整体性能测试,采用Chrome浏览器自带的Network网页加载计时工具,从浏览器发出PHP读取MariaDB数据库的请求,到浏览器收到运行结果,即针对一个完整的Request和Response的PHP存读取数据库操作计时,发现优化前https://mood.nbpt.edu.cn/index.php主页中PHP访问MariaDB加载耗时稳定在352 ms左右;而优化后,主页加载耗时稳定在124 ms左右,处理速度达到优化前的2.8倍,优化效果明显。
5.5 抗拥塞测试
在整个LNMP架构中,拥塞出现在MariaDB服务这个节点中。因此,采用sysbench OLTP RO对MariaDB数据库进行了只读压力测试,考察其在高线程时的吞吐量TPS,即每秒钟系统能够处理的交易或事务的数量,如图4所示。
图4 两种线程调度器吞吐量对比
可以看出,在高达4 096个线程并发读取的情况下,新的pool-of-threads线程池吞吐量TPS能稳定在5 250;而传统的thread-per-connection调度器TPS则急速下降到268,几乎被压垮。测试表明,使用线程池设计的MariaDB数据库服务器具有抗拥塞特征,能应付网络访问高峰。
6 结 语
本文详细论述了LNMP服务器在安全性、处理速度和抗拥塞性上的深度技术改进,应用在闪电moodle服务器(网址 https://mood.nbpt.edu.cn/)中,该服务器得到了多年的应用检验,用户体验度高,取得了很好的应用效果。
本文提出的技术改进对构建于Linux操作系统上的各类系统如Docker等的性能优化都具有重要的参考价值。随着技术进步,人们对LNMP服务器不断深入研究,LNMP服务器也必将更加安全、快速和稳固。