it-swarm.com.ru

Загрузка данных в браузер пользователя с сервера

Я хочу перенести данные с сервера в браузер. Мне уже известна функция php ob_flush(), которая отправляет выходной буфер. Мне нужна помощь с небольшим количеством логики. Я использую API-интерфейс Facebook в реальном времени, поэтому я хочу отправлять данные пользователю каждый раз, когда Facebook посещает мой сайт.

Вот мой код, который я пытаюсь отправить данные в браузер, но он не работает.

<?php
header('Access-Control-Allow-Origin: *');
header('Content-Type: text/event-stream');
ini_set("log_errors", 1);
ini_set("error_log", "php-error.log");
error_log( "LOGS STARTS FROM HERE" );
if(isset($_GET['hub_challenge'])){
    echo $_GET['hub_challenge'];    
}
if($_SERVER['REQUEST_METHOD'] == "POST"){
    $updates = json_decode(file_get_contents("php://input"), true); 
    // Replace with your own code here to handle the update 
    // Note the request must complete within 15 seconds.
    // Otherwise Facebook server will consider it a timeout and 
    // resend the Push notification again.
    print_r($updates);
    ob_flush();
    flush();
    //file_put_contents('fb.log', print_r($updates,true), FILE_APPEND);     
    //error_log('updates = ' . print_r($updates, true));              
}
?>
21
Akash Kumar

Как подсказал @som, вы можете просто использовать запросы с интервалом между ними, вам не нужно использовать сокеты.

Но дело в том, что вы пытаетесь получить данные из API и сразу передать их в браузер. Лучше всего разделить эти два шага.

В сценарии, который получает данные от Facebook, сохраните эти данные в базе данных или где-то еще:

if($_SERVER['REQUEST_METHOD'] == "POST"){
    $updates = json_decode(file_get_contents("php://input"), true); 

    insertDataToDatabase($updates); // you'll have to implement this.
}

Затем настройте страницу мониторинга:

monitor.php

<script>
lastId = 0;

$(document).ready(function() {
    getNewDataAtInterval();
});

function getNewDataAtInterval() {
    $.ajax({
        dataType: "json",
        url: "getData.php",
        data: {lastId: lastId}
    }).done(function(data) {
        for(i=0; i<data.messages.length; i++) {
            $("#messages").append("<p>" + data.messages[i]['id'] + ": " + data.messages[i]['message'] + "</p>");
            if (data.messages[i]['id'] > lastId) lastId = data.messages[i]['id'];
        }

        setTimeout(getNewDataAtInterval, 10000);
    }).fail(function( jqXHR, textStatus ) {
        alert( "Request failed: " + jqXHR.responseText );
    });
}
</script>

<div id="messages"></div>

Наконец, создайте серверный сценарий для возврата JSON с новыми сообщениями, загруженными из базы данных.

getData.php

$lastId = $_GET['lastId'];
$newMessages = getUpdatesFromDatabase($lastId);

exit(json_encode(array("messages"=>$newMessages)));

function getUpdatesFromDatabase($lastId) {
    // I'm using this array just as an example, so you can see it working.
    $myData = array(
        array("id"=>1,"message"=>"Hi"),
        array("id"=>2,"message"=>"Hello"),
        array("id"=>3,"message"=>"How are you?"),
        array("id"=>4,"message"=>"I'm fine, thanks")
    );

    $newMessages = array();
    foreach($myData as $item) {
        if ($item["id"] > $lastId) {
            $newMessages[] = $item;
            $newLastId = $item["id"];
        }
    }

    return $newMessages;
}
8
Marcos Dimitrio

Использование кометы или прототипа будет лучшей практикой здесь. Ajax увеличит нагрузку на сервер. Он будет часто опрашивать сервер. Вот некоторая необходимая информация об использовании кометы.

Вот пример того, как реализовать комету с php для отправки данных в реальном времени.

3
Pratik Soni

Я нашел Pusher и Redis для Push данных с сервера на браузер, пока искал лучшее решение.

Толкатель

Вы можете удобно использовать трансляцию событий, используя драйвер Pusher, используя JavaScript SDK Pusher.

this.pusher = new Pusher('pusher-key');

this.pusherChannel = this.pusher.subscribe('reference_id');

this.pusherChannel.bind('SomeEvent', function(message) {
    console.log(message.user);
});

Redis

