ECache - O cache efetivo

Agora você terá um eficiente sistema de cache, que abrange vídeos do Youtube, arquivos do Windows Update e reescreve URLs para otimizar imagens do Orkut e updates de antivírus. Assim você pode ter um ganho de 30% ou mais em seu link e economia de tráfego.

[ Hits: 56.938 ]

Por: Rodrigo Manga em 29/10/2008


Scripts



Aqui está o código dos scripts necessários ao sistema:

rewriter.php

#!/usr/bin/php
<?PHP
//error_reporting(0);

include "adm.php";
$exit = false;

$f = fopen('php://stdin','r');

while ($exit == false) { // loop for wait squid url

   // get url from squid
   $url = fgets($f);
   $url = explode(" ",$url);
   $ip =  $url[1];
   $url = $url[0];
   $url = explode("\n",$url);
   $url = $url[0];

   if ($url == "") { // squid exiting...
      logadd("exiting...");
      exit;
   } else {
      logadd("IN:($ip)$url");

      if (preg_match("/^http:\/\/dl[1-9]\.avgate\.net\.*/", $url, $resultado)) {
        $url = preg_replace("/\/dl[0-9]\./", "/dl7.", $url);
        print "$url\n";
        logadd("OUT:$url");
      } else if (preg_match("/^http:\/\/download[0-9]{3}\.avast\.com.*/", $url, $resultado)) {
        $url = preg_replace("/\/download[0-9][0-9][0-9]\./", "/download626.", $url);
        print "$url\n";
        logadd("OUT:$url");
      } else if (preg_match("/^http:\/\/akamai\.avg\.com.*/", $url, $resultado)) {
         $url = preg_replace("/\/akamai\./", "/downloadfree.", $url);
         print "$url\n";
         logadd("OUT:$url");
      } else if (preg_match("/^http:\/\/update\.avg\.com.*/", $url, $resultado)) {
         $url = preg_replace("/\/update\./", "/guru.", $url);
         print "$url\n";
         logadd("OUT:$url");
      } else if (preg_match("/^http:\/\/img[2-9]\.orkut\.com.*/", $url, $result)) {
         // ############################### ORKUT
         $url = preg_replace("/\/img[0-9]\./", "/img1.", $url);
         print "$url\n";
         logadd("OUT:$url");
      } else if (preg_match("/\.windowsupdate\.com\//", $url,$result)){
         // ############################### WINDOWS UPDATE
  
         // get file name
         $file = get_filename($url);
         if ($file != "") {
            check_file($file,$url);
         } else { // dont find file, repass url
            print "$url\n";
            logadd("OUT:$url");
         }
      } else if (preg_match("/\.youtube\.com/", $url,$result)){
         // ############################### YOUTUBE
         // get  videoid
         $videoid = get_videoid($url);
         // get quality
         $fmt = get_quality($url);
         $file="$fmt$videoid.flv";
         // check if url need to pass
         if (($file != "") and (strrpos($url,"/get_video?") > 0)) {
            check_file($file,$url);
         } else { // dont find file, repass url
            print "$url\n";
            logadd("OUT:$url");
         }
      } else {
         // url not match
         print "$url\n";
         logadd("OUT:$url");
      }
   }
}
fclose($f);
?>

adm.php

<?PHP
//constants
$cache_log = "/tmp/rewriter.log";
$cache_dir = "/var/www/ecache";
$cache_limit = 1024*1024*1024*30;// em bytes! 1024*1000*20 = 20 Mb
$cache_scr = "/etc/squid3";
$mysql_host = "localhost";
$mysql_user = "root";
$mysql_pass = "senhazim";
$server_ip = "10.0.1.1:8080";
$cache_url = "http://$server_ip/ecache";
$packet_size = 200;   // in bytes
$packet_delay = 2000;
// time in ms
$con = mysql_pconnect($mysql_host,$mysql_user,$mysql_pass) or die("error mysql");
mysql_select_db("ecache",$con) or die("error DB");

