Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Scalability Issues ... cure first, prevent later

Scalability Issues ... cure first, prevent later

Slides for my PHPBenelux UG session in Houthalen on May 30th

Thijs Feryn

May 31, 2012
Tweet

More Decks by Thijs Feryn

Other Decks in Technology

Transcript

  1. Please3
    rate3my3talk3on3
    Joind.in
    https://joind.in/6577

    View full-size slide

  2. It3works3on3my3computer

    View full-size slide

  3. It’s3the3internet’s3fault

    View full-size slide

  4. Suits
    &
    hipsters

    View full-size slide

  5. “Isn't'hos*ng'
    supposed'to'be'a'
    bulk'product?”

    View full-size slide

  6. “The'Cloud'will'
    make'sure'my'app'
    scales”

    View full-size slide

  7. “Large'campaigns'
    on'shared'hos*ng,'
    why'is'that'a'
    problem?”

    View full-size slide

  8. “What's'a'
    database'index?”

    View full-size slide

  9. “We'll'solve'that'
    with'an'Ajax'call”

    View full-size slide

  10. “We'll'probably'have'
    10.000'visitors'on'
    the'next'campaign”

    View full-size slide

  11. GeBng3the3numbers3right

    View full-size slide

  12. Let’s3do3a3complete3rewrite3!

    View full-size slide

  13. Have3you3got3the3Gme?

    View full-size slide

  14. Or3the3money?

    View full-size slide

  15. What3do3you3do?

    View full-size slide

  16. Throw3
    servers3at3
    the3
    problem

    View full-size slide

  17. Do3some3
    tricks3!

    View full-size slide

  18. Throw3servers3at3the3problem

    View full-size slide

  19. Scale3verGcally

    View full-size slide

  20. Fast
    Cheap
    vs
    Limited
    Expensive
    Long%
    term
    Short%
    term

    View full-size slide

  21. Scale3horizontally

    View full-size slide

  22. Efficient
    Unlimited
    vs
    ConsideraGons
    IniGal3effort
    Long%
    term
    Short%
    term

    View full-size slide

  23. What3kind3of3infrastructure3do3you3need?

    View full-size slide

  24. What3kind3of3infrastructure3do3you3need?
    One3big3
    box?

    View full-size slide

  25. What3kind3of3infrastructure3do3you3need?
    Scale3across3
    1003nodes?

    View full-size slide

  26. What3kind3of3infrastructure3do3you3need?
    That3big3F53
    loadbalancer?

    View full-size slide

  27. What3kind3of3infrastructure3do3you3need?
    Only3the3DB3
    gets3
    hammered

    View full-size slide

  28. What3kind3of3infrastructure3do3you3need?
    Lots3of3staGc3
    traffic

    View full-size slide

  29. What3kind3of3infrastructure3do3you3need?
    Shared3hosGng3
    will3do3...3
    except3during3the3
    month3of3the3event

    View full-size slide

  30. About3a3mulGYserver3setup

    View full-size slide

  31. Goal:3(almost)3no3code3changes

    View full-size slide

  32. Is3caching3
    the3
    answer?

    View full-size slide

  33. Frontend Backend
    Data%
    cache
    Opcode%
    cache
    Page%
    cache
    APC
    Memcached
    SQLite
    File
    Redis
    +more
    +more

    View full-size slide

  34. What?
    When3not?
    How3long?
    CompaGbility
    Limited3size

    View full-size slide

  35. RewriteEngine*On
    RewriteCond*%{REQUEST_FILENAME}*=s*[OR]
    RewriteCond*%{REQUEST_FILENAME}*=l*[OR]
    RewriteCond*%{REQUEST_FILENAME}*=d
    RewriteRule*^.*$*=*[NC,L]
    RewriteRule*^.*$*index.php*[NC,L]
    Use3mod_rewrite
    Hit3frontcontroller3if3resource3doesn’t3exist

    View full-size slide

  36. Use3mod_rewrite
    Write3output3to3file3and3print3to3screen
    require('db.php');
    $data = db_get_content_from_uri(
    $_SERVER['REQUEST_URI']);
    $fp = fopen(dirname(__FILE__).
    $_SERVER['REQUEST_URI'], 'w');
    fwrite($fp, $data);
    fclose($fp);
    echo $data;

    View full-size slide

  37. Auto3prepend3&3auto3append
    AutomaGcally3executed3before3every3request
    [PHP]
    auto_prepend_file*“/path/to/prepend.php”
    auto_append_file*“/path/to/append.php”

    View full-size slide

  38. Auto3prepend
    Read3from3cache3or3start3output3buffer
    if($_SERVER['REQUEST_METHOD'] == 'GET'){
    $key = md5('http://'.$_SERVER['HTTP_HOST'].
    $_SERVER['REQUEST_URI']);
    if(($output = apc_fetch($key)) !== false){
    echo $output;
    exit();
    }
    ob_start();
    }

    View full-size slide

  39. Auto3append
    Close3output3buffer,3store3data3in3cache3&3
    print3output
    if($_SERVER['REQUEST_METHOD'] == 'GET'){
    $output = ob_get_contents();
    ob_end_clean();
    $key = md5('http://'.$_SERVER['HTTP_HOST'].
    $_SERVER['REQUEST_URI']);
    apc_store($key,$output,20);
    echo $output;
    }

    View full-size slide

  40. Data3cache
    Caching3layer3
    before3on3top3
    of3the3
    database

    View full-size slide

  41. class Data
    {
    private $_mysqlDependency;
    public function __construct($mysqlDependency)
    {
    $this->_mysqlDependency = $mysqlDependency;
    }
    public function fetchQueryFromDatabase($key)
    {
    return $this->_mysqlDependency-
    >fetchQueryEscaped("SELECT * FROM `myTable` WHERE `myKey`
    = '%s'",$key);
    }
    }
    No%
    cache

    View full-size slide

  42. class Data
    {
    private $_namespace = 'myNamespace_';
    private $_ttl = 3600;
    private $_mysqlDependency;
    public function __construct($mysqlDependency)
    {
    $this->_mysqlDependency = $mysqlDependency;
    }
    public function fetchQueryFromDatabase($key)
    {
    if(($value = apc_fetch(md5(
    $this->_namespace.$key))) === false){
    $value = $this->_fetchDataFromDatabase($key);
    $this->_storeDataInCache($key,$value);
    }
    return $value;
    }
    ...
    Override%
    &%use%cache
    Read%
    from%DB%&%
    store%in%cache

    View full-size slide

  43. ...
    private function _storeDataInCache($key,$value)
    {
    apc_store(md5($this->_namespace.$key),$value,
    $this->_ttl);
    return $this;
    }
    private function _fetchQueryFromDatabase($key)
    {
    return $this->_mysqlDependency-
    >fetchQueryEscaped("SELECT * FROM `myTable` WHERE `myKey`
    = '%s'",$key);
    }
    } Fetch%
    from%DB
    Store%
    in%cache

    View full-size slide

  44. You3can3also3use3Memcached

    View full-size slide

  45. You3can3also3use3Memcached
    memcached.sess_prefix = "memc.sess.key."
    session.save_path="127.0.0.1:11211"
    session.save_handler = memcached
    Also%
    for%session%
    handling

    View full-size slide

  46. Reverse3
    (caching)3
    proxies

    View full-size slide

  47. Put3‘em3before3
    your3
    webserver

    View full-size slide

  48. They3honor3
    your3caching3
    headers

    View full-size slide

  49. backend default {
    .host = "127.0.0.1";
    .port = "8080";
    }
    acl purge {
    "localhost";
    "127.0.0.1";
    "some.host.name.com";
    "1.2.3.4";
    }

    View full-size slide

  50. sub vcl_recv {
    if (req.request == "PURGE") {
    if (!client.ip ~ purge) {
    error 405 "Not allowed: " + client.ip;
    }
    return(lookup);
    }
    if (req.request != "GET" &&
    req.request != "HEAD" &&
    req.request != "PUT" &&
    req.request != "POST" &&
    req.request != "TRACE" &&
    req.request != "OPTIONS" &&
    req.request != "DELETE") {
    return (pipe);
    }

    View full-size slide

  51. if (req.request != "GET" && req.request != "HEAD") {
    return (pass);
    }
    if (req.http.Accept-Encoding) {
    if (req.url ~ "\.(gif|jpg|jpeg|swf|flv|mp3|mp4|
    pdf|ico|png|gz|tgz|bz2)(\?.*|)$") {
    remove req.http.Accept-Encoding;
    } else if (req.http.Accept-Encoding ~ "gzip") {
    set req.http.Accept-Encoding = "gzip";
    } else if (req.http.Accept-Encoding ~ "deflate"){
    set req.http.Accept-Encoding = "deflate";
    } else {
    remove req.http.Accept-Encoding;
    }
    }

    View full-size slide

  52. if (req.url ~ "\.(gif|jpg|jpeg|swf|css|js|flv|mp3|
    mp4|pdf|ico|png)(\?.*|)$") {
    unset req.http.cookie;
    set req.url = regsub(req.url, "\?.*$", "");
    return (lookup);
    }
    if (req.url ~ "\?(utm_(campaign|medium|source|term)|
    adParams|client|cx|eid|fbid|feed|ref(id|src)?|v(er|
    iew))=") {
    set req.url = regsub(req.url, "\?.*$", "");
    }
    if (req.http.cookie ~ "^ *$") {
    unset req.http.cookie;
    }

    View full-size slide

  53. sub vcl_fetch {
    if (req.url ~ "wp-(login|admin)" || req.url ~
    "preview=true" || req.url ~ "xmlrpc.php") {
    return (hit_for_pass);
    }
    if ( (!(req.url ~ "(wp-(login|admin)|login)"))) {
    unset beresp.http.set-cookie;
    set beresp.ttl = 1h;
    }
    if (req.url ~ "\.(gif|jpg|jpeg|swf|css|js|flv|mp3|
    mp4|pdf|ico|png)(\?.*|)$") {
    set beresp.ttl = 365d;
    }
    }

    View full-size slide

  54. sub vcl_deliver {
    if (obj.hits > 0) {
    set resp.http.X-Cache = "HIT";
    } else {
    set resp.http.X-Cache = "MISS";
    }
    }
    sub vcl_hit {
    if (req.request == "PURGE") {
    purge;
    set obj.ttl = 0s;
    error 200 "OK";
    }
    }
    sub vcl_miss {
    if (req.request == "PURGE") {
    purge;
    error 200 "OK";
    }
    }

    View full-size slide

  55. FPM
    hfp://www.php.net/manual/en/install.fpm.configuraGon.php

    View full-size slide

  56. [www]
    listen = 9000
    user = www-data
    group = www-data
    pm = dynamic
    pm.max_children = 10
    pm.start_servers = 4
    pm.max_spare_servers = 6
    pm.max_requests = 500
    php_admin_value[sendmail_path] = /usr/sbin/sendmail -t -i -f
    [email protected]
    php_flag[display_errors] = off
    php_admin_value[error_log] = /var/log/fpm-php.www.log
    php_admin_flag[log_errors] = on
    php_admin_value[memory_limit] = 32M

    View full-size slide

  57. upstream fpm {
    server php1.server.com:9000;
    server php2.server.com:9000;
    }
    upstream memcached {
    server memcached1.server.com:11211;
    server memcached2.server.com:11211;
    }
    server {
    root /var/www;
    index index.php index.html index.htm;
    server_name nginx.server.com;
    location / {
    try_files $uri $uri/ /index.php;
    }
    location @php {
    fastcgi_pass fpm;
    fastcgi_index index.php;
    include fastcgi_params;
    }

    View full-size slide

  58. location ~ \.php$ {
    set $memcached_key $request_uri;
    memcached_pass memcached;
    memcached_next_upstream not_found;
    default_type text/html;
    error_page 404 405 502 = @php;
    }
    location ~* \.(?:ico|css|js|gif|jpe?g|png)$ {
    expires max;
    add_header Pragma public;
    add_header Cache-Control "public, must-
    revalidate, proxy-revalidate";
    }
    }

    View full-size slide

  59. location ~ \.php$ {
    set $memcached_key $request_uri;
    memcached_pass memcached;
    memcached_next_upstream not_found;
    default_type text/html;
    error_page 404 405 502 = @php;
    }
    location ~* \.(?:ico|css|js|gif|jpe?g|png)$ {
    expires max;
    add_header Pragma public;
    add_header Cache-Control "public, must-
    revalidate, proxy-revalidate";
    }
    }

    View full-size slide

  60. Content
    Delivery
    Network

    View full-size slide

  61. +3
    W33Total3
    Cache

    View full-size slide

  62. ✓Enable3caching
    ✓Enable3cron.php
    ✓Configure3log3retenGon
    ✓Flat3catalog
    ✓Enable3compilaGon
    ✓Install3Varnish3Cache3plugin
    ✓Split3frontend3&3backend3servers
    ✓Perform3indexing3on3separate3
    server

    View full-size slide

  63. Boost
    Varnish
    Memcached

    View full-size slide

  64. Other3tricks3...

    View full-size slide

  65. Mess3with3the3hosts3file
    Test3load3&3change3docroot
    Play3with3Apache3&3MySQL3
    seBngs
    Add3a3database3index
    Enable3MySQL3query3caching

    View full-size slide

  66. Don’t3be3clueless

    View full-size slide

  67. Ask3for3help

    View full-size slide

  68. Apply3the3tricks

    View full-size slide

  69. And3don’t3forget3to3refactor3alerwards

    View full-size slide

  70. And3then3relax3...3and3be3happy

    View full-size slide