返回介绍

最佳实践 47:配置高性能动态网站

发布于 2025-04-20 17:44:44 字数 4422 浏览 0 评论 0 收藏

所谓动态网站,是指需要根据用户的请求数据实时计算出页面内容的网站。这类网站包括论坛、在线交易等。PHP 和 Java 是开发动态网站中使用比较广泛的编程语言,相应的 PHP-FPM 和 Tomcat 是这两种编程语言的运行环境。下面讲解这两种运行环境的优化方法。

PHP-FPM 优化

1.php.ini 优化

在 php.ini 中,需要设定的优化项目为 max_execution_time,其表示每个 PHP 程序执行的最长时间,默认值是 30s。通常情况下,每个 PHP 程序的最长执行时间可以设置为 5s 或者以下,这样可以防止执行时间过长的 PHP 程序把 FPM 进程数耗尽。建议值为 5s。

2.php-fpm.conf 优化

在 php-fpm.conf 中,需要设定的优化项目如下。

- error_log:指定文件,记录 PHP 程序执行过程中的错误。

- log_level:日志的记录级别,从高到低依次为 alert,error,warning,notice,debug。建议修改成 warning。

- pm:指定进程的管理方法,可选项是 static、dynamic 和 ondemand。在实践中,使用 static 的静态方法是效率最高的。使用 static 方法时,FPM 主进程初始化时一次性生成指定数量(由 pm.max_children 指令控制)的子进程,用于处理 Nginx 使用 FastCGI 协议转发过来的请求。

- pm.max_children:FPM 主进程初始化时一次性生成指定数量。根据网站的并发量估算,在通常的经验中,初始时设置为 32 一般可以满足大部分网站的并发处理需求。

- slowlog:指定慢处理程序的调用栈输出文件位置。slowlog 是 FPM 中最重要的日志之一,通过这个日志,可以分析出网站程序中响应较慢的那些处理脚本,同时能够定位到具体的执行慢的函数。

- request_slowlog_timeout:指定超过多长的执行时间后,需要把程序的调用栈输出到 slowlog 指定的位置。建议值:1s。

- request_terminate_timeout:指定单一请求超过多长的执行时间后,FPM 主进程把子进程关闭。通常情况下,每个 PHP 程序的最长执行时间可以设置为 5s 或者以下,这样可以防止执行时间过长的 PHP 程序把 FPM 进程数耗尽。建议值:5s。

3.使用 file_get_contents 的警告

在运维某游戏论坛时,有游戏玩家反馈论坛访问较慢,同时,外部探测也证实出现有时打开较慢的情况。通过查看 FPM 的 slowlog,可以看到如下提示:

Sep 13 19:55:57.230849 pid 15519 (pool default)
script_filename = /app/www/bbs.xcb.sdo.com/viewthread.php
[0x00007fff85016ce0] file_get_contents() /app/www/bbs.xcb.sdo.com/include/global.func.php:1464
[0x00007fff8501af50] QueryPropertyInfo() /app/www/bbs.xcb.sdo.com/viewthread.php:610
[0x00007fff85027b60] viewthread_procpost() /app/www/bbs.xcb.sdo.com/viewthread.php:390

查看文件/app/www/bbs.xcb.sdo.com/include/global.func.php:

1463         $url = 'http://gip.xcb.sdo.com:8089/handlers/QueryPropertyInfo.ashx? appid=1&gameid=88&pt='.$ptaccount.'&ip='.$onlineip.'&q=2,3,4';
1464         $content = @file_get_contents($url);

通过对 gip.xcb.sdo.com 端口 8089 的检测,可以判断是无法正常连接。而在使用 file_get_contents 函数获取 URL 的内容时,没有超时机制,导致该接口无法正常返回时,占满 PHP FPM 解析进程,出现页面无法加载的情况。

通过修改代码后访问正常:

$url = 'http://gip.xcb.sdo.com:8089/handlers/QueryPropertyInfo.ashx?appid=1&gameid=88&pt='.$ptaccount.'&ip='.$onlineip.'&q=2,3,4';
$content = Http_Get($url);

Http_Get($url)函数加入了超时机制 curl_setopt($ch,CURLOPT_TIMEOUT,2)。

通过这个事件,可以学习到,对于任何外部接口,在调用过程中,必须加入相应的超时机制和超时后的处理方法,否则,可能导致整个站点出现无法访问的情况。

Tomcat 优化

1.增加 Tomcat 可以使用的内存

在默认安装 Tomcat 后,Tomcat 可以使用的内存较小。在 catalina.sh 中,通过修改以下内容来增加 Tomcat 可以使用的内存(注意加粗字体部分,根据本身服务器可用内存进行调整):

if [ -z "$LOGGING_MANAGER" ]; then
  JAVA_OPTS="$JAVA_OPTS -XX:PermSize=256M -XX:MaxPermSize=1024m -Xmx8192m -Xms8192m -Djava.util.logging.manager=org.apache.juli.ClassLoaderLogManager"
else
  JAVA_OPTS="$JAVA_OPTS -XX:PermSize=256M -XX:MaxPermSize=1024m -Xmx8192m -Xms8192m $LOGGING_MANAGER"
fi

2.MySQL JDBC 连接丢失的问题解决

在 Tomcat 中,一般使用 JDBC 的连接池去操作 MySQL 数据库。在 Tomcat 日志中,遇到过连接丢失的情况,日志输出如下:

2013-07-18 08:40:21,671[ERROR,JDBCExceptionReporter] The last packet successfully received from the server was 50,286,413 milliseconds ago. The last packet sent successfully to the server was 50,286,809 milliseconds ago. is longer than the server configured value of 'wait_timeout'. You should consider either expiring and/or testing connection validity before use in your application, increasing the server configured values for client timeouts, or using the Connector/J connection property 'autoReconnect=true' to avoid this problem.
org.hibernate.exception.JDBCConnectionException: could not execute query
at org.hibernate.exception.SQLStateConverter.convert(SQLStateConverter.java:99)
at org.hibernate.exception.JDBCExceptionHelper.convert(JDBCExceptionHelper.java:66)
at org.hibernate.loader.Loader.doList(Loader.java:2545)
at org.hibernate.loader.Loader.listIgnoreQueryCache(Loader.java:2276)
at org.hibernate.loader.Loader.list(Loader.java:2271)
at org.hibernate.loader.custom.CustomLoader.list(CustomLoader.java:316)
at org.hibernate.impl.SessionImpl.listCustomQuery(SessionImpl.java:1842)at org.hibernate.impl.AbstractSessionImpl.list(AbstractSessionImpl.java:165)
at org.hibernate.impl.SQLQueryImpl.list(SQLQueryImpl.java:157)

发生这个问题的原因是:连接池中的某个连接,因为超时,被 MySQL 关闭了,但程序依然试图通过这个连接对 MySQL 发起请求。

解决问题的方法是,在配置 JDBC 时,加入以下重试机制:

jdbc:mysql://mysql-ip:3306/?autoReconnect=true

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。