PHP开启 OPCache 来降低负载并提高抗并发能力

opcache 其实就是 OpCode + cache,php 是一种解释型语言,它的执行可分为以下几个流程
2024-09-09T01:07:47.png

这样一来的话,对于同一个文件,反复请求,就要不断解析、编译和执行PHP脚本,消耗过多资源。为了解决这个问题,OpCode 缓存技术应运而生
Opcode 缓存就是将编译好的Opcode码缓存在内存中,下次再执行这个脚本时,直接会去找到缓存下的opcode码,直接执行最后一步,而不再需要中间的步骤了,其目地是避免重复编译,减少 CPU 和内存开销
2024-09-09T01:08:45.png

OPcache 参数说明


一样直接看官方文档: OPcache 配置

这边挑几个平时配置用的比较多的说明一下:

1. opcache.enable

  • 默认值: 1
  • 说明: 启用操作码缓存。如果禁用此选项,则不会优化和缓存代码。 在运行期使用 ini_set() 函数只能禁用 opcache.enable 设置,不可以启用此设置。 如果在脚本中尝试启用此设置项会产生警告。新的 php 版本是默认开启的,但是旧的比如 5.6.x 默认是关闭的

2. opcache.enable_cli

  • 默认值: 0
  • 说明: 仅针对 CLI 版本的 PHP 启用操作码缓存。 通常被用来测试和调试

3. opcache.memory_consumption

  • 默认值: 128
  • 说明: OPcache 的共享内存大小,以兆字节为单位。最小允许值为 "8"。如果设置的值小于最小值,则强制设置为允许的最小值。

4. opcache.interned_strings_buffer

  • 默认值: 8
  • 说明: 存储预留字符串的内存大小。PHP 解释器在背后会找到相同字符串的多个实例,把这个字符串保存在内存中,如果再次使用相同的字符串,PHP 解释器会使用指针。这么做能节省内存。默认情况下,PHP 驻留的字符串会隔离在各个 PHP 进程中。这个设置能让 PHP-FPM 进程池中的所有进程把驻留字符串存储到共享的缓冲区中,以便在 PHP-FPM 进程池中的多个进程之间引用驻留字符串。

5. opcache.max_accelerated_files

  • 默认值: 10000
  • 说明: OPcache 哈希表中可存储的脚本文件数量上限。真实的取值是在质数集合{ 223, 463, 983, 1979, 3907, 7963, 16229, 32531, 65407, 130987, 262237, 524521, 1048793 } 中找到的第一个大于等于设置值的质数。设置值取值范围最小值是 200,最大值是 1000000。超出范围的值将限制为允许的值。

6. opcache.validate_timestamps

  • 默认值: 1
  • 说明: 如果启用,那么 OPcache 会每隔 opcache.revalidate_freq 设定的秒数 检查脚本是否更新。 如果禁用此选项,你必须使用 opcache_reset() 或者 opcache_invalidate() 函数来手动重置 OPcache,也可以 通过重启 Web 服务器来使文件系统更改生效。 

7. opcache.revalidate_freq

  • 默认值: 2
  • 说明: 检查脚本时间戳是否有更新的周期,以秒为单位。 设置为 0 会导致针对每个请求, OPcache 都会检查脚本更新。如果 opcache.validate_timestamps 配置指令设置为禁用,那么此设置项将会被忽略

8. opcache.fast_shutdown

  • 默认值: 0
  • 说明: 如果启用,则会使用快速停止续发事件。 所谓快速停止续发事件是指依赖 Zend 引擎的内存管理模块 一次释放全部请求变量的内存,而不是依次释放每一个已分配的内存块。从 PHP 7.2.0 开始, 默认执行

OPcache 相关配套函数


PHP 官方提供了与 OPcache 相关的函数,具体文档: OPcache 函数

函数名说明
opcache_compile_file该函数可以用于在不用运行某个 PHP 脚本的情况下,编译该 PHP 脚本并将其添加到字节码缓存中去。 该函数可用于在 Web 服务器重启之后初始化缓存,以供后续请求调用。
opcache_get_configuration该函数将返回缓存实例的配置信息。
opcache_get_status该函数将返回内存中缓存实例的状态信息。不会返回有关文件缓存的任何信息。
opcache_invalidate废除脚本缓存
opcache_is_script_cached该函数用于检测 PHP 脚本是否已经被缓存
opcache_reset该函数将重置整个字节码缓存。在调用 opcache_reset() 之后,所有的脚本将会重新载入并且在下次命中的时候重新解析。此函数仅重置内存中的缓存,不会重置文件缓存。

利用这些函数,我们可以手动清空 OPcache,或者也可以将其封装成脚本,在代码更新后自动执行脚本

配置

好了,现在有了大概了解后,我们就可以直接在php.ini中配置啦

[opcache]
zend_extension=opcache.so
opcache.enable = 1
opcache.memory_consumption=1024
opcache.interned_strings_buffer=32
opcache.max_accelerated_files=80000
opcache.revalidate_freq=30
opcache.fast_shutdown=1
opcache.enable_cli=1

注意,这边有几个参数是有调整的,比如将缓存的内存设置为 1024M,将存储预留字符串的内存大小设置为 32M,将 可存储的脚本文件数量上限设置为 80000,最后将缓存刷新的时间设置为 30s。最后重启一下 php-fpm 即可

安装状态面板

我们只需要将其下载到服务器的某一个目录下

cd /data/wwwroot/
git clone https://github.com/rlerdorf/opcache-status.git --depth=1
cd opcache-status

然后在 nginx 那边增加一个路由,然后转发解析到这个目录下的 opcache.php 就行了,注意最好只能内网才能访问

location ^~ /opcache  {
    root    /data/wwwroot/opcache-status/;
    try_files $uri /opcache.php;
    fastcgi_pass 127.0.0.1:9000;
    include    fastcgi.conf;
    allow 127.0.0.1;
    allow xxx;
    allow xxx;
    deny all;
}

2024-09-09T01:20:15.png
本地直接命令行运行php opcache.php效果
2024-09-09T01:18:17.png

OPcache 缓存刷新/重置的几种方式

  1. php 执行 opcache_reset() 方法,重置整个 Opcode 缓存,所有的PHP脚本将会被重新解析再预编译为 Opcode
  2. php 执行 opcache_invalidate()方法, 清除指定脚本缓存,可以传递两个参数,一个是刷新文件路径,一个是force字段, 如果 force 没有设置或者传入的是 FALSE,那么只有当脚本的修改时间 比对应Opcode的时间更新时,脚本的缓存才会失效
  3. 重启 php-fpm service php-fpm restart,不过这种情况请求会中断, 或者重载 service php-fpm reload,重载相对于重启则平顺很多,不会导致用户请求直接中断,相对来说风险低很多,但是 php-fpm 收到reload信号,便会向所有子进程发送 SIGGUIT 信号,同时注册一个定时器,在规定的时间之内子进程没有退出,接着在发送 SIGTERM 信号,结束子进程。如果在一秒之内子进程还是没结束 直接发送SIGKILL 强制杀死,这时候 nginx 就会返回 502 错误码
  4. 配置 opcache.validate_timestamps=1 和 opcache.revalidate_freq=xx, 以秒为单位,那么 OPcache 会每隔 opcache.revalidate_freq 设定的秒数 检查脚本是否更新, 如果发现内存中的时间晚于文件更新时间,将会更新缓存
  5. OPcache 达到最大内存消耗,也就是 opcache.memory_consumption 这个配置,Opcache 就会尝试重新启动缓存
  6. OPcache 达到最大加速文件,也就是 opcache.max_accelerated_files 这个配置,Opcache 就会尝试重新启动缓存
  7. OPcache 浪费的内存超过了配置的 opcache.max_wasted_percentage,Opcache 就会尝试重新启动缓存, 手动清空 wasted memory 只能重启 PHP-FPM 或使用 PHP 函数 opcache_reset()

需要注意的是,当 PHPPHP-FPM 的方式运行的时候,OPcache 的缓存是无法通过 php 命令进行清除的,只能通过 http 或 cgi 到 php-fpm 进程的方式来清除缓存

如果 Opcache 不设置过期时间的话,应尽量避免流量高峰期发布代码,因为这时候清理缓存,可能会导致重复新建缓存。

1. 生产环境不设置缓存定时刷新时间

将刷缓存的时间(opcache.validate_timestamps)设置为 0, 表示永不刷新,然后在每次 Jenkins 构建 php 程序的时候,调用一个脚本来手动刷新缓存

#!/bin/bash
WEBDIR=/var/www/html/
RANDOM_NAME=$(head /dev/urandom | tr -dc A-Za-z0-9 | head -c 13)
echo "<?php opcache_reset(); ?>" > ${WEBDIR}${RANDOM_NAME}.php
curl http://localhost/${RANDOM_NAME}.php
rm ${WEBDIR}${RANDOM_NAME}.php
上面的脚本意思就是,先生成一个随机的字符串 RANDOM_NAME,长度为13,包含大写字母、小写字母和数字,接下来创建一个新的PHP文件,文件名为 ${RANDOM_NAME}.php,并且这个文件在 ${WEBDIR} 目录下。这个PHP文件的内容是<?php opcache_reset(); ?>, 接下来使用 curl 命令访问这个新创建的PHP文件。由于这个文件的内容是 opcache_reset(),所以这会导致服务器上的OPcache被重置, 最后,删除这个临时创建的PHP文件

或者直接走 php 的 CLI 指令,不过这种方式如果是在 php-fpm 下是没有效果的,只能用上面那种 http 请求 php 文件的方式。

#!/bin/bash
php -r "opcache_reset();"

2. 增加 hugepage 配置

启用 opcache.huge_code_pages 将 PHP 代码(文本段)拷贝到 HUGE PAGES 中。这应该会提高性能,但是需要适当的 OS 配置。自 PHP 7.0.0 起可在 Linux 上使用,自 PHP 7.4.0 上可在 FreeBSD 上使用。
opcache.huge_code_pages=1

同时需要在 /etc/sysctl.conf 设置系统分配的大页面(huge pages)的数量,比如下面就分配了512个大页面

$ cat /etc/sysctl.conf | grep huge
vm.nr_hugepages=512

3. 配置 preload 用于缓存预热

PHP 7.4.0 起,PHP 可以被配置为在引擎启动时将一些脚本预加载进 opcache 中
opcache.preload=preload.php

标签: PHP

相关文章

Typecho博客系统的xmlrpc的使用附PHP示例代码

XML-RPC 是一种远程过程调用(RPC)协议,它使用 XML 编码请求和响应,并通过 HTTP 进行传输。XML-RPC 允许客户端调用远程服务器上的方法,并获取返回结果。这种协议简单、轻量...

php+mysql中如何处理嵌套(子)事务并保持原子性一致

在PHP和MySQL中处理子事务并保持原子性一致性是一个复杂但非常重要的问题,尤其是在处理涉及多个数据库操作的业务逻辑时。以下是一些关键的解决方案、思路、技术要点和涉及的难点讲解。解决方案与思路...

如何使用Go编写跨平台组件并让Java或PHP调用

在现代软件开发中,跨语言调用是一个常见的需求。假设我们有一个用Go语言编写的组件,我们希望Java或PHP能够直接调用这个组件中对外提供的方法。为了实现这一目标,我们可以使用以下几种方法:1. ...

深入解析PHP的filter_var函数及其应用场景

在PHP开发中,数据的过滤与验证是至关重要的环节。PHP的 filter_var 函数提供了一种简洁而强大的方式来对输入数据进行验证和过滤。本篇文章将从专业的角度详细介绍 filter_var ...

Typecho开发数据库常用API操作

表创建和删除在 Typecho 插件开发过程中,往往需要创建表。可以使用query()来进行表的创建、修改或者删除。$db= Typecho_Db::get(); $prefix = $db-&...

Typecho博客模板制作手册

文件结构说明文件名作用必须style.css主题样式文件否screenshot.png主题缩略图,图片后缀支持 jpg,png,gif,bmp,jpeg否index.php首页以及说明文件是40...

PHP+Redis查询附近的人功能

在之前的博客《MySQL 查询附近的人》中,使用 MySQL 查询附近的人在用户较少时性能表现尚可。然而,随着应用用户的增多,查询性能问题开始凸显。因此,这篇文章介绍了使用 Redis 的 GE...

图片Base64编码

CSR生成

图片无损放大

图片占位符

Excel拆分文件