function logadd($line) {
   global $cache_log;
   $flog = fopen("$cache_log",'a');
   fwrite($flog,date("H:m:s ").$line."\n");
   fclose($flog);
}

      
function web2mysql($data){
   $mes = array("Jan" => 1, "Feb" => 2, "Mar" => 3, "Apr" => 4, "May" => 5, "Jun" => 6, "Jul" => 7, "Aug" => 8, "Sep" => 9, "Oct" => 10, "Nov" => 11, "Dec" => 12);
   $data = explode(" ",$data);
   return $data[3]."-".$mes[$data[2]]."-".$data[1]." ".$data[4];
}

function get_filename($url) {
   $url = preg_replace("/\?/","&",$url);
   $url = preg_replace("/&/","//",$url);
          
   if (preg_match("/[0-9]{10}$/", $url, $resultado)) {
      // metadados
      $url = explode("/",$url);
      return $url[(count($url)-3)];
   } else {
      // arquivos do winupdate
      $url = explode("/",$url);
      return $url[(count($url)-1)];
   }
}

function get_videoid($url) {
   $urla = preg_replace("/\?/","&",$url);
   $urla = explode("&",$urla);
   $ifim=count($urla);
   for ($iini=0;$iini<=$ifim;$iini++) {
      $urlnn=explode("=",$urla[$iini]);
            
      if (trim($urlnn[0]) == "video_id"){
         $ok=$iini;
         $vid=$urlnn[1];
      }
   }

   return $vid;
}

function get_quality($url) {
   $urla = preg_replace("/\?/","&",$url);
   $urla = explode("&",$urla);
   $ifim=count($urla);
   for ($iini=0;$iini<$ifim;$iini++) {
      $urlnn=explode("=",$urla[$iini]);
            
      if (trim($urlnn[0]) == "fmt") {
         //blz achou fmt
         $ok=$iini;
         $fmt=$urlnn[1];
      }
   }

   return $fmt;
}
      
function delete_file($filename){
   global $cache_dir;
   $filepath = "$cache_dir/$filename";
          
   if (file_exists($filepath)) {
      unlink ($filepath);
   }

   $query = "UPDATE ecache SET deleted=1, ndeleted=ndeleted+1 WHERE file='$filename'";
   mysql_query($query);
   loga("Deleted $filename");
}
      
function check_limit($filesize) {
   global $cache_limit;
   $total = 999999999999999999999;
   while (($total+$filesize) > $cache_limit) {
      $query = "SELECT SUM(size) FROM ecache";
      $result = mysql_query($query);
      $nresults = mysql_num_rows($result);
            
      if ($nresults > 0) list($total) = mysql_fetch_row($result);
            
      if (($total+$filesize) > $cache_limit) {
         $query = "SELECT file FROM ecache WHERE deleted=0 order by `last_request` asc,`requested` desc, ndeleted asc limit 1";
         $result = mysql_query($query);
         $nresults = mysql_num_rows($result);
                
         if ($nresults == 1) {
            list($file) = mysql_fetch_row($result);
            deleta_video($file);
         } else break;
      }

   }
}

function my_get_headers($url ) {
   $url_info=parse_url($url);
          
   if (isset($url_info['scheme']) && $url_info['scheme'] == 'https') {
      $port = 443;
      @$fp=fsockopen('ssl://'.$url_info['host'], $port, $errno, $errstr, 10);
   } else {
      $port = isset($url_info['port']) ? $url_info['port'] : 80;
      @$fp=fsockopen($url_info['host'], $port, $errno, $errstr, 10);
   }

   if($fp) {
      stream_set_timeout($fp, 10);
      $head = "HEAD ".@$url_info['path']."?".@$url_info['query'];
      $head .= " HTTP/1.0\r\nHost: ".@$url_info['host']."\r\n\r\n";
      fputs($fp, $head);
      while(!feof($fp)) {
                
         if($header=trim(fgets($fp, 1024))) {
            $sc_pos = strpos( $header, ':' );
                  
            if( $sc_pos === false ) {
               $headers['status'] = $header;
            } else {
               $label = substr( $header, 0, $sc_pos );
               $value = substr( $header, $sc_pos+1 );
               $headers[strtolower($label)] = trim($value);
            }
         }
      }
            
      if ((strpos($headers['status'],'302') === false) and (strpos($headers['status'],'303') === false)) {
         return $headers;
      } else {
         return my_get_headers($headers['location']);
      }

   } else {
      return false;
   }
}

