https://github.com/xpressengine/xe-core/pull/1598


XE는 느리다는 악평이 많은 편입니다. XE는 여러가지 현대적인 프레임워크 형태이면서 트리거, 애드온 위젯 등등의 여러 인터페이스를 통해서 확장이 가능한 형태이고, 이러한 확장성때문에 느리다고 하더라도 많은 불만이 있지는 않은 것 같습니다.

그러나 XE는 몇가지만 고치더라도 클라우드플레어나 varnish 캐서서버 혹은 nginx 리버스 프록시 캐시서버를 통해서 그 성능을 획기적으로 향상시킬 수 있습니다. 그것도 아주 간단한 패치를 하는 것 만으로 이러한 성능향상이 가능합니다.

그것은 다름아닌 캐시서버의 적중률을 방해하는 대표적인 요소인 Set-Cookie: 헤더를 최소화 하는 것입니다.

Set-Cookie:헤더는 세션이 시작되는 경우 및 쿠키를 설정하는 경우에 설정이 됩니다. 웹 서비스를 하게되면 대부분의 사용자는 로그인사용자가 아닌 익명의 사용자이고, 로봇등에 의해서 상당량의 접속이 있게됩니다. 이러한 상당수의 익명의 클라이언트의 경우 세션을 세팅하지 않거나 쿠키 헤더를 쓰지 않는것 만으로도 캐시서버의 적중률을 높일 수 있습니다.

XE의 경우에도  session_start()가 무조건적으로 호출되고 있습니다. 이 경우 Set-Cookie:PHPSESSID=... 쿠키가 설정이 되며 varnish 캐시서버가 효율적으로 작동하지 않게 됩니다. 아래는 varnish 캐서서버 + XE + nginix (php5-fpm) 설정을 한 서버에 XE를 설치한 후에 살펴본 헤더입니다.

$ curl -I  http://localhost/
HTTP/1.1 200 OK
Content-Type: text/html; charset=UTF-8
X-Powered-By: PHP/5.5.9-1ubuntu4.9
Cache-Control: public, must-revalidate, post-check=0, pre-check=0
Set-Cookie: PHPSESSID=1g12hp22q8r0ojbjog7seaft26; path=/
Accept-Ranges: bytes
Date: Thu, 09 Jul 2015 00:54:35 GMT
X-Varnish: 1471733958
Age: 0
Via: 1.1 varnish
Connection: keep-alive
X-Backend-Server: nginx/1.4.x (Ubuntu)
Server: nginx/1.4.x (Ubuntu)
X-Cache-Hit: MISS

위에서 보는바와 같이 varnish 캐시서버에서 캐시가 미스가 되고 있습니다.

ab 벤치마크입니다. 1000회를, 동시에 3개 요청하는 경우입니다. 상당히 느리고 이것이 현재 XE를 nginx + varnish 캐시서버로 운용할때의 대략적 성능입니다. (혹은 단순히 nginx 혹은 아파치 웹서버만 운용하는 경우)

$ ab -n 1000 -c 3  http://localhost/
This is ApacheBench, Version 2.3 <$Revision: 1528965 $>
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/

