Flush immediato...

Mi sto perdendo in un bicchier d’acqua. :slight_smile:

Ho un piccolo script che recupera da un server remoto dei dati e li elabora sul mio server web inserendoli in un db

La procedura completa impiega dai 20 ai 90 secondi.

All’interno della procedura ho inserito vari echo per monitorare gli step della stessa. Ho pensato di aggiungere flush(); per veder apparire subito i dati sul client, ma questi vengono mostrati solo al termine di tutta la procedura.

Quindi mi sto domandando se su YII ci sia un altro modo per inviare direttamente i dati al browser durante l’elaborazione. Ho provato a cercare, ma non ho trovato soluzioni di sorta. Dove sta quindi l’inghippo?

Grazie a chi mi indirizza sulla giusta via :slight_smile:

Marco

Da quello che dici, a me sembra che l’inghippo sia in questo server remoto. Voglio dire… se la pagina ci mette tanto ad arrivare il problema sta li. Secondo me, senza il bisogno di echo, potresti fare girare questa pagina in background, magari con una chiamata ajax. … ed aspettare che carichi.

Intanto grazie per la risposta

Metto il codice cosi magari si capisce meglio :smiley:




public function actionImportaCategorie()

  {

    $y = 0;

    $x = 0;

    $nTempo = GetMicroTime();

    echo "<b>Recupero dati categorie</b> ... (".number_format(GetMicroTime()-$nTempo,2)." sec.)<br />";flush();


    $sSql .= file_get_contents('http : / / www. SITO_REMOTO. com/crea_sql.asp?tab=categorie');


    echo "Vettorializzazione dati categorie ... (".number_format(GetMicroTime()-$nTempo,2)." sec.)<br />";flush();


    $aSql = explode("\n",$sSql);

    echo "Inizio inserimento/aggiornamento DB ... (".number_format(GetMicroTime()-$nTempo,2)." sec.)<br />";flush();


    $connection=Yii::app()->db;

    $command=$connection->createCommand("SET foreign_key_checks = 0;");

    $rowCount=$command->execute();

    #$command=$connection->createCommand("TRUNCATE categorie;");

    #$rowCount=$command->execute();


    echo count($aSql)." aggiornamenti da effettuare... (".number_format(GetMicroTime()-$nTempo,2)." sec.)<br />";flush();


    foreach ($aSql as $key=>$value) {

      if (trim($value)!='') {

        $command=$connection->createCommand($value);

        $rowCount=$command->execute();

      }

      $y++;

      $x+=$rowCount;

      if ( ( $y % 500)==0 ) {

      	echo " - Aggiornate ".$y." righe con ".$y." query.(".number_format(GetMicroTime()-$nTempo,2)." sec.)<br />";flush();

      }

    }

    $command=$connection->createCommand("SET foreign_key_checks = 0;");

    $rowCount=$command->execute();

  	echo " - Aggiornate ".$y." righe con ".$y." query.(".number_format(GetMicroTime()-$nTempo,2)." sec.)<br />

      Importazione categorie completata.<br /><br />";

	}



Il ciclo FOREACH dovrebbe restituirmi un ECHO ogni 500 righe, ma tutti gli ECHO (anche quelli iniziali) mi vengono visualizzati solo al completamento di tutta la funzione.

L’altro server risponde in 1 secondo. Il tempo dello script è tutto nel ciclo FOREACH.

Cosa sbaglio?

Per completezza ecco le righe dell’output:

Ecco queste righe appaiono solo una volta completato il processo e non durante l’esecuzione.

Lo sbaglio è il sito di origine che è in asp. No dai scherzo. Guarda, ragionando ad oggetti ho imparato che il codice deve fare una cosa alla volta. Quindi, per farla breve, secondo me devi togliere le echo da questa pagina, ed usarne un’altra che restituisce la stringa “aggiornate … … …”. Quello che ci hai mostrato io lo lancerei in background senza controllare nulla.

tornando all’inizio di questo intervento… actionImportaCategorie servirà appunto per importare le categorie mentre un’ipotetica actionVerificaAggiornamenti farebbe un “count di la” … e ti restituirebbe il lavoro fatto.

Questo avrebbe anche lo scopo di isolare i contesti ed avere un codice più pulito. Però è un po’ diverso da quello che mi hai chiesto. Funziona! Io, per esempio, nelle mie GUI cerco sempre di strutturare delle chiamate ajax del tipo Controller::action() che restituiscono dei json. la GUI richiede l’informazione, e con jquery animo la pagina. Ciascun comportamento ha un suo piccolo javascript ed un suo piccolo php.

Anche se non ho trovato la TUA soluzione, spero di averti dato degli spunti per trovarne di NUOVE.