Если вы используете вещатель Redis, вам нужно написать свой собственный паб/подписчик Redis, чтобы получать сообщения и транслировать их, используя выбранную вами технологию веб-сокетов. Например, вы можете использовать популярную библиотеку Socket.io, написанную на Node.

Используя библиотеки Node socket.io и ioredis, вы можете быстро написать вещатель событий для публикации всех событий, передаваемых вашим приложением:

var app = require('http').createServer(handler);
var io = require('socket.io')(app);

var Redis = require('ioredis');
var redis = new Redis();

app.listen(6001, function() {
    console.log('Server is running!');
});

function handler(req, res) {
    res.writeHead(200);
    res.end('');
}

io.on('connection', function(socket) {
    //
});

redis.psubscribe('*', function(err, count) {
    //
});

redis.on('pmessage', function(method, channel, message) {
    message = JSON.parse(message);
    io.emit(channel + ':' + message.event, message.data);
});
2
Pratik Soni

Почему нужно толкать? Может быть, я скучаю здесь, но в противном случае это единственный способ решить эту проблему? Вы хотите иметь возможность задавать текст, скажем, div, который мы называем statusUpdate, который показывает новые статусы с Facebook, когда они публикуются? Тогда вы могли бы:

Разделите процесс на поток сбора статуса, который работает как демон, который постоянно пытается извлечь из FB API (не знаю никаких спецификаций или не имею никаких знаний о FB API, но я могу только представить, что есть вызовы для поиска если есть новые статусы). 

Не имеет значения, является ли API потоковым или что нам нужно подключаться каждую X секунду, что мы можем принять во внимание? Я бы настроил демона в php с, а затем запустил его с SSH с помощью команды: Nohup php daemon.php, чтобы запустить скрипт с бесконечным циклом, подобным этому:

Define('SLEEP', 1);  // loop every second

while (true) {  

   // do your thing, dont exit

   if( $fbMonkey->checkNewStatus() ){
        $fbMonkey->toDatabase(new_statuses);
  }

   if( some_reason_to_exit() == TRUE ){
      exit;
   }

    sleep(SLEEP);  
}
// While ends with break

Затем, возможно, включите в HTML целевого пользователя (конец процесса браузера) функцию JavaScript, которая считывает из таблицы состояния, которые заполняет демон, а затем те, которые не были отмечены как просматриваемые (пользователями или чем-то подобным), и возвращает непрочитанные статусы в браузер. Если мы создадим для него бесконечный цикл в браузере и позволим ему обновлять div statusUpdate и новым содержимым (html или tekst, не имеет значения?). Я хотел бы, чтобы такой вызов задерживался, проверял каждые 20 секунд или около того и обновлял div.

http://www.w3schools.com/ajax/tryit.asp?filename=tryajax_xml2

function loadXMLDoc(url)
{
var xmlhttp;
var txt,xx,x,i;
if (window.XMLHttpRequest)
  {// code for IE7+, Firefox, Chrome, Opera, Safari
  xmlhttp=new XMLHttpRequest();
  }
else
  {// code for IE6, IE5
  xmlhttp=new ActiveXObject("Microsoft.XMLHTTP");
  }
xmlhttp.onreadystatechange=function()
  {
  if (xmlhttp.readyState==4 && xmlhttp.status==200)
    {
    txt="<table border='1'><tr><th>Title</th><th>Artist</th></tr>";
    x=xmlhttp.responseXML.documentElement.getElementsByTagName("CD");
    for (i=0;i<x.length;i++)
      {
      txt=txt + "<tr>";
      xx=x[i].getElementsByTagName("TITLE");
        {
        try
          {
          txt=txt + "<td>" + xx[0].firstChild.nodeValue + "</td>";
          }
        catch (er)
          {
          txt=txt + "<td>&nbsp;</td>";
          }
        }
    xx=x[i].getElementsByTagName("ARTIST");
      {
        try
          {
          txt=txt + "<td>" + xx[0].firstChild.nodeValue + "</td>";
          }
        catch (er)
          {
          txt=txt + "<td>&nbsp;</td>";
          }
        }
      txt=txt + "</tr>";
      }
    txt=txt + "</table>";
    document.getElementById('txtCDInfo').innerHTML=txt;
    }
  }
xmlhttp.open("GET",url,true);
xmlhttp.send();
}

Или я полностью нахожусь здесь?

1
Daniel Mulder