function check_file($file,$url) {
   global $cache_dir, $cache_scr, $cache_url;
   // check if file is on DB
   $query = "SELECT file FROM ecache WHERE file='$file'";
   $result = mysql_query($query);
   $nresults = mysql_num_rows($result);
          
   if (($nresults == 0) or (!file_exists("$cache_dir/$file"))){
      // get size and last modified of file
      $headers = my_get_headers($url);
      $size = $headers["content-length"];
      $last_modified = web2mysql($headers["last-modified"]);
            
      if ($size == "") {
         logadd("Error when get file header $file");
         print "$url\n";
      } else {
         if ($nresultados == 0){
            $query = "INSERT INTO ecache(file,size,modified,last_request)";
            $query .= "values('$file',$size,'$last_modified',now())";
            mysql_query($query);
         } else {
            $query = "UPDATE ecache SET deleted=0, downloaded=0 where file='$file'";
            mysql_query($query);
         }

         // download in background
         system("($cache_scr/downloader.php ".'"'.$url.'") > /dev/null &');
         // rewrite url
         print "$cache_url.php?file=$file\n";
         logadd("MISS:$cache_url.php?file=$file");
      }

   } else {
      // if file exists
      $query = "SELECT size,dowloaded FROM ecache WHERE file='$file'";
      $result = mysql_query($query);
      list($size,$downloaded) = mysql_fetch_row($result);
            
      if ($size == $downloaded){
         // if download is completed
         print "$cache_url/$file\n";
         logadd("HIT:$cache_url/$file");
                
         // update hit                      
         $query = "UPDATE ecache SET requested=requested+1, last_request=now() where file='$file'";
         mysql_query($query);
      } else {
         // while downloading...
         print "$cache_url.php?file=$file\n";
         logadd("MISS:$cache_url.php?file=$file");
      }
   }
}
?>

downloader.php

#!/usr/bin/php
<?php
error_reporting(0);
include "adm.php";

function update_db(){
   global $total,$file;
   $query = "UPDATE ecache SET downloaded=$total where file='$file'";
   mysql_query($query);
}

// check parms
$url = "";
for ($i = 1; $i <= $argc; $i++) {
   $url .= $argv[$i];
}

if ($url == ""){
   print "Check parms\n";
   logadd("dw:Check parms");
   exit(1);
} else {
   if (preg_match("/\.windowsupdate\.com\//", $url,$result)) {
      // get filename
      $file = get_filename($url);
   } else
      if (preg_match("/\.youtube\.com/", $url,$result)) {
         $videoid = get_videoid($url);
         // get quality
         $fmt = get_quality($url);
         $file="$fmt$videoid.flv";
      }

      $query = "SELECT size,downloaded FROM ecache where file='$file'";
      $result = mysql_query($query);
      $nresults = mysql_num_rows($result);
          
      if ($nresults == 0){
         print "Where are registry ind DB?\n";
         logadd("Dont find file registry: $file");
         exit(1);
      } else {
         list($size,$downloaded) = mysql_fetch_row($result);
      }

      print "$size/$downloaded:";
      logadd("downloading $file");
      touch("$cache_dir/$file"); // create file
      chmod("$cache_dir/$file",0666);// change permissions
      $total = 0;

      while ($size != $total) {
         $fIN  = fopen($url,'r');
         $fOUT = fopen("$cache_dir/$file", 'w');
         $last_mark = microtime(true);
         while (!feof($fIN)) {
            $buffer = fread($fIN, 1024*8);
            $tbuffer = strlen($buffer);
                
            if ($tbuffer > 0) {
               $total += $tbuffer;
               fwrite($fOUT,$buffer);
               set_time_limit(1);
                  
               if (microtime(true)-$last_mark > 1) {
                  $last_mark = microtime(true);
                  update_db();
               }
            }
         }

         fclose($fIN);
         fclose($fOUT);
      }

      update_db();
      print "\n$size/$total\n";
      loga("END! $file");
   }
}
?>

