XpressEngine에도 mod_disk_cache를 간단히 적용해보았습니다.

주의할 점

실제로 mod_disk_cache를 제대로 적용하기 위해서는 세션 쿠키 등등을 최소화하여 캐싱이 유리하도록 만들고, 익명 사용자에 대해서는 세션쿠키를 쓰지 말고 별도로 처리하면 캐싱이 유리하게 됩니다.

이 문서는 단순히 s-maxage=3으로 세팅하는 매우 간단한 패치이며, mod_disk_cache의 효과를 아파치벤치라는 간단한 벤치마크 툴로 테스트하는 결과를 보여주므로 실제로 사용자가 느낄 수 있는 성능 향상과는 괴리가 있을 수 있습니다.


XpressEngine에 적용하기

XE 버전 1.7.x에 대해서 작성했습니다. 패치로 만드는 대신에 다음과 같이 수동으로 고치는 방식을 설명합니다.

1) classes/display/DisplayHandler.class.php 파일에서 다음 함수를 찾아서 Expires, Cache-Control, Pragma 헤더 삭제.

        /**
         * print a HTTP HEADER for HTML, which is encoded in UTF-8
         * @return void
         */
        function _printHTMLHeader()
        {
                header("Content-Type: text/html; charset=UTF-8");
                #header("Expires: Mon, 26 Jul 1997 05:00:00 GMT"); # 제거
                header("Last-Modified: " . gmdate("D, d M Y H:i:s") . " GMT");
                #header("Cache-Control: no-store, no-cache, must-revalidate"); # 제거
                header("Cache-Control: s-maxage=3, max-age=0, must-revalidate");
                header("Cache-Control: post-check=0, pre-check=0", false);
                #header("Pragma: no-cache"); # 제거
        }


2) classes/context/Context.class.php 파일에서 session_start()를 찾아서 session_cache_limiter('') 추가

                session_cache_limiter(''); # 추가
                session_start();

이렇게 고치면 끝.

테스트 결과

