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 the DPC12 edition of my scalability talk.

Thijs Feryn

June 09, 2012
Tweet

More Decks by Thijs Feryn

Other Decks in Technology

Transcript

  1. View Slide

  2. Evangelist

    View Slide

  3. View Slide

  4. View Slide

  5. View Slide

  6. @ThijsFeryn

    View Slide

  7. Please3
    rate3my3talk3on3
    Joind.in
    https://joind.in/6247

    View Slide

  8. It3works3on3my3computer

    View Slide

  9. View Slide

  10. It’s3the3internet’s3fault

    View Slide

  11. Suits
    &
    hipsters

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  18. GeBng3the3numbers3right

    View Slide

  19. Let’s3do3a3complete3rewrite3!

    View Slide

  20. Have3you3got3the3Gme?

    View Slide

  21. Or3the3money?

    View Slide

  22. What3do3you3do?

    View Slide

  23. Nothing

    View Slide

  24. Throw3
    servers3at3
    the3
    problem

    View Slide

  25. Do3some3
    tricks3!

    View Slide

  26. Throw3servers3at3the3problem

    View Slide

  27. Scale3verGcally

    View Slide

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

    View Slide

  29. Scale3horizontally

    View Slide

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

    View Slide

  31. What3kind3of3infrastructure3do3you3need?

    View Slide

  32. What3kind3of3infrastructure3do3you3need?
    One3big3
    box?

    View Slide

  33. What3kind3of3infrastructure3do3you3need?
    Scale3across3
    1003nodes?

    View Slide

  34. What3kind3of3infrastructure3do3you3need?
    That3big3F53
    loadbalancer?

    View Slide

  35. What3kind3of3infrastructure3do3you3need?
    Only3the3DB3
    gets3
    hammered

    View Slide

  36. What3kind3of3infrastructure3do3you3need?
    Lots3of3staGc3
    traffic

    View Slide

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

    View Slide

  38. About3a3mulGYserver3setup

    View Slide

  39. Goal:3(almost)3no3code3changes

    View Slide

  40. Cache

    View Slide

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

    View Slide

  42. What?
    When3not?
    How3long?
    CompaGbility
    Limited3size

    View Slide

  43. Tricks

    View Slide

  44. 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 Slide

  45. 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 Slide

  46. APC

    View Slide

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

    View Slide

  48. 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 Slide

  49. 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 Slide

  50. Data3cache
    Caching3layer3
    before3on3top3
    of3the3
    database

    View Slide

  51. 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 Slide

  52. 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 Slide

  53. ...
    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 Slide

  54. You3can3also3use3Memcached

    View Slide

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

    View Slide

  56. Reverse3
    (caching)3
    proxies

    View Slide

  57. Put3‘em3in3
    front3of3your3
    webserver

    View Slide

  58. They3honor3
    your3caching3
    headers

    View Slide

  59. View Slide

  60. Request vcl_recv
    In cache?
    vcl_hash Cacheable?
    vcl_hit() vcl_miss()
    vcl_deliver()
    vcl_fetch()
    No
    Yes
    No
    Yes
    Response

    View Slide

  61. 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 Slide

  62. 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 Slide

  63. 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 Slide

  64. 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 Slide

  65. 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 Slide

  66. 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 Slide

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

    View Slide

  68. [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 Slide

  69. View Slide

  70. 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 Slide

  71. 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 Slide

  72. 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 Slide

  73. View Slide

  74. Content
    Delivery
    Network

    View Slide

  75. +3
    W33Total3
    Cache

    View Slide

  76. View Slide

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

    View Slide

  78. View Slide

  79. Boost
    Varnish
    Memcached

    View Slide

  80. Don’t3be3clueless

    View Slide

  81. Ask3for3help

    View Slide

  82. Apply3the3tricks

    View Slide

  83. And3don’t3forget3to3refactor3akerwards

    View Slide

  84. And3then3relax3...3and3be3happy

    View Slide

  85. View Slide