ecache.php

<?php
error_reporting(0);
include "/etc/squid3/adm.php";
$url = "";
for ($i = 1; $i <= $argc; $i++) {
   $url .= $argv[$i];
}
      
if($url != "") {
   $file = $url;
} else {
   $file = $_GET["file"];
          
   if ($file == "") {
      print "Check parms\n";
      exit(1);
   }
}

// wait for file exists
while (!file_exists("$cache_dir/$file")) {
   sleep(1);
}

$query = "SELECT size,downloaded FROM ecache where file='$file'";
$result = mysql_query($query);
$nresults = mysql_num_rows($result);
list($size,$downloaded) = mysql_fetch_row($result);
$query = "UPDATE ecache SET requested=requested+1, last_request=now() where file='$file'";
mysql_query($query);
// TODO: check if file have new modification
//config headers
session_cache_limiter('nocache');
header("Expires: Thu, 19 Nov 1981 08:52:00 GMT");
header("Last-Modified: " . gmdate("D, d M Y H:i:s") . " GMT");
/* HTTP/1.1 clients and the rest (MSIE 5) */
header("Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0");
/* HTTP/1.0 clients */
header("Pragma: no-cache");
header("Content-Type: application/octet-stream");
header("Content-Disposition: attachment; filename=\"" . $file. "\"");
header("Content-Length: $size");
// send file
$f = fopen("$cache_dir/$file",'r');
$total = 0;
$last_mark = microtime(true);
while ($total < $size) {
   usleep($packet_delay);
          
   if ((($total+$packet_size) >= $downloaded) and ($size != $downloaded)) {
      // if buffer overflow
      $tbuffer = ($downloaded-$total)-2;
   } else $tbuffer = $packet_size;
          
   if ($tbuffer > 0) {
      $buffer = fread($f, $tbuffer);
      print $buffer;
      set_time_limit(1);
      $total += strlen($buffer);
   }
          
   if (($size != $downloaded) and ((microtime(true)-$last_mark) > 1)) {
      $ultimo_tempo = microtime(true);
      $query ="SELECT downloaded FROM ecache WHERE file='$file'";
      $result = mysql_query($query);
      $nresults = mysql_num_rows($result);
            
      if ($nresults != 0){
         list($downloaded) = mysql_fetch_row($result);
      }
   }

}

fclose($f);
?>

ecache_db.sql

CREATE DATABASE IF NOT EXISTS `ecache`
DEFAULT CHARACTER SET utf8 COLLATE utf8_bin;
USE ecache;

CREATE TABLE IF NOT EXISTS `ecache` (
  `file` varchar(50) NOT NULL,
  `size` int(10) unsigned NOT NULL default '0',
  `downloaded` int(10) unsigned NOT NULL default '0',
  `modified` datetime NOT NULL,
  `requested` int(10) unsigned NOT NULL default '0',
  `last_request` datetime NOT NULL,
  `deleted` tinyint(3) unsigned NOT NULL default '0',
  `ndeleted` int(10) unsigned NOT NULL default '0',
  PRIMARY KEY  (`file`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;

Página anterior     Próxima página

Páginas do artigo
   1. O sistema
   2. Scripts
   3. Arquivos de configuração
Outros artigos deste autor

Thunder Cache - Cache inteligente

Leitura recomendada

Bloqueando Windows Live Messenger com Squid (Debian ou Ubuntu)

Squid balanceado com LVS

Squid 3.1.5 com suporte a TPROXY (sem bridge)

Bloqueando conteúdo com Squid no RedHat 9

SquidGuard: o bloqueador de listas para o Squid

  
Comentários
[1] Comentário enviado por rodrigomanga em 29/10/2008 - 11:58h

Pessoal, os scripts que foram postados já estão desatualizados e com bugs, por favor, entrem em www.biazus.com e peguem os novos scripts

[2] Comentário enviado por arium em 29/10/2008 - 12:05h

As habilidades do Rodrigo em php for Extremamente úteis!, além da portabilidade que ele conseguiu! a utilização do mysql possibilitou diversos controles antes não existentes!!!! nota 10 pro Rodrigão! valeu brother!

[3] Comentário enviado por brunocontin em 29/10/2008 - 15:08h

amigo essa configuração do squid.conf, deve ser colocada em qual posição no .conf, pois no meu mesmo colocando antes do deny all, ele fecha tudo.

[4] Comentário enviado por reng.unip em 29/10/2008 - 17:18h

Parabéns pelo artigo!!!

Abraço...

[5] Comentário enviado por powerd0wn em 04/11/2008 - 14:23h

Fala, xará... tudo bom?

Cara, gostei muito do seu artigo, mas a dúvida é a seguinte:

Você faz alguma validação se o arquivo foi alterado?

Pelo que entendi, você apenas verifica se a url já se encontra em cache e, caso sim, direciona para o cache já feito. Mas, digamos que a url seja a mesma, somente o conteúdo seja alterado. Como você faz pra controlar/verificar isso?

Abraços,

Rodrigo Martins

[6] Comentário enviado por rodrigomanga em 05/11/2008 - 00:56h

ainda não faz, é simples de fazer, na próxima versão já deve estar implementado.

[7] Comentário enviado por dailson em 13/11/2008 - 12:16h

Amigo. Estou tentando pegar os novos códigos, porém na página indicada só tem um link para um fórum onde eu não consigo me registrar, pois a imagem para confirmação não aparece.
Você poderia ajudar:???

[8] Comentário enviado por chiareloto em 17/12/2008 - 23:42h

Amigo fiz conforme o passo a passo mais quando deixo habilitado no squid a funcao .youtube.com ele nao consegue abrir os videos do youtube...o que pode estar errado...

[9] Comentário enviado por celso annes em 16/01/2009 - 17:56h

Check parms esta me dando esse erro o que pode ser?

[10] Comentário enviado por lucasmcarlos em 27/05/2009 - 16:08h

kra ... legal seu post ... porem aki em casa não deu muito certo não ... para fala bem a verdade não funcionou nada ....
qdo eu fiz todos os passos q vc pediu ...
mais qdo restartei o squid ... jah era não navegou mais ... ai fui lah no squid.conf #desativei as linhas , restartei voltou a navegar ...

então fica assim .. qdo coloca suas regras do squid.conf para rodar ... para a navegação, qdo tiro volta ao normal ... vc sabe oq pode estar acontecendo ...

obrigado

[11] Comentário enviado por csguerreiro em 17/09/2009 - 22:23h

Grande rodrigo, vc relatou que para usar o thunder cache no freebsd existem algumas alterações poderia então me ajudar descrevendo quais as alterações necessárias, pois uso freebsd, e achei que o squid trabalhou melhor fazendo cache do que o ubuntu, e queria implementar o thunder no freebsd 7.2,caso puder contribuir agradeço. comentário postar e caso puder envie um email cleiton@gnex.com.br. E claro, o funcionamento do thunder é muito bom mesmo.
Grande abraço,

[12] Comentário enviado por fabioholliday em 17/12/2012 - 17:43h

Me tire uma duvida, la onde tem "opções de redirecionamento, onde eu colco o conteúdo dentro do squid.conf ? pois fiquei nesta duvida...

[13] Comentário enviado por evertoncl em 11/04/2013 - 21:38h

Alguém ai ainda esta utilizando ou pode me dizer se ainda funciona??
caso não esteja funcionando indicar uma outra solução


Contribuir com comentário