아파치 벤치(ab -n 1000 -c 3 http://localhost/xe/)로 테스트해보니 다음과 같은 결과가 나왔습니다.

... Concurrency Level: 3 Time taken for tests: 0.433 seconds Complete requests: 1000 Failed requests: 0 Write errors: 0 Total transferred: 9072075 bytes HTML transferred: 8596000 bytes Requests per second: 2309.36 [#/sec] (mean) Time per request: 1.299 [ms] (mean) Time per request: 0.433 [ms] (mean, across all concurrent requests) Transfer rate: 20459.63 [Kbytes/sec] received


by dumpcookie 2013. 11. 9. 00:46

하는김에 도쿠위키도 mod_disk_cache를 간단히 적용시켜 보았습니다.

이론대로라면 도쿠위키의 경우에도 mod_disk_cache를 적용시키면 static html를 서비스하는 것과 같은 효과를 보게 되므로, 제 테스트 서버에서 ~2000 RPS의 속도가 나와야 합니다.

우선 테스트 서버에서 도쿠위키 해더를 분석해보았습니다.

$ wget -S  -nd http://localhost/doku/doku.php
--2013-11-08 23:00:14--  http://localhost/doku/doku.php
Resolving localhost... 127.0.0.1
Connecting to localhost|127.0.0.1|:80... connected.
HTTP request sent, awaiting response...
  HTTP/1.1 200 OK
  Date: Fri, 08 Nov 2013 14:00:14 GMT
  Server: Apache/2.2.17 (Fedora)
  X-Powered-By: PHP/5.3.8
  Set-Cookie: DokuWiki=thd20jrlllcl784amndh49mrs3; path=/doku/; HttpOnly
  Expires: Thu, 19 Nov 1981 08:52:00 GMT
  Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0
  Pragma: no-cache
  Set-Cookie: DWcc0cd92de073dc44ef3a8f01d4702539=deleted; expires=Thu, 01-Jan-1970 00:00:01 GMT; path=/doku/; httponly
  Vary: Accept-Encoding
  Connection: close
  Content-Type: text/html; charset=utf-8
Length: unspecified [text/html]

위의 예에서 살펴보면 Expires: Cache-Control: 및 Pragma: no-cache가 붙는데, 이는 도쿠위키에서 session을 쓸 때에 자동으로 붙게되는 헤더입니다. 이를 일단 무시하도록 하기 위해서 session_start()를 찾아서 session_cache_limiter('');를 넣어주면 Expires: Cache-Control:Pragma: no-cache가 붙지 않게 되며, Cache-Control 헤더에는 s-maxage=3를 수동으로 붙여넣어 주어야 합니다.

도쿠위키를 간단히 고쳐주면 다음과 같습니다.

--- inc/init.php        2013-11-08 22:40:43.606000239 +0900
+++ inc/init.php        2013-11-08 22:43:10.418000603 +0900
@@ -141,6 +141,7 @@

 // init session
 if (!headers_sent() && !defined('NOSESSION')){
+    session_cache_limiter('');
     session_name("DokuWiki");
     $cookieDir = empty($conf['cookiedir']) ? DOKU_REL : $conf['cookiedir'];
     if (version_compare(PHP_VERSION, '5.2.0', '>')) {
@@ -148,6 +149,7 @@
     }else{
         session_set_cookie_params(0,$cookieDir,'',($conf['securecookie'] && is_ssl()));
     }
+    header('Cache-Control: public, s-maxage=3, max-age=0, post-check=0, pre-check=0');
     session_start();

     // load left over messages

이렇게 고쳐주고 헤더가 잘 변경되었는지 살펴보니

$ wget -S  -nd http://localhost/doku/doku.php
--2013-11-08 23:07:23--  http://localhost/doku/doku.php
Resolving localhost... 127.0.0.1
Connecting to localhost|127.0.0.1|:80... connected.
HTTP request sent, awaiting response...
  HTTP/1.1 200 OK
  Date: Fri, 08 Nov 2013 14:07:23 GMT
  Server: Apache/2.2.17 (Fedora)
  X-Powered-By: PHP/5.3.8
  Cache-Control: public, s-maxage=3, max-age=0, post-check=0, pre-check=0
  Set-Cookie: DokuWiki=u9upco5mfls4nv87uqel66l4r0; path=/doku/; HttpOnly
  Set-Cookie: DWcc0cd92de073dc44ef3a8f01d4702539=deleted; expires=Thu, 01-Jan-1970 00:00:01 GMT; path=/doku/; httponly
  Vary: Accept-Encoding
  Connection: close
  Content-Type: text/html; charset=utf-8
Length: unspecified [text/html
...

위와 같이 s-maxage가 잘 추가되어있음을 알 수 있습니다.

이렇게 고치고 난 후에 아파치벤치(ab -n 1000 -c 3)를 돌려보니, 패치 전에는 ~90 RPS가 나오던 도쿠위키가 패치 이후에는 ~1500~2000 RPS가 나오게 되었습니다.

$ ab -n 1000 -c 3 http://localhost/doku/doku.php
This is ApacheBench, Version 2.3 <$Revision: 655654 $>
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
Completed 500 requests
Completed 600 requests
Completed 700 requests
Completed 800 requests
Completed 900 requests
Completed 1000 requests
Finished 1000 requests


Server Software:        Apache/2.2.17
Server Hostname:        localhost
Server Port:            80

Document Path:          /doku/doku.php
Document Length:        37282 bytes

Concurrency Level:      3
Time taken for tests:   0.641 seconds
Complete requests:      1000
Failed requests:        0
Write errors:           0
Total transferred:      37846542 bytes
HTML transferred:       37356564 bytes
Requests per second:    1560.62 [#/sec] (mean)
Time per request:       1.922 [ms] (mean)
Time per request:       0.641 [ms] (mean, across all concurrent requests)
Transfer rate:          57679.66 [Kbytes/sec] received

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        0    0   0.2      0       1
Processing:     1    1   0.4      1       3
Waiting:        0    1   0.4      1       2
Total:          1    2   0.4      2       3

Percentage of the requests served within a certain time (ms)
  50%      2
  66%      2
  75%      2
  80%      2
  90%      2
  95%      3
  98%      3
  99%      3
 100%      3 (longest request)

mod_disk_cache 적용시 주의할 점

주의할 점은 이 패치는 단지 mod_disk_cache를 이런 식으로 붙일 수 있다는 것을 보여줄 뿐이지, 도쿠위키와 긴밀하게 작동하지는 않는다는 점입니다. (도쿠위키의 경우 세션을 사용하기때문에 세션 쿠키가 헤더에 붙게되는데, 이러한 쿠키는 mod_disk_cahe가 캐시를 효율적으로 하기 어렵게 합니다. 도쿠위키 및 모니위키 모두 mod_disk_cache를 십분 활용하기 위해서는 session 쿠키 등등을 최소화해서 static html로 서비스하도록 고쳐주어야 합니다.)

by dumpcookie 2013. 11. 8. 23:14

미디어위키는 어떠한 방식으로 속도를 향상시키고 있는지 관련 문서를 살펴보다가 이와 관련된 한글 문서가 거의 없다시피 하다는 것을 알게 되어서 간략하게 mod_disk_cache를 모니위키에 적용하는 방식을 요약 보고해봅니다.


미디어위키 튜닝에 관련된 문서를 보다가 http://www.mediawiki.org/wiki/Manual:Performance_tuning

아래 문서에 관한 링크를 알게 되었는데 이에 의하면 그 느려터진 미디어위키도 엄청난 속도 향상이 있다고 합니다.


즉, 6 RPS(Requests Per second. 초당 페이지 요청 수) 수준의 느린 미디어위키가 800 RPS로 속도향상이 됩니다.

http://www.testbit.eu/2011/using-mod_disk_cache-with-mediawiki/

아마도 테스트 서버의 사양이 낮아서 800 RPS가 나온 것으로 추측되는데,

이론적으로 mod_disk_cache를 사용하게되면 static html이 저장하여 서비스하게 되므로, 로컬에서 아파치벤치로 테스트하면 static html 속도가 나와야 합니다.


그래서 이것을 모니위키에 적용해보면 어떨까 하여서 모니위키에 간단한 패치로 캐시 컨트롤하는 부분에 s-maxage를 넣어서 패치시키고 mod_disk_cache를 적용해봤습니다.


약 200~300 RPS 속도가 나오던 모니위키가 문서에 거의 상관 없이 로컬 서버에서 1500~2000 RPS의 속도가 나오는군요. (~2000 RPS 수준이면 제 로컬 서버에서 정적(static) HTML 속도 수준입니다)


s-maxage를 설정하는 부분은 위의 링크에 자세하게 설명되었지만, s-maxage를 3으로 설정해서 dynamic/static 페이지에 상관 없이 약 3초간 모든 문서를 static문서로 간주하게끔 해서 mod_disk_cache 기능이 작동되도록 하는 것입니다. 이 경우 dynamic PHP 문서가 static으로 3초간 유지되기때문에 사용자는 문서가 약간 지연이 되면서 갱신되는 듯한 느낌을 받을 수 있습니다.


모니위키에 mod_disk_cache 적용해보기


미디어위키에 관련된 자세한 내용은 위의 링크를 통해 알 수 있으므로, 모니위키의 경우에 대해서 s-maxage를 적용시킨 부분을 간단히 적어보겠습니다. (패치라고 할 것도 없이 매우 간단한 패치입니다. wiki.php의 소스 맨 아래쪽에 있는 부분을 고치면 됩니다)


패치 이전에 다음 명령을 내려보면

$ wget -S --delete-after -nd http://moniwiki.kldp.net/wiki.php/HelpContents

--2013-11-08 21:27:22--  http://moniwiki.kldp.net/wiki.php/HelpContents
Resolving moniwiki.kldp.net... 115.145.209.51
Connecting to moniwiki.kldp.net|115.145.209.51|:80... connected.
HTTP request sent, awaiting response...
  HTTP/1.1 200 OK
  Date: Fri, 08 Nov 2013 12:27:22 GMT
  Server: Apache/2.2.17 (Fedora)
  X-Powered-By: PHP/5.3.8
  Cache-Control: public, max-age=0, post-check=0, pre-check=0
  Set-Cookie: MONIWIKI-%2A-41a4c8ea773d14d5de8455aeee1d0600-%2A-Anonymous=ujh7bu4e4aidsfn9lkq0j0b482; expires=Fri, 08-Nov-2013 13:27:22 GMT; path=/wiki.php
  Set-Cookie: MONI_TRAIL=En%7EMoniWiki%09HelpContents; expires=Sun, 08-Dec-2013 12:27:22 GMT; path=/wiki.php
  Vary: Accept-Encoding
  Connection: close
  Content-Type: text/html
Length: unspecified [text/html]
....

Cache-Control이 다음과 같이 나오도록 간단히 패치해줍니다. (wiki.php 소스의 아래쪽을 고치면 됨)

(실제로 아래와 같이 고치지는 않았으나 좀 더 명확하게 보여주기 위해서 패치의 핵심만을 포함시켰습니다)

diff --git a/wiki.php b/wiki.php
index d2cc150..ab8a0e7 100644
--- a/wiki.php
+++ b/wiki.php
@@ -5828,7 +5828,7 @@ if (session_id() == '' and empty($Config['nosession']) and is_writable(ini_get('
   else
     $private = 'private';
   if (empty($_GET['action']) or $_GET['action'] == 'show')
-    header('Cache-Control: '.$private.', max-age=0, post-check=0, pre-check=0');
+    header('Cache-Control: '.$private.', s-maxage=3, max-age=0, post-check=0, pre-check=0');
   //else if (!empty($_GET['action']) and in_array($_GET['action'], array('edit', 'info', 'diff')))
   //  header('Cache-Control: '.$private.', max-age=600, post-check=0, pre-check=0');

...
  Cache-Control: public, s-maxage=3, max-age=0, post-check=0, pre-check=0
...

이렇게만 하면 mod_disk_cache가 작동되게 되며 로컬 서버에서 ab -n 5000 -c 3 -k로 다음과 같은 속도가 나오게됩니다.

$ ab -n 5000 -c 3 -k http://moniwiki.kldp.net/wiki.php/HelpContents
This is ApacheBench, Version 2.3 <$Revision: 655654 $>
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/

Benchmarking moniwiki.kldp.net (be patient)
Completed 500 requests
Completed 1000 requests
Completed 1500 requests
Completed 2000 requests
Completed 2500 requests
Completed 3000 requests
Completed 3500 requests
Completed 4000 requests
Completed 4500 requests
Completed 5000 requests
Finished 5000 requests


Server Software:        Apache/2.2.17
Server Hostname:        moniwiki.kldp.net
Server Port:            80

Document Path:          /wiki.php/HelpContents
Document Length:        14845 bytes

Concurrency Level:      3
Time taken for tests:   2.204 seconds
Complete requests:      5000
Failed requests:        0
Write errors:           0
Keep-Alive requests:    0
Total transferred:      76975299 bytes
HTML transferred:       74239845 bytes
Requests per second:    2269.11 [#/sec] (mean)
Time per request:       1.322 [ms] (mean)
Time per request:       0.441 [ms] (mean, across all concurrent requests)
Transfer rate:          34114.34 [Kbytes/sec] received

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        0    0   0.2      0       7
Processing:     0    1  26.5      0    1507
Waiting:        0    1  16.1      0    1009
Total:          0    1  26.5      1    1508

Percentage of the requests served within a certain time (ms)
  50%      1
  66%      1
  75%      1
  80%      1
  90%      1
  95%      1
  98%      1
  99%      1
 100%   1508 (longest request)

위의 예에서 2269 RPS가 나왔는데, 평균적으로 2000 RPS정도 나옵니다. @@



mod_disk_cache를 사용할 때에 주의할 점은

1. 이 경우 익명 사용자의 경우 대해서만 s-maxage가 적용되고 mod_disk_cache에 의해 캐싱된다.

2. mod_disk_cache가 좀 더 완벽하게 적용되려면 모든 PHP 결과를 dynamic 컨텐츠가 아닌 static 컨텐츠가 될 수 있도록 고치면 더욱 좋다. 이렇게 하면 s-maxage가 3초가 아니라 좀 더 길게 잡을 수 있게 된다. (이 문제는 미디어위키의 경우도 마찬가지)

3. 3초간의 짧은 시간동안 캐시가 유지되므로 접속이 많이 몰리는 경우에 서버에 부담을 덜 수 있게 된다.

4. 캐시가 3초간의 짧은 시간동안에만 유지되므로 실제로 체감 속도는 크게 달라지지는 않는다. 단, 서버의 로드가 줄어들기때문에 사용자가 많은 위키의 경우에 효과를 볼 수 있게 된다.


차기버전의 모니위키 1.2.2에서는 mod_disk_cache를 지원하도록 하는 패치가 같이 포함될 예정입니다.



그밖에 mod_cache/mod_disk_cache를 사용할 때에 유용한 팁이 있는 링크입니다.

http://www.philchen.com/2009/02/09/some-tuning-tips-for-apache-mod_cache-mod_disk_cache




by dumpcookie 2013. 11. 8. 21:54

최신 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

예전부터 하려던 것을 이번 모니위키 1.2.0 릴리스를 내놓으면서 이것 저것 정리하면서 시도해보았습니다.

인터넷에 검색해보면 http://www.xpressengine.com/tip/15921874같은 내용처럼 모니위키를 구버전 XE에서 연동시키는 소스를 볼 수 있지만 위의 예에서 연동시키는 방식으로는 모니위키의 ACL 플러그인을 쓰지 못할 뿐만 아니라, XE의 구조가 조금만 바뀌어도 쓸 수 없게 되어버리기때문에 그리 권장할 만한 구현 방식이 아닙니다.

XE의 회원을 연동하기 위해서는 nFORGE 회원과 모니위키를 연동시킨 방식처럼 "회원이 로그인 된 상태인 경우 강제로 모니위키 회원 정보를 생성하는 방식"으로 접근하면 편리합니다. 이 경우에는 Security 플러그인을 확장하는 방식이 아니라 모니위키의 회원 클래스인 WikiUser 클래스를 확장하는 방식을 사용하게 됩니다.

class User_xe17 extends WikiUser {
    function User_xe17($id = '') {
        global $DBInfo;

        define('__XE__', true);

        $zbxe_root_dir = !empty($DBInfo->zbxe_root_dir) ?
                $DBInfo->zbxe_root_dir : __DIR__.'/../../../xe'; // XE root dir

        require_once($zbxe_root_dir."/config/config.inc.php");

        $context = &Context::getInstance();
        $context->init(); // 여기서 매우 느리다.
        ...

(위의 방식은 제가 직접 찾은 방법이나, 이러한 방식으로 할 수 있다는 내용을 블로그를 검색해보니 찾을 수 있더군요)

그러나 위와 같은 방식으로 하면 모니위키가 너무 너무 느려지기때문에 간소화된 init()을 다음과 같은 식으로 호출하는 것이 낫습니다.

    function xe_context_init($xe) {
        //
        // simplified XE context init method to speed up
        //

        // set context variables in $GLOBALS (to use in display handler)
        $xe->context = &$GLOBALS['__Context__'];
        $xe->context->_COOKIE = $_COOKIE;

        $xe->loadDBInfo();

        // set session handler
        if (Context::isInstalled() && $this->db_info->use_db_session == 'Y') {
            $oSessionModel = getModel('session');
            $oSessionController = getController('session');
            session_set_save_handler(
                    array(&$oSessionController, 'open'),
                    array(&$oSessionController, 'close'),
                    array(&$oSessionModel, 'read'),
                    array(&$oSessionController, 'write'),
                    array(&$oSessionController, 'destroy'),
                    array(&$oSessionController, 'gc')
           );
        }
        session_start();

        if ($sess = $_POST[session_name()]) {
            session_id($sess);
        }
    }

    function User_xe17($id = '') {
        global $DBInfo;

        define('__XE__', true);

        $zbxe_root_dir = !empty($DBInfo->zbxe_root_dir) ?
                $DBInfo->zbxe_root_dir : __DIR__.'/../../../xe'; // XE root dir

        require_once($zbxe_root_dir."/config/config.inc.php");

        $context = &Context::getInstance();
        $this->xe_context_init($context); // simplified init context method $context->init(); 대신에 사용.

        $oMemberModel = &getModel('member');
        $oMemberController = &getController('member');

        if ($oMemberModel->isLogged()) {
            ...

참고적으로 위와같이 간소화된 context 초기화 루틴을 사용하는 경우 기존의 context->init()을 사용하는 것보다 거의 20배가 빨랐습니다. (아파치 벤치 ab -n 500 -c 5로 로컬에서 측정한 경우. XE 회원 연동을 아예 하지 않는 경우가 그렇지 않은 경우보다 2.5배 빠름)

이를 좀 더 개선하여 Varnish cache를 사용할 수 있도록 고쳤으며, 최초 접속하는 경우에만 사용자 정보를 DB를 통해 가져오는 절차를 거치고 그 이후에는 세션을 통해서 인증을 하게 되므로 속도 저하가 없어졌습니다.

여기서 사용한 방식을 응용하면 XE1.7의 회원 정보를 보다 빠르게 타 PHP에서 연동시킬 수 있을 것으로 생각됩니다.

완전한 소스는 http://moniwiki.kldp.net/wiki.php/XeUserPlugin 에서 받으실 수 있습니다.

특징 및 기타 변경사항

- 버전 1.5부터는 모니위키 1.2.5에 기본 내장 예정 (2015/04/30)
- XE 1.7, XE 1.8 모두 지원함
- Varnish cache 지원 가능. XE 사용자 모듈을 사용하는데에 따르는 속도 저하가 없음 (2015/4/30 수정)

by dumpcookie 2013. 7. 5. 05:48

드디어 모니위키 속도가 300RPS의 벽을 깼다.

불필요한 초기화 부분 한 줄 제거했는데 이렇게 나옴;; https://github.com/wkpark/moniwiki/commit/bc662afdc2268e86154da8a6abfe61c0a93432c8 참조

아래는 nginx + php-fpm + eaccelerator를 설정하고 모니위키 1.2.0 개발판 테스트해본 결과.

아파치를 쓰더라도 거의 비슷하지만 nginx + php-fpm의 경우가 좀 더 빠른 듯.


아래는 몇 번을 반복했을 때 가장 높은 측정값이 나온 경우.

$ ab -c 5 -n 300 http://localhost:8800/moniwiki/wiki.php?FrontPage
This is ApacheBench, Version 2.3 <$Revision: 655654 $>
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
Finished 300 requests


Server Software:        nginx/0.8.54
Server Hostname:        localhost
Server Port:            8800

Document Path:          /moniwiki/wiki.php?FrontPage
Document Length:        30134 bytes

Concurrency Level:      5
Time taken for tests:   0.778 seconds
Complete requests:      300
Failed requests:        0
Write errors:           0
Total transferred:      9088800 bytes
HTML transferred:       9040200 bytes
Requests per second:    385.65 [#/sec] (mean)
Time per request:       12.965 [ms] (mean)
Time per request:       2.593 [ms] (mean, across all concurrent requests)
Transfer rate:          11409.94 [Kbytes/sec] received

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        0    0   1.2      0      16
Processing:     5   13   4.7     12      43
Waiting:        0   12   4.7     11      43
Total:          5   13   4.9     12      44

Percentage of the requests served within a certain time (ms)
  50%     12
  66%     13
  75%     14
  80%     15
  90%     18
  95%     22
  98%     27
  99%     29
 100%     44 (longest request)

config.php의 설정값

$trail=0; // trail 끔
$cachetime=60*60*24; // 캐시 설정

by dumpcookie 2013. 5. 21. 08:56

최근에 엔하위키 속도 문제에 관련되어서 모니위키를 업데이트하게 되었고,

관련 작업이 모니위키 개발자 버전에 반영되었으며, 차일 피일 미루던 모니위키 1.2.0 릴리스 준비를 하고 있습니다.


엔하위키가 느린 점에 대해서 말들이 많았는데,

현재 엔하위키에는 간단한 몇가지 속도 패치가 적용된 상태입니다.

- 페이지 캐싱 활성화 - 모니위키 1.1.3부터 지원하였던 이 기능을 비로소 최근에 활성화 시켰습니다.

- 모니위키 1.1.5부터 지원했던 랜덤 페이지 전용 페이지 목록 인덱싱 기능을 활성화 시켰습니다.

- 기타 페이지 목록이 빈번하게 갱신되던 문제를 해결한 패치를 적용시켰습니다.



현재 엔하위키는 그나마 예전보다는 나은 속도를 내고 있습니다만 여전히 다음과 같은 문제가 있습니다.


1. 전체 페이지 리스트가 여전히 빈번하게 갱신되고 있다.

  - 현재 랜덤페이지에 의해서 사용되는 전체 페이지 목록을 가져오는 부분이 문제가 있는 듯 여전히 전체 페이지 목록이 자주 갱신되고 있습니다.

  - 이 문제는 패치 자체 버그로 보입니다. 그러나 예전보다 크기가 작고 빠른 속도로 갱신이 되어서 큰 문제를 느끼지 못하고 있는 듯.


2. 검색 - 역링크 / 제목검색 / 본문검색 포함

  - 현재 DBA 인덱서 패치가 추가로 엔하위키에 적용된 상태이지만 몇가지 문제로 사용되지 않고 있습니다.

  - DBA 인덱서를 적용한다 하더라도 역링크 및 제목검색은 상당히 빠르지만 본문검색은 적게는 수초가 걸리고 그 이상으로 느린 경우도 있습니다.

  - 인덱서 패치를 적용했는데 오히려 제목 목차가 느려졌습니다. (효율적이지 않은 제목 인덱싱)



이러한 문제를 해결하기 위해서

1. DBA를 사용한 본문/제목/링크 등등의 인덱싱 처리를 일차적으로 최적화 과정에서 배제시켰습니다.

2. 본문 검색보다는 제목 검색에 초점을 두었습니다. 본문 검색은 외부 검색엔진을 도입하는 것이 여러모로 나을 것으로 생각되며,

3. 전체 페이지 목록을 갱신하는 부분을 모듈화하고, 일단 매우 간단한 구현을 하였습니다. (기존의 페이지 목록 인덱서 개량)

4. 페이지 내용 검색(Fulltext search) 및 이와 관련된 느린 검색에 대해서 일단 시간 제한 혹은 개수 제한을 걸고 페이징 처리를 하도록 하였습니다.


이렇게 방향을 변경하고 다음의 작업이 진행되었습니다.

1. 전체 페이지 목록을 한꺼번에 갱신할 필요가 아예 없게 만들었습니다.

 - 랜덤 페이지 이외에 전체 페이지목록이 필요한 부분에 대해서 통합하고

 - 느린 전체 페이지 갱신을 아예 할 필요가 없게 만들었습니다. (페이지 추가/제거/이름 변경시에 전체 리스트를 업데이트만 함)

 - 이는 기존의 페이지이름 인덱서를 업데이트한 것입니다.


2. 역링크는 이제 검색이 아닙니다.

 - 역링크를 검색기능이 아니게 되었습니다.

  (그런데 모니위키의 역링크는 그 구현상 원래부터 검색이 아니였습니다 ^^;; 이번에 버그를 잡았습니다.)

 - 역링크는 페이지가 만들어지거나 갱신될 때에 즉각적으로 아주 빠르게 갱신됩니다.

 - 실제로 역링크를 사용시에는 그냥 역링크 정보를 가져오기만 합니다.


3. 페이지 이름검색이 빨라지고 유연해졌습니다.

 - 공백이 들어있거나 공백이 아예 없거나, 특수문자가 일부 들어간다고 해도 잘 찾아냅니다.

 - 특별히 이름 검색에 대해서 인덱싱 처리하거나 하지 않는데도 20만 페이지 규모에서도 매우 빠릅니다.


4. 본문 검색에 시간 제한을 걸고, 페이징처리를 하였습니다.

 - 일단 본문검색에 대해서 이러한 방식으로 처리하여 서버에 줄 수 있는 부담을 최소화했습니다.



일단 이러한 일련의 대규모 위키사이트에 대한 작업은 모니위키 1.2.0 개발자 버전에 대해서 진행중입니다.



참고적으로 로컬로 측정한 아파치 벤치마크 값입니다.

- 모니위키의 경우 19만 페이지가 있습니다. / 도쿠위키는 전체 10페이지 미만.

- 페이지는 단순한 HelpContents 페이지를 기준으로 삼았습니다. (static html은 그 랜더링된 결과)

- ab -c 2 -n 500을 기준으로 했습니다. (단순 측정값입니다.)

- 서버사양 AMD dual core 5200 / 2GB 램


static html의 경우 ~1000RPS

모니위키 1.2.0 개발판의 경우 ~300RPS (eaccelerator + 내장 캐시 사용)

모니위키 1.1.5 구버전의 경우 ~220RPS (eaccelerator + 내장 캐시)

도쿠위키 2012-10-13일 판 ~120RPS (eaccelerator + 내장 캐시 사용)

(미디어위키는 기본 설치 설정으로는 너무 느려서 논외.)



모니위키 1.2.0 최신 개발자 버전 맛보기 (임시적으로 엔하위키 19만 페이지가 읽기 전용으로 되어있음)

http://moniwiki.kldp.net/moniwiki/wiki.php


및 모니위키 github 사이트

https://github.com/wkpark/moniwiki


http://moniwiki.kldp.net/wiki.php/MoniWikiFeatures/1.2.0 그밖에 모니위키에 포함될 새로운 기능들

by dumpcookie 2013. 5. 18. 18:31

XE 게시판과 위키를 연동하는 경우

위키 링크라던지 위키에 관련된 기능이 게시판에도 필요한 경우가 종종 있습니다.

([[위키백과]]같은 위키 링크)


이 애드온은 위키의 링크를 손쉽게 연결시켜 주는 xe 게시판용 애드온입니다



이와 관련된 기능은 xe 사이트의 애드온을 찾아보면 이미 있습니다. (wikilink로 검색하면 몇 개 나옴)


여기서 소개하는 애드온은 위키네임을 링크시켜주는 기능 말고도 모니위키에서 지원하는 일부 기능인 URL을 고쳐주는 기능이 같이 들어있습니다.


예를 들어서 http://rigvedawiki.org/r1/wiki/FrontPage 같은 식으로 예전 링크가 있다면 이것을 rigvedawiki.net으로 고쳐서 자동으로 보여줍니다.


급조한 것이지만 xe 1.7에서 아무런 문제없이 동작하는 것을 확인하였습니다~

1.5에서도 문제없이 될 것으로 생각됩니다.


아직 urlmapping은 xe 사이트에 정식등록한 상태가 아닙니다.


urlmapping 애드온은 위키 관련된 링크를 고쳐주거나 하는 애드온이므로 이것과 관련된 기능 제안을 환영합니다~


TODO

- 인터위키 연결 지원 외


스크린샷 (관리자 메뉴)



다운로드


urlmapping-0.1.tar.gz


by dumpcookie 2013. 5. 10. 15:22

리눅스상에서 자유롭게 쓸 수 있는 오픈소스/자유 소프트웨어 Text-To-Speech(TTS)엔진은 꽤 많으며, 다음과 같은 공개 TTS엔진이 유명합니다.

  • Festivalhttp://www.cstr.ed.ac.uk/projects/festival/ - MIT 형식의 라이선스 (상용 제한 없음) - 대부분의 리눅스 배포판에서 기본으로 설치됨
  • MBROLAhttp://tcts.fpms.ac.be/synthesis/ - 한국어도 지원 (부산대 김경석 교수가 만든 "한말(hanmal)" 음성 지원) - 개인은 자유롭게 쓸 수 있으나, 제한적인 라이선스(상용 안됨)
  • eSpeakhttp://espeak.sourceforge.net/ - GPL 라이선스 - 윈도우 및 안드로이드 지원
  • HTS http://hts.sp.nitech.ac.jp/ - BSD 라이선스 (상용제한 없음) - Google TTS엔진에서 사용한다고 함.
    HTS 관련 문서를 읽어보면 상당히 다양한 프로젝트에서 HTS를 사용하고 있다는 것을 알 수 있으며, 음성 소스만 주어지면 그 목소리에 대응하는 synthesize용 목소리를 만들 수 있습니다!! @@ 단, 데모에서 들을 수 있는 음성은 매우 품질이 좋은 반면, flite나 festival 등등의 다른 프로젝트용으로 만들어진 음성자료를 통해 생성된 음질 썩 좋은 편이 아닙니다.
    (데모 페이지는 http://homepages.inf.ed.ac.uk/jyamagis/demos/page35/page35.html를 비롯한 일련의 페이지)

그러나 이러한 공개 TTS엔진에서 만들어내는 소리가 그렇게 훌륭한 편은 아닙니다. MBROLA가 그중에 가장 나은 소리를 들려준다고 하지만 라이선스가 제한이 있고, Festival가 그나마 들어줄만한 소리를 만들어내지만 안드로이드의 svox-pico보다는 음성의 질이 떨어집니다.

구글 안드로이드 도넛(버전 1.6)부터 자체 내장된 svox-pico TTS엔진은 2008년에 SVOX사에서 공개한 TTS엔진입니다. svox-pico는 소스코드까지 공개된 TTS엔진이며, 공개당시 매우 획기적인 일이였고, 소스 및 실행 바이너리 크기가 상당히 적고 가벼운 편이며, 발음도 훌륭한 편이였습니다.

현재는 IVONA와 같은 안드로이드에서 무료로 쓸 수 있는 TTS엔진이 있기때문에 svox-pico의 인기가 다소 떨어진 편이지만, svox-pico의 소스가 공개되어 있으므로 svox-pico를 리눅스에서도 쓸 수 있게되는 것은 시간문제였습니다. (그런데 검색해보니 관련된 한국어 문서가 전혀 없더군요. 그래서 이렇게 포스팅을 하게 된 것입니다 ;)

svox-pico 소스코드

svox-pico 최신 소스코드를 직접 받고자 하는 경우에는 구글 레포지터리를 통해서 받을 수 있습니다.
https://android.googlesource.com/platform/external/svox.git

여기에는 svox의 소스코드뿐만 아니라 언어팩을 만들 수 있는 툴(윈도우용) 및 메뉴얼도 같이 포함되어 있기때문에, 전체 소스코드의 압축된 크기가 상당히 큽니다.

svox-pico 설치해보기

svox-pico는 우분투나 데비안용 패키지가 이미 수년전에 나와있는 상태입니다. 페도라에서 설치하기 위해서 검색해보니 쉽게 찾을 수 없었으나, ALT 리눅스용 소스 rpm이 있었습니다. ALT리눅스용 소스 rpm을 다운로드 받아서 페도라에 맞게끔 spec파일을 살짝 고치고 빌드하니 아무런 문제 없이 컴파일 할 수 있었으며 실행도 아주 잘 되었습니다.

  • src.rpm은 다음 사이트를 통해서 얻을 수 있었습니다.
    http://sisyphus.ru/en/srpm/Sisyphus/svox-pico
  • pico2wave --wave test.wav "Hello World" 라고 실행하면 test.wav가 얻어집니다.
  • 생성되는 소리는 진저브레드에서 Pico-TTS 엔진을 기본으로 설정했을때에 나오는 소리와 똑같았습니다.
  • git 소스로부터 직접 빌드하려 할 경우라면 http://wiki.freeswitch.org/wiki/Mod_tts_commandline 사이트를 참조하시기 바랍니다.


by dumpcookie 2013. 3. 14. 21:54

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 2 3 |