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: 55.857 ]

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

Squid3 no Debian 8 (Jessie) com suporte a filtro de páginas HTTPS

Squid autenticando em Win2000/2003 com Debian Etch

Limitando acesso ao Team Viewer com Squid e IPTables

Squid como proxy transparente: Instalando e configurando

Colocando senha nos gerenciadores do Squid (SARG e MYSAR) de forma simples

  
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




Patrocínio

Site hospedado pelo provedor RedeHost.
Linux banner

Destaques

Artigos

Dicas

Tópicos

Top 10 do mês

Scripts