При использовании обычного протокола HTTP веб-браузеры всегда запрашивают ответ у вашего сервера. Как только ответ отправлен, сервер закрывает соединение.

Истинная технология постоянных соединений, такая как WebSocket, является одним из исключений, когда браузер устанавливает определенный тип соединения с сервером и, понимая намерение, сервер будет поддерживать соединение открытым. Таким образом, как только есть данные, которые нужно «протолкнуть» в браузер, соединение всегда готово. При таком подходе нет необходимости временно сохранять данные, поскольку вы просто «передаете их».

Методы опроса (в том числе длительный опрос, когда сервер продолжает посылать «сердцебиение» клиенту, как будто медленный ответ не имеет смысла) могут использоваться в качестве обходного пути, но всегда есть некоторый интервал времени, когда соединение больше не существует открыть, пока не произойдет следующий цикл. Когда соединение отсутствует, ваш единственный вариант - временно сохранить данные, поэтому, когда ваш браузер возвращается, вы можете нажать ожидающие данные. Если вы думаете о том, чтобы сохранить его во временной переменной, учтите, что при выполнении скрипта PHP после завершения все данные, выделенные в памяти, связанной с этой областью, собираются мусором.

1
andy

Хорошая дискуссия. Мне нравится: D @ Andy: отлично, для меня первый раз, когда кто-то может объяснить разницу очень точно, и я понял: D @ Marcos Dimitrio Я согласен

Я сам запустил чертовски неприятный пул потоков демонов Twitter API, которые делают именно это, за исключением функции $ _POST Push из Facebook, если я правильно понимаю. Он отслеживает твиты в реальном времени для массивов из сотен/тысяч кластеров ключевых слов через потоковый API Firehose. Это путь пойти или потерпеть ужасное поражение иначе: D ИМХО конечно. Это половина из двух демонов, называемых getTweets и parseTweets. 

<?php
ob_start();
require_once('config/phirehose-config.php');
require_once('lib.php');
$oDB = new db;

// run as a daemon aka background process
while (true) {

  // Process all statuses
  $query = 'SELECT cache_id, raw_Tweet ' .
    'FROM json_cache';
  $result = $oDB->select($query);
  while($row = mysqli_fetch_assoc($result)) {

    $cache_id = $row['cache_id'];
//    $status = unserialize(base64_decode($row['raw_Tweet']));
    $Tweet_object = json_decode($row['raw_Tweet'],false);


    // JSON payload for statuses stored in the database  
    // serialized base64 raw data

      // Delete cached copy of Tweet
      //    $oDB->select("DELETE FROM json_cache WHERE cache_id = $cache_id");

        // Limit tweets to a single language,
        // such as 'en' for English
        //if ($Tweet_object->lang <> 'nl') {continue;}

    // Test status update before inserting
    $Tweet_id = $Tweet_object->id_str;

    if ($oDB->in_table('tweets','Tweet_id=' . $Tweet_id )) {continue;}

    $Tweet_text = $oDB->escape($Tweet_object->text);    
    $created_at = $oDB->date($Tweet_object->created_at);
    if (isset($Tweet_object->geo)) {
      $geo_lat = $Tweet_object->geo->coordinates[0];
      $geo_long = $Tweet_object->geo->coordinates[1];
    } else {
      $geo_lat = $geo_long = 0;
    } 
    $user_object = $Tweet_object->user;
    $user_id = $user_object->id_str;
    $screen_name = $oDB->escape($user_object->screen_name);
    $name = $oDB->escape($user_object->name);
    $profile_image_url = $user_object->profile_image_url;


    // Add a new user row or update an existing one
    $field_values = 'screen_name = "' . $screen_name . '", ' .
      'profile_image_url = "' . $profile_image_url . '", ' .
      'user_id = ' . $user_id . ', ' .
      'name = "' . $name . '", ' .
      'location = "' . $oDB->escape($user_object->location) . '", ' . 
      'url = "' . $user_object->url . '", ' .
      'description = "' . $oDB->escape($user_object->description) . '", ' .
      'created_at = "' . $oDB->date($user_object->created_at) . '", ' .
      'followers_count = ' . $user_object->followers_count . ', ' .
      'friends_count = ' . $user_object->friends_count . ', ' .
      'statuses_count = ' . $user_object->statuses_count . ', ' . 
      'time_zone = "' . $user_object->time_zone . '", ' .
      'last_update = "' . $oDB->date($Tweet_object->created_at) . '"' ;     

    if ($oDB->in_table('users','user_id="' . $user_id . '"')) {
      $oDB->update('users',$field_values,'user_id = "' .$user_id . '"');
    } else {            
      $oDB->insert('users',$field_values);
    }

    // percist status to database

    $field_values = 'Tweet_id = ' . $Tweet_id . ', ' ....


    //... Somethings are to be for da cook alone, its hard work          

            foreach ($entities->hashtags as $hashtag) {

      $where = 'Tweet_id=' . $Tweet_id . ' ' .
        'AND tag="' . $hashtag->text . '"';     

      if(! $oDB->in_table('Tweet_tags',$where)) {

        $field_values = 'Tweet_id=' . $Tweet_id . ', ' .
          'tag="' . $hashtag->text . '"';   

        $oDB->insert('Tweet_tags',$field_values);
      }
    }
    foreach ($entities->urls as $url) {

      if (empty($url->expanded_url)) {
        $url = $url->url;
      } else {
        $url = $url->expanded_url;
      }

      $where = 'Tweet_id=' . $Tweet_id . ' ' .
        'AND url="' . $url . '"';       

      if(! $oDB->in_table('Tweet_urls',$where)) {
        $field_values = 'Tweet_id=' . $Tweet_id . ', ' .
          'url="' . $url . '"'; 

        $oDB->insert('Tweet_urls',$field_values);
      }
    }       
  } 

  if(DEBUG){ 
     echo ob_get_contents();
     ob_clean();
  }else{
     ob_clean();
  }

  // Longer sleep equals lower server load
  sleep(1);
}
?>

Также отлично подходит для пауков и гусениц, для которых есть моя собственная команда. Покажите мне лучший способ сделать это, все вещи, которые рассматриваются как ресурсы и масштабируемость, поскольку постоянно подключенный виджет веб-сайта для обновлений статуса FB действительно похож на использование Echelon в качестве пульта дистанционного управления снова imho).

0
Daniel Mulder

Отправляйте запрос только тогда, когда вы вернете данные с сервера. А на спящем сервере пока не найдешь что то новое отправить и отправить ответ обратно.

Продолжайте до тех пор, пока ваша страница не станет активной, и вы увидите, что данные передаются .. Это позволяет избежать проверки связи с сервером через регулярные промежутки времени.

Я не уверен, что они ссылаются на это здесь https://en.wikipedia.org/wiki/Comet_(programming)

0
Manoj Kumar

Если вам просто нужно простое решение, и вы не беспокоитесь о совместимости старых браузеров и у вас ситуация с низким трафиком, события, отправленные сервером могут помочь.

Вы создаете его экземпляр одной строкой, если ваш скрипт генерации Push-сообщений находится на том же сервере.

var evtSource = new EventSource("messages.php");

Затем функция для обработки входящих сообщений.

    evtSource.onmessage = function(e) {

       console.log(e.data);
    }

messages.php должен иметь заголовок как

header("Content-Type: text/event-stream\n\n");

Затем выполните бесконечный цикл с заданным интервалом.

пример:

header("Content-Type: text/event-stream\n\n");
date_default_timezone_set("Pacific/Auckland"); // as required
$sleepTime = 8; // # of seconds delayed between notifications

while (1) 
{   

   // Send a message at $sleepTime second intervals.
   echo 'data: The time is ' . date("H:i:s") . "\n\n";

   ob_end_flush();
   flush();
   sleep($sleepTime);
}

Очевидно, что вам нужно будет читать сообщения откуда-то и представлять их по мере необходимости, но, по крайней мере, этот пример дает вам представление о том, как создать поток событий без необходимости вдаваться в относительную сложность WebSocket.

Отказ от ответственности: у меня нет опыта работы с PHP, но это решение кажется работать на меня. Если есть какие-то проблемы с этим, я бы хотел услышать.

0
ekendra

Вариант 1 (если вам нужна надежная система): Комбинация сервера очередей (например, AMQP или MQTT и т.д.) И клиента веб-сокета, например http://www.hivemq.com/full-featured-mqtt-client- браузер/ будет надежным. AMQP поддерживает почти каждый язык, например в PHP ( https://pecl.php.net/package/amqp ).

Вариант 2: Используйте ajax и сообщите браузеру, чтобы получать обновления с сервера через определенные промежутки времени (как уже упоминалось в комментариях)

0
Hemant Kumar