L’ho detto che mi stavo perdendo in un bicchier d’acqua … appena riesco provo e ti faccio sapere com’è andata :slight_smile: intanto grazie.

Anche io ho avuto esperienze di questo tipo, e il flush funziona in una maniera un po’ strana.

In teoria funziona, ma in pratica sulla mia macchina di sviluppo e sul server c’erano evidenti differenze di tempo.

Secondo me se ci fosse piu’ output funzionerebbe meglio.

Ovviamente il consiglio di sensorario e’ ottimo, io non ho mai fatto cosi’ perche’ sono troppo pigro, ma la strada corretta e’ quella che dice lui.

Rieccomi come promesso ho trovato il tempo di riscrivere la procedura … e sto godendo come un riccio … a dire il vero ho goduto di più per il 57% al referendum 2011, ma quello è un altro discorso.

Veniamo a noi.

Per fare quanto proposto da sensoriano non ho potuto usare le sessione in quanto vengono salvate alla fine del processo in background.

Mi son rigirato quindi su una tabella.

Inspiegabilmente (anzi se mi illuminate son contento!) il seguente codice non va:




      $objAvanzamento = new T2Avanzamento;

      $objAvanzamento->Azione = $sTab;

      $objAvanzamento->Valore = "11111222223333";

      $objAvanzamento->StatoAvanzamento = 1;

      $objAvanzamento->save();



in quanto non inserisce nessun record, ma con createCommand ho risolto la situazione

PS: la cosa triste è che il codice sopracitato in altri punti funziona :-S

Ma veniamo ai vari pezzi in modo ordinato cosi i posteri italiani potranno trovare la via aperta per gestire la situazione.

Prima facciamo la tabellina dove salvare le varie azioni:




CREATE TABLE `t2_avanzamento` (

  `Azione` char(50) NOT NULL,

  `Valore` text NOT NULL,

  `StatoAvanzamento` tinyint(3) unsigned NOT NULL default '0' COMMENT '0=finito; 1=in avanzamento;',

  PRIMARY KEY  (`Azione`)

);



Ho usato un tinyint per lo Stato … si sa mai che in futuro invece di gestire solo 2 stati ne possano esser gestiti di più …

Poi passiamo ad scrivere 2 righette di JS per richiamare il processo di background e impostare il controllo periodico dell’avanzamento.




      <script type="text/javascript">


        var sTab; 


        function aggiornaOutput() {

          $("#outputComando").load("/a/categorieImportaAvanzamento/"+sTab);

        } 


        $(".comando a").click(function() {

          var interval = setInterval("aggiornaOutput()", 1000);

          sTab = $(this).attr("title");

          $("#outputComando").load($(this).attr("href"), function(response, status, xhr) {

            clearInterval ( interval );

            aggiornaOutput();

          });

          return false;

        });


      </script>



Il JS ha una funzione aggiornaOutput che viene richiamata ogni secondo per ricevere l’avanzamento del lavoro lanciato con click sul link. Completato il caricamento della pagina viene cancellato il ciclo di aggiornamento e richiamato un’ultima volta la funzione per esser sicuro di avere il risultato finale dell’avanzamento.

Passiamo ora a gestire le action inserendo 3 funzioncine:




