PHP Road Runner 와 Nginx + PHP-FPM 성능 테스트


고성능 PHP application server - Road Runner 와 라라벨 연동하기 에 이어서 facebook ModernPUG 사용자들의 의견을 받아서 다시 테스트한 결과입니다.

환경

테스트 환경의 OS 는 CentOS 8입니다.

$ lsb_release -a

LSB Version:    :core-4.1-amd64:core-4.1-noarch
Distributor ID: CentOS
Description:    CentOS Linux release 8.3.2011
Release:        8.3.2011
Codename:       n/a


Max Open File 의 숫자는 524,288 입니다.

$ ulimit -Hn
524288

$ ulimit -Sn
524288

CPU 는 i7-8700 이고 memory 는 32G 입니다.

$ lscpu

Model name:          Intel(R) Core(TM) i7-8700 CPU @ 3.20GHz

$ free -h
              total        used        free      shared  buff/cache   available
Mem:           31Gi       2.9Gi        20Gi       1.3Gi       7.5Gi        26Gi
Swap:          15Gi          0B        15Gi


php.ini 에서 성능에 영향을 줄만한 요소는 다음과 같습니다.

  • memory_limit => 512M => 512M
  • opcache.enable => On => On
  • opcache.enable_cli => On => On


PHP-FPM 의 설정중에서 성능에 영향을 줄 만한 부분을 다음과 같이 설정했습니다.

  • pm.max_children = 200
  • pm.start_servers = 50
  • pm.min_spare_servers = 50
  • pm.max_spare_servers = 100
  • pm.max_requests = 0
 Click here to expand...
$ php-fpm74 -tt

[22-Mar-2021 09:26:05] NOTICE: [global]
[22-Mar-2021 09:26:05] NOTICE:  pid = /var/opt/remi/php74/run/php-fpm/php-fpm.pid
[22-Mar-2021 09:26:05] NOTICE:  error_log = /var/opt/remi/php74/log/php-fpm/error.log
[22-Mar-2021 09:26:05] NOTICE:  syslog.ident = php-fpm
[22-Mar-2021 09:26:05] NOTICE:  syslog.facility = 24
[22-Mar-2021 09:26:05] NOTICE:  log_buffering = yes
[22-Mar-2021 09:26:05] NOTICE:  log_level = unknown value
[22-Mar-2021 09:26:05] NOTICE:  log_limit = 1024
[22-Mar-2021 09:26:05] NOTICE:  emergency_restart_interval = 0s
[22-Mar-2021 09:26:05] NOTICE:  emergency_restart_threshold = 0
[22-Mar-2021 09:26:05] NOTICE:  process_control_timeout = 0s
[22-Mar-2021 09:26:05] NOTICE:  process.max = 0
[22-Mar-2021 09:26:05] NOTICE:  process.priority = undefined
[22-Mar-2021 09:26:05] NOTICE:  daemonize = yes
[22-Mar-2021 09:26:05] NOTICE:  rlimit_files = 0
[22-Mar-2021 09:26:05] NOTICE:  rlimit_core = 0
[22-Mar-2021 09:26:05] NOTICE:  events.mechanism = epoll
[22-Mar-2021 09:26:05] NOTICE:  systemd_interval = 10s
[22-Mar-2021 09:26:05] NOTICE:  
[22-Mar-2021 09:26:05] NOTICE: [www]
[22-Mar-2021 09:26:05] NOTICE:  prefix = undefined
[22-Mar-2021 09:26:05] NOTICE:  user = lesstif
[22-Mar-2021 09:26:05] NOTICE:  group = lesstif
[22-Mar-2021 09:26:05] NOTICE:  listen = /var/opt/remi/php74/run/php-fpm/www.sock
[22-Mar-2021 09:26:05] NOTICE:  listen.backlog = 511
[22-Mar-2021 09:26:05] NOTICE:  listen.acl_users = nginx
[22-Mar-2021 09:26:05] NOTICE:  listen.acl_groups = undefined
[22-Mar-2021 09:26:05] NOTICE:  listen.owner = undefined
[22-Mar-2021 09:26:05] NOTICE:  listen.group = undefined
[22-Mar-2021 09:26:05] NOTICE:  listen.mode = undefined
[22-Mar-2021 09:26:05] NOTICE:  listen.allowed_clients = 127.0.0.1
[22-Mar-2021 09:26:05] NOTICE:  process.priority = undefined
[22-Mar-2021 09:26:05] NOTICE:  process.dumpable = no
[22-Mar-2021 09:26:05] NOTICE:  pm = dynamic
[22-Mar-2021 09:26:05] NOTICE:  pm.max_children = 200
[22-Mar-2021 09:26:05] NOTICE:  pm.start_servers = 50
[22-Mar-2021 09:26:05] NOTICE:  pm.min_spare_servers = 50
[22-Mar-2021 09:26:05] NOTICE:  pm.max_spare_servers = 100
[22-Mar-2021 09:26:05] NOTICE:  pm.process_idle_timeout = 10
[22-Mar-2021 09:26:05] NOTICE:  pm.max_requests = 0
[22-Mar-2021 09:26:05] NOTICE:  pm.status_path = undefined
[22-Mar-2021 09:26:05] NOTICE:  ping.path = undefined
[22-Mar-2021 09:26:05] NOTICE:  ping.response = undefined
[22-Mar-2021 09:26:05] NOTICE:  access.log = undefined
[22-Mar-2021 09:26:05] NOTICE:  access.format = undefined
[22-Mar-2021 09:26:05] NOTICE:  slowlog = /var/opt/remi/php74/log/php-fpm/www-slow.log
[22-Mar-2021 09:26:05] NOTICE:  request_slowlog_timeout = 0s
[22-Mar-2021 09:26:05] NOTICE:  request_slowlog_trace_depth = 20
[22-Mar-2021 09:26:05] NOTICE:  request_terminate_timeout = 0s
[22-Mar-2021 09:26:05] NOTICE:  request_terminate_timeout_track_finished = no
[22-Mar-2021 09:26:05] NOTICE:  rlimit_files = 0
[22-Mar-2021 09:26:05] NOTICE:  rlimit_core = 0
[22-Mar-2021 09:26:05] NOTICE:  chroot = undefined
[22-Mar-2021 09:26:05] NOTICE:  chdir = undefined
[22-Mar-2021 09:26:05] NOTICE:  catch_workers_output = no
[22-Mar-2021 09:26:05] NOTICE:  decorate_workers_output = yes
[22-Mar-2021 09:26:05] NOTICE:  clear_env = yes
[22-Mar-2021 09:26:05] NOTICE:  security.limit_extensions = .php .phar
[22-Mar-2021 09:26:05] NOTICE:  php_value[soap.wsdl_cache_dir] = /var/opt/remi/php74/lib/php/wsdlcache
[22-Mar-2021 09:26:05] NOTICE:  php_value[session.save_path] = /var/opt/remi/php74/lib/php/session
[22-Mar-2021 09:26:05] NOTICE:  php_value[session.save_handler] = files
[22-Mar-2021 09:26:05] NOTICE:  php_admin_value[log_errors] = 1
[22-Mar-2021 09:26:05] NOTICE:  php_admin_value[error_log] = /var/opt/remi/php74/log/php-fpm/www-error.log
[22-Mar-2021 09:26:05] NOTICE:  
[22-Mar-2021 09:26:05] NOTICE: configuration file /etc/opt/remi/php74/php-fpm.conf test is successful


각 실험후 nginx 와 PHP-FPM 을 재구동해서 영향도를 줄였습니다.

nginx + php-fpm

먼저 4 개의 쓰레드로 20개의 connection 으로 부하를 주었더니 초당 490 처리량이 나왔습니다.

$ wrk -t 4 -c 20 http://rr.local

Running 10s test @ http://rr.local
  4 threads and 20 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency   207.00ms  312.96ms   1.15s    81.24%
    Req/Sec   125.88     66.62   376.00     66.24%
  4943 requests in 10.07s, 87.61MB read
Requests/sec:    490.67
Transfer/sec:      8.70MB


4 개의 쓰레드로 50개의 connection 으로 부하를 주었더니 초당 587처리량이 나왔습니다.

$ wrk -t 4 -c 50 http://rr.local 

Running 10s test @ http://rr.local	
  4 threads and 50 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency    41.96ms   19.69ms 121.13ms   67.53%
    Req/Sec   147.76     64.95   413.00     70.71%
  5919 requests in 10.08s, 104.90MB read
  Socket errors: connect 0, read 0, write 0, timeout 79
Requests/sec:    587.25
Transfer/sec:     10.41MB


Road Runner

nginx 와 php-fpm 을 재구동해서 기존 테스트의 영향도를 줄이고 road runner 를 구동했습니다.

조금이라도 빠르게 해보려고 .rr.yaml 설정에 mode 를 production 으로 하고 로깅은 파일로 하도록 설정하고 num_workers 는 100, max_jobs 은 0 으로 셋팅후에 구동했습니다.

server:
  command: "php ./vendor/bin/rr-worker start --relay-dsn unix:///var/run/rr/rr-rpc.sock"
  relay: "unix:///var/run/rr/rr-rpc.sock"

logs:
  mode: production
  output: rr.log
http:
  address: 127.0.0.1:8080
  middleware: ["headers", "static", "gzip"]
  pool:
    num_workers: 100
    max_jobs: 0 # feel free to change this
    supervisor:
      exec_ttl: 60s
  headers:
    response:
      X-Powered-By: "RoadRunner"
  static:
    dir: "public"
    forbid: [".php"]


먼저 마찬가지로 4개 쓰레드로 20개의 connection 을 발생시키자 초당 561 개 처리가 가능했습니다.

$ wrk -t 4 -c 20  http://rr.local 

Running 10s test @  http://rr.local 
  4 threads and 20 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency   255.75ms  383.92ms   1.47s    80.76%
    Req/Sec   154.32    114.25     0.99k    85.08%
  5669 requests in 10.09s, 100.74MB read
Requests/sec:    561.80
Transfer/sec:      9.98MB


4개 쓰레드로 50개의 connection 을 발생시키자 이보다 약간 낮은 초당 546 개 처리가 가능했습니다.

$ wrk -t 4 -c 50  http://rr.local 

Running 10s test @  http://rr.local 
  4 threads and 50 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency    32.54ms   15.72ms  75.93ms   67.50%
    Req/Sec   137.49    104.68   770.00     88.13%
  5506 requests in 10.08s, 97.84MB read
  Socket errors: connect 0, read 0, write 0, timeout 81
Requests/sec:    546.11
Transfer/sec:      9.70MB


결론

제가 잘못 설정했는지 road runner 의 worker 와 FPM 의 worker 를 크게 조정해 봐도 nginx + PHP-FPM 과 큰 차이가 없었습니다. 단순 PHP script 를 비교해 보는 것은 의미가 없다고 생각해서 laravel framework 에서만 테스트를 해 보았는데 왜 성능 차이가 없는지 모르겠네요.

정확한 비교를 위해서 laravel octane 이 나오면 다시 한 번 테스트를 해봐야 하겠네요.