Benchmarking localhost (be patient)
Completed 100 requests
Completed 200 requests
Completed 300 requests
Completed 400 requests
....
Requests per second:    74.85 [#/sec] (mean)
Time per request:       40.079 [ms] (mean)
Time per request:       13.360 [ms] (mean, across all concurrent requests)
Transfer rate:          2533.88 [Kbytes/sec] received
...

그러면 이번에는 XE 성능을 100배 향상시키는 속도 패치를 적용한 후에 테스트를 해보겠습니다. 즉 session_start()가 기본적으로 호출이 안되고 Set-Cookie 헤더가 붙지 않게 되며, 캐시 적중률이 올라가게 됩니다.

$ curl -I  http://localhost/
HTTP/1.1 200 OK
Content-Type: text/html; charset=UTF-8
X-Powered-By: PHP/5.5.9-1ubuntu4.9
Cache-Control: public, must-revalidate, post-check=0, pre-check=0
X-Backend-Status: normal
Date: Thu, 09 Jul 2015 00:59:14 GMT
X-Varnish: 1471735403 1471735392
Age: 6
Via: 1.1 varnish
Connection: keep-alive
X-Backend-Server: nginx/1.4.x (Ubuntu)
Server: nginx/1.4.x (Ubuntu)
X-Cache-Hit: HIT

(몇차례 curl -I를 실행하면 Cache-Hit가 HIT 상태임을 나타내고 있습니다.)

ab 벤치마크 측정

$ ab -n 1000 -c 3  http://localhost/
This is ApacheBench, Version 2.3 <$Revision: 1528965 $>
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/

Benchmarking localhost (be patient)
Completed 100 requests
Completed 200 requests
Completed 300 requests
....
Requests per second:    8018.35 [#/sec] (mean)
Time per request:       0.374 [ms] (mean)
Time per request:       0.125 [ms] (mean, across all concurrent requests)
Transfer rate:          269428.95 [Kbytes/sec] received
...

8000 RPS로 아파치 벤치값으로 비교하여 무려 ~100배가 향상되었습니다.

동시에 200개 요청을 하고 20000회 요청을 해면 다음과 같습니다.

$ ab -n 20000 -c 200  http://localhost/
...
Requests per second:    14025.98 [#/sec] (mean)
Time per request:       14.259 [ms] (mean)
Time per request:       0.071 [ms] (mean, across all concurrent requests)
Transfer rate:          473883.72 [Kbytes/sec] received

무려 14000 RPS나 나옵니다. 초당 470MB/s
동시에 500개 요청을 해도 결과는 비슷합니다. (ab -n 20000 -c 500)

더 놀라운 것은 시스템의 부하 상태입니다. (ab -n 20000 -c 500을 처리하고 난 이후)

$ w
 10:04:25 up xx days, 17:28,  x user,  load average: 0.25, 0.19, 0.18

그러면 nginx만의 성능은 어떨까요 ? ab -n 10000 -c 50 http://localhost:8000/ 1만회 50개 요청을 동시에 해보았습니다.

$ ab -n 10000 -c 50  http://localhost:8000/ (ngix가 8000 포트로 설정되어 있음)
This is ApacheBench, Version 2.3 <$Revision: 1528965 $>
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/

Benchmarking localhost (be patient)
Completed 1000 requests
Completed 2000 requests
Completed 3000 requests
Completed 4000 requests
....
Requests per second:    106.64 [#/sec] (mean)
Time per request:       468.850 [ms] (mean)
Time per request:       9.377 [ms] (mean, across all concurrent requests)
Transfer rate:          3570.91 [Kbytes/sec] received
....

~100RPS 수준이고 3MB/s 밖에 안나옵니다.

그리고 시스템 부하상태입니다.

$ w
 10:09:01 up xx days, 17:33,  x user,  load average: 3.64, 1.45, 0.63

현재 이 패치는 https://github.com/xpressengine/xe-core/pull/1598 PR로 등록되어 있으며 반영되기를 기다리고 있습니다. 또한 XE 포럼에도 글을 등록하였습니다. https://www.xpressengine.com/forum/23040664

XE3 개발때문인지 그다지 반응이 시근둥한 편입니다만 많은 관심가져주시기 바랍니다~

참고로 현재 이 패치는 리그베다위키 BBS에 적용되어 운용중입니다.

by dumpcookie 2015. 7. 19. 04:00

세션($_SESSION쿠키)의 사용은 캐시 서버의 캐시 적중률에 방해를 일으켜서 캐시서버 성능을 저하시키는 원인이 됩니다.

https://github.com/gnuboard/gnuboard5/pull/15

이 패치는 그누보드에서 무조건적으로 session_start()가 호출되는 부분을 변경하여. session_start()가 선택적으로 호출되게 하여 캐시서버의 캐시 적중률을 높여줍니다.

이 패치를 적용하고 난 후에 다음과 같은 줄을 커맨트아웃시킨 후에 아파치벤치의 성능을 살펴보겠습니다.

// common.php의 다음 줄을 찾아서 커맨트아웃
// 방문자수의 접속을 남김
#include_once(G5_BBS_PATH.'/visit_insert.inc.php');

위 visit_insert.inc.php는 중복 접속이 카운팅되는 것을 막기위해 쿠키를 사용하고 있는데, 쿠키를 사용하는 부분때문에 캐시 적중률을 떨어뜨리고 있습니다. 이 부분은 다른 방식으로 고쳐야만 그누보드가 좀 더 캐시서버 친화적이 될 수 있습니다.

패치 적용하지 않은 경우

$ ab -n 1000 -c 30 "http://g4.xxxxx.xxx"

Requests per second:    1854.10 [#/sec] (mean)
Time per request:       16.180 [ms] (mean)
Time per request:       0.539 [ms] (mean, across all concurrent requests)
Transfer rate:          21510.47 [Kbytes/sec] received
...

~1800RPS 및 21MB/s가 나오고 있습니다.

패치 적용 후

이 패치를 적용한 후에 중복 접속 카운팅 부분을 커맨트아웃 시키고
varnish 캐시서버 + 그누보드5 + nginx (php4-fpm 세팅) 성능을 아파치벤치마크 성능이 다음과 같이 나옵니다.

$ ab -n 1000 -c 30 "http://g4.xxxxxx.xxxx/"
...
Requests per second:    12976.40 [#/sec] (mean)
Time per request:       2.312 [ms] (mean)
Time per request:       0.077 [ms] (mean, across all concurrent requests)
Transfer rate:          148354.17 [Kbytes/sec] received
...

~12000RPS 및 148MB/s 속도가 나오고 있습니다.

접속회수 1만회 동시접속을 300까지 늘려보면

$ ab -n 10000 -c 300 "http://g4.xxxxxx.xxxx/"
...
Requests per second:    17307.54 [#/sec] (mean)
Time per request:       17.333 [ms] (mean)
Time per request:       0.058 [ms] (mean, across all concurrent requests)
Transfer rate:          197871.10 [Kbytes/sec] received
...

서버 로드상태

$ w
 01:30:28 up xx days,  8:54,  x users,  load average: 0.13, 0.08, 0.11

단순히 아파치벤치 성능상으로 최대 ~8배의 성능 향상이 있음을 볼 수 있습니다.


이 패치는 현재 https://github.com/gnuboard/gnuboard5/pull/15 에 등록시켜 반영을 기다리고 있는 중입니다. (http://sir.co.kr/g5_tip/2964 글도 같이 올렸습니다.)

by dumpcookie 2015. 7. 19. 03:38

최신 PHP에 대응하는 eAccelerator 빌드를 구하기 어려운 경우가 있는데, 더군다나 Win32 빌드가 버그가 있어서 캐시가 제대로 저장되지 않고 error_log에 오류를 뿌려대는 버그가 있습니다.

...
EACCELERATOR: Open for write failed for "C:/tmp/eaccelerator/0/3/2//eaccelerator-322dca16f42e18e9c9905165d150e901": No such file or directory
...

이를 수정하기 위해서 win32 빌드를 직접 해 보았습니다.

빌드하는 방식은 다음 사이트를 참고하여 정확히 따라하면 완벽하게 빌드됩니다.

 How to build APC or eAccelerator for PHP on windows

(블로그를 검색해보면 한글로 설명해놓은 곳도 많으니 참고하시기 바랍니다.)

캐시 디렉토리가 잘 못 지정되는 버그에 대한 패치는 다음과 같습니다.

--- a/eaccelerator.c
+++ b/eaccelerator.c
@@ -396,7 +396,11 @@ int eaccelerator_md5(char* s, const char* prefix, const char* key TSRMLS_DC)
     PHP_MD5Update(&context, (unsigned char*)key, strlen(key));
     PHP_MD5Final(digest, &context);
     make_digest(md5str, digest);
+#ifndef ZEND_WIN32
     snprintf(s, MAXPATHLEN-1, "%s/%d/", EAG(cache_dir), ea_mm_instance->cache_dir_uid);
+#else
+    snprintf(s, MAXPATHLEN-1, "%s/", EAG(cache_dir));
+#endif
     n = strlen(s);
     for (i = 0; i < EACCELERATOR_HASH_LEVEL && n < MAXPATHLEN - 1; i++) {
         s[n++] = md5str[i];


이 문제는 커밋 "Windows reverted to the old way, to hard to get a uid."의 부족분을 약간 더 수정한 것입니다.

다음 첨부파일은 PHP 5.4.19 소스를 바탕으로 컴파일된 결과물이며, TS / NTS 버전을 각각 포함하고 있습니다.

eAccelerator-v1.0-php-5.4.xy.zip

※ 주의: eAccelerator는 PHP 최신의 일부 기능이 제대로 안되는 문제가 있으며, 여기에 첨부된 dll은 이 문제에 관련된 패치가 전혀 적용되어 있지 않으니 주의하셔야 합니다.

by dumpcookie 2013. 8. 28. 19:38

mecab 프로젝트에 대해 조금 더 살펴보니 2006년에 만들어진 꽤 오래된 프로젝트였습니다. http://mecab.googlecode.com/svn/trunk/mecab/doc/index.html

mecab 패키지에는 이미 java/perl/python 등등의 바인딩을 지원하지만 php 바인딩만 자체 지원하지 않습니다. 그래서 php 바인딩은 없나 하고 살펴보니 2008년도부터 php바인딩을 지원하는 프로젝트가 있었더군요. https://github.com/rsky/php-mecab by rsky

rpm 패키지는 없나 해서 살펴보니 금방 찾을 수 없어서, 그냥 빌드해봤습니다. (참고로 본인은 Fedora 15 사용중)

  1. 소스를 받고 : 소스가 github의 레포지터리이므로 git clone으로 소스를 받고
  2. php 바인딩 소스 디렉토리에서 phpize 실행
  3. make 명령으로 빌드
  4. modules 디렉토리에 생성된 mecab.so/usr/lib/php/modules 아래에 설치
  5. php.ini 설정

$ git clone https://github.com/rsky/php-mecab
Cloning into php-mecab...
remote: Counting objects: 616, done.
remote: Compressing objects: 100% (249/249), done.
remote: Total 616 (delta 427), reused 548 (delta 359)
Receiving objects: 100% (616/616), 120.44 KiB | 110 KiB/s, done.
Resolving deltas: 100% (427/427), done.
$ cd php-mecab/
$ ls
mecab  packages  README.md
$ cd mecab
$ phpize
Configuring for:
PHP Api Version:         20090626
Zend Module Api No:      20090626
Zend Extension Api No:   220090626

그런다음 빌드

$ ./configure
checking for grep that handles long lines and -e... /bin/grep
checking for egrep... /bin/grep -E
checking for a sed that does not truncate output... /bin/sed
checking for cc... cc
checking whether the C compiler works... yes
checking for C compiler default output file name... a.out
checking for suffix of executables...
checking whether we are cross compiling... no
checking for suffix of object files... o
checking whether we are using the GNU C compiler... yes
checking whether cc accepts -g... yes
checking for cc option to accept ISO C89... none needed
checking how to run the C preprocessor... cc -E
(중략)
checking whether to build shared libraries... yes
checking whether to build static libraries... no
configure: creating ./config.status
config.status: creating config.h
config.status: executing libtool commands
$ make
... (생략)

php 바인딩 소스 본체가 mecab.c라는 소스가 전부라서 눈깜짝할 사이에 빌드가 됩니다.

빌드된 mecab.so 파일을 다음과 같은 식으로 설치해줍니다.

$ sudo cp modules/mecab.so /usr/lib/php/modules/

다음과 같은 내용을 /etc/php.ini에 넣거나, /etc/php.d/mecab.ini라는 파일을 만들어 추가해줍니다.

extension=mecab.so

다음과 같은 간단한 예제를 examples/* 디렉토리 아래에 있는 다양한 예제를 참고해서 만들어봤습니다.

$dic = './mecab-ko-dic-1.1.3-20130226';
ini_set('mecab.default_dicdir', $dic);

$arg = array();

$mecab = mecab_new($arg);

$str = "PHP-mecab 한글테스트중입니다. 무궁화꽃이 피었습니다";
echo mecab_sparse_tostr($mecab, $str);

mecab_destroy($mecab);

이 파일은 test.php라고 이름붙여 저장하고, mecab용 한글 사전 디렉토리는 현재 디렉토리 아래에 있는 은전한닢 프로젝트의 사전으로 지정하였으며 (./mecab-ko-dic-1.1.3-20130226), 다음과 같이 실행해 보니 아무런 문제없이 잘 실행됨을 볼 수 있었습니다.

$ php test.php
PHP     SL,*,*,*,*,*,*
-       SY,*,*,*,*,*,*
mecab   SL,*,*,*,*,*,*
한글    NN,T,한글,*,*,*,*
테스트  NN,F,테스트,*,*,*,*
중      NNB,T,중,*,*,*,*
입니다  VCP+EF,F,입니다,Inflect,VCP,EF,이/VCP+ㅂ니다/EF
.       SF,*,*,*,*,*,*
무궁화  NN,F,무궁화,Compound,*,*,무궁+화
꽃      NN,T,꽃,*,*,*,*
이      JKC,F,이,*,*,*,*
피      VV,F,피,*,*,*,*
었      EP,T,었,*,*,*,*
습니다  EC,F,습니다,*,*,*,*
EOS

by dumpcookie 2013. 3. 11. 16:06
| 1 |