public function aggiornaAvanzamento( $objConn, $sTab, $sAvanzamento, $bFinito ){

    $objComm  = $objConn->createCommand( "REPLACE INTO t2_avanzamento

      (Azione, Valore, StatoAvanzamento) VALUES

      ('".$sTab."','".$sAvanzamento."',".$bFinito.");");

    $rowCount = $objComm->execute();

}



Questa funzioncina facile facile è solo per tenere più pulito il codice e si incarica di aggiornare la tabella. Avrei preferito usare il primo codice citato, ma non capisco come mai non salvi un picchio :-(. Se vedete qualche errore che mi sfugge fatemi sapere :slight_smile:

Procediamo con la procedura che deve girare in background al click sul link:




public function actionCategorieImporta()

  {

    $objConn  = Yii::app()->db;

    $sTab = $_GET['tab'];

    $sAvanzamento = '';

    $y = 0;

    $x = 0;

    $sSql = '';


    $objAvanzamento = new T2Avanzamento;

    $objRecord = $objAvanzamento->findByPk($sTab);


    if ( is_null($objRecord) || $objAvanzamento->StatoAvanzamento==0 ) {

      $nTempo = GetMicroTime();

      $sAvanzamento = "<b>Inizio recupero dati per ".$sTab."</b> ... (".number_format(GetMicroTime()-$nTempo,2)." sec.)<br />";

      self::aggiornaAvanzamento($objConn,$sTab,$sAvanzamento,1);


      $sSql .= file_get_contents(URL_REMOTO.'/crea_sql.asp?tab='.$sTab);


      $sAvanzamento .= "Vettorializzazione dati ... (".number_format(GetMicroTime()-$nTempo,2)." sec.)<br />";

      self::aggiornaAvanzamento($objConn,$sTab,$sAvanzamento,1);

      $aSql = explode("\n",$sSql);


      $sAvanzamento .= "Inizio inserimento/aggiornamento DB ... ".count($aSql)." aggiornamenti da effettuare... (".number_format(GetMicroTime()-$nTempo,2)." sec.)<br />";

      self::aggiornaAvanzamento($objConn,$sTab,$sAvanzamento,1);


      $objComm  = $objConn->createCommand("SET foreign_key_checks = 0;");

      $rowCount = $objComm->execute();


      foreach ($aSql as $sSql) {

        if (trim($sSql)!='') {

          $objComm  = $objConn->createCommand($sSql);

          $rowCount = $objComm->execute();

        }

        $y++;

        $x += $rowCount;

        if ( ( $y % 1000)==0 ) {

          $sAvanzamento .= " - Aggiornate ".$y." righe con ".$y." query.(".number_format(GetMicroTime()-$nTempo,2)." sec.)<br />";

          self::aggiornaAvanzamento($objConn,$sTab,$sAvanzamento,1);

        }

      }

      $objComm  = $objConn->createCommand("SET foreign_key_checks = 1;");

      $rowCount = $objComm->execute();

      $sAvanzamento .= " - Aggiornate ".$y." righe con ".$y." query.(".number_format(GetMicroTime()-$nTempo,2)." sec.)<br /><br /><b>IMPORTAZIONE COMPLETATA!</b><br /><br />";


      self::aggiornaAvanzamento($objConn,$sTab,$sAvanzamento,0);

    }

  }



Controllo che non sia già in esecuzione la stessa operazione e poi salvo nel DB tutti i passaggi.

Non rimane quindi che la risposta alle richieste sull’avanzamento fatte ogni secondo(tempo medio di spazientimento dell’utente):




public function actionCategorieImportaAvanzamento()

  {

    $sTab = $_GET['tab'];

    $objAvanzamento = new T2Avanzamento;

    $objRecord = $objAvanzamento->findByPk($sTab);

    echo $objRecord->Valore;

  }



RISULTATO FINALE:

Ora voi lo vedete tutto assieme … io mi son goduto l’apparir delle singole righe ogni secondo :stuck_out_tongue:

Che dite? Potevo fare diversamente in qualche punto? Qualcosa di migliorabile? Ogni suggerimento è ben accetto :slight_smile:

Saluti

Marco (SE&O)

Mi avevano detto funzionava il php … e non funzionava.

Alla fine ho dovuto pure imparare i comandi MsSql :slight_smile:

Vabbè devo solo copiare dei dati per qualche settimana, poi si passa sui nuovi server Linux con PHP e Mysql :wink:

Saluti

Marco (SE&O)

La parte migliorabile è senz’altro nell’astrazione del codice. E’ un concetto un po’ strano: detto in un italiano migliore: secondo me ora dovresti cercare di scrivere codice più integrato in Yii. Non farlo non è grave. Mettiti nei panni di un’altra persona che conosce Yii ma non conosce te e non può parlare con te. Se deve mettere mano sul tuo codice non sa che pattern hai usato.

L’uni consiglio chiaro che posso darti è questo:




    $.getJSON('index.php?json=json/buildable',function(data){

        alert(data.campo);

    });



Colgo anche l’occasione per mostrarvi un browser game dove faccio un uso pesante (troppo pesante ed al momento anche mal gestito) di questa architettura:

https://github.com/sensorario/yagolands/blob/master/javascript/yago.js

Il front-end fa delle chiamate che restituiscono un json. Ciascuna chiamata mi da json differenti. Lo stesso front-end, javascript, modifica la pagina. Anzi … visto che un paio di giorni fa ho deciso di rendere open source il mio browser game =) se vi va di forkare, sviluppare con me a me farebbe piacere. Non è basato su yii ma mi sono lasciato ispirare da alcuni concetti.

Uno dei motivo per cui ho cominciato ad usare YII è proprio quello di avere una base comune su cui lavorare in gruppo su alcuni progetti opensouce che sto pensando di fare…però non ho capito, seppur sia il mio intento, quello che intendi. Ossia da un punto di vista pratico puoi illuminarmi più chiaramente sulle migliorie di cui parli?

Ovviamente chiunque si voglia cimentare in esempi esplicativi e migliorie è ben accetto … tanto per cominciare a parlar la stessa lingua :smiley: