使用 Swoole Compiler 加密后,不再需要 PHP 源代码,因此不再需要对PHP源码进行词法分析、语法分析和生成Opcode字节码。二进制目标文件只有经过处理的 opcode 二进制指令和内存数据,在第一次运行时将指令和数据载入内存。通常加密后的代码运行时性能损耗会增加10%~15%,最高不超过20%。

对全部或大部分PHP代码加密

Swoole Compiler 软件本身就是将 PHP 代码编译为经过特殊处理的opcode,所以使用Swoole Loader运行加密后的程序时,不需要其他OpCache扩展,包括opcache、apc、eAccelerator等opcode缓存扩展。

仅对少部分PHP代码加密

若项目中,仅少部分PHP代码进行了加密,大部分代码未加密。则需要同时开启opcache和swoole_loader来提高性能。当swoole_loader发现是非加密代码时,会将编译控制权转给opcache,由opcache实现代码的加载。

Opcache JIT 引起的 502 问题

请注意 Opcache JIT 与 Swoole Loader 存在不兼容,若要同时使用 swoole_loader 和 opcache 时必须要修改 php.ini 关闭 opcache.jit

opcache.jit=0

Opcache 配置

同时开启opcache和swoole_loader时,必须只使用OPcache文件缓存,并关闭JIT,否则可能会发生崩溃

推荐配置如下:

opcache.enable=1
; Swoole、Hyperf、WorkerMan 等 CLI 常驻进程需要开启
opcache.enable_cli=1
opcache.jit=0
opcache.file_cache=/tmp/php-opcache
opcache.file_cache_only=1

启动PHP进程前,需要创建opcache.file_cache所指定的目录,并确保PHP运行用户对该目录有读写权限。

  • 推荐使用专用目录,例如/tmp/php-opcache或/dev/shm/php-opcache,不要直接使用/tmp或/dev/shm
  • opcache_get_status()函数无法查询文件缓存状态,该函数仅对共享内存缓存有效
  • opcache.file_cache与下文的swoole_loader.cache_dir是两套不同的缓存,不能互相替代

加密文件缓存

Swoole Loader 加载加密文件时,需要打开文件、读取文件头判定类型、完成PHP版本与License校验,再读取加密数据进行解密。若加密代码存放于NFS网络文件系统或机械磁盘,这些读取会产生大量IOPS(尤其是getattr等元数据操作),导致iowait升高、系统负载上升。

Swoole Loader 3.2.5及以上版本支持加密文件缓存:首次加载后将数据缓存到本地目录,之后命中缓存时不再访问源文件所在的NFS或磁盘。

该功能仅在Linux平台生效

开启缓存

修改php.ini:

swoole_loader.cache_enable=1
swoole_loader.cache_dir=/dev/shm/swoole-loader-cache
swoole_loader.cache_lifetime=60

配置项

配置项 默认值 说明
swoole_loader.cache_enable 0 是否开启缓存,默认关闭
swoole_loader.cache_dir /dev/shm/swoole-loader-cache 缓存目录,必须为绝对路径
swoole_loader.cache_lifetime 60 缓存有效期,单位为秒,设置为0表示永不过期

这三个配置项均为PHP_INI_SYSTEM级别,只能在php.ini中修改。

Loader会在cache_dir下按运行用户创建子目录swoole-loader-<uid>(属主为该用户,权限0700),缓存文件命名为<md5>.slc。若cache_dir本身允许其他用户写入(如/tmp、/dev/shm),该目录必须带有sticky位,否则缓存不会生效。

工作原理

  1. 首次加载某个文件时,Loader读取源文件并判定文件类型,将读到的加密数据原样写入本地缓存
  2. 后续加载命中缓存时,直接从本地缓存读取,不再open、stat源文件,路径解析也由缓存直接返回
  3. 缓存超过cache_lifetime后,由其中一个进程读取源文件并刷新缓存,刷新期间其他进程继续使用已有缓存,不会同时涌向NFS
  4. 每次加载依然会执行License校验与解密,缓存只是替换了数据的读取位置,不降低安全性
  5. 未加密文件同样会生成缓存条目,但仅记录文件类型,代码数据仍由opcache负责缓存(见上文)

使用建议

  • 推荐将cache_dir设置为内存文件系统/dev/shm,完全没有磁盘IO;加密文件较多、内存有限时可改用本地SSD目录
  • 请勿将cache_dir设置在NFS上,否则缓存自身也会产生NFS请求
  • 多台机器共享同一份加密代码时,每台机器各自使用本地缓存目录,效果最好
  • 同一台机器运行多个PHP版本时,请为每个版本配置不同的cache_dir
  • 缓存写入失败(如/dev/shm空间不足)会自动降级为直接读取源文件,不影响程序运行
  • 缓存条目按文件路径生成,数量与加密文件数量相当;发布目录变化后旧条目不会自动清理,可手动删除缓存目录

验证与清理

ls /dev/shm/swoole-loader-cache/swoole-loader-*

能列出*.slc文件即表示缓存已生效,此时再用nfsstat -c观察,getattr、read等操作次数应明显下降。

需清理缓存时,停止PHP进程后直接删除缓存目录即可。

挂载 NFS 网络文件系统

若加密后的代码存放于NFS系统,可能会导致文件系统元数据IOPS升高,从而引发系统负载升高。使用下面的命令查看:

开启加密文件缓存后,命中缓存的请求不再访问NFS,以下的挂载参数优化可作为补充手段

nfsstat -c
Client rpc stats:
calls      retrans    authrefrsh
345600562   0          346852200

Client nfs v3:
null         getattr      setattr      lookup       access       readlink     
0         0% 343637891 99% 63        0% 137777    0% 2210824   0% 1         0% 
read         write        create       mkdir        symlink      mknod        
1989      0% 20444     0% 391       0% 0         0% 0         0% 0         0% 
remove       rmdir        rename       link         readdir      readdirplus  
34        0% 2         0% 38        0% 0         0% 97        0% 7495      0% 
fsstat       fsinfo       pathconf     commit       
111       0% 2         0% 1         0% 9         0%

可以看到getattr操作次数非常多,需要对NFS挂载参数进行优化,减少NFS文件系统RPC次数。

1. 调整挂载选项

mount -o remount,actimeo=60,acregmin=60,acregmax=60,acdirmin=60,acdirmax=60 /www/wwwroot

这将设置属性缓存的有效期为 60 秒,可以显著减少网络请求。

2. 使用noatime选项

mount -o remount,noatime /www/wwwroot

这可以减少不必要的访问时间更新。

3. 考虑使用NFSv4

NFSv4 提供了更好的缓存机制,可以减少网络往返。

最后更新:2026-09-29