Sunucu tarafından gönderilen olaylar ve php - sunucudaki olayları ne tetikler?


93

Herşey,

HTML5 Rocks, Sunucu Tarafından Gönderilen Olaylar (SSE) hakkında güzel bir başlangıç ​​eğitimine sahiptir:

http://www.html5rocks.com/en/tutorials/eventsource/basics/

Ancak önemli bir kavramı anlamıyorum - sunucudaki bir mesajın gönderilmesine neden olan olayı tetikleyen nedir?

Başka bir deyişle - HTML5 örneğinde - sunucu yalnızca bir kez zaman damgası gönderir :

<?php
header('Content-Type: text/event-stream');
header('Cache-Control: no-cache'); // recommended to prevent caching of event data.
function sendMsg($id, $msg) {
  echo "id: $id" . PHP_EOL;
  echo "data: $msg" . PHP_EOL;
  echo PHP_EOL;
  ob_flush();
  flush();
}
$serverTime = time();
sendMsg($serverTime, 'server time: ' . date("h:i:s", time()));

Pratik bir örnek oluşturuyor olsaydım - örneğin, Facebook tarzı bir "duvar" veya bir hisse senedi, sunucunun her veri parçası değiştiğinde istemciye yeni bir mesaj "göndereceği" bir hisse senedi, bu nasıl çalışır?

Diğer bir deyişle ... PHP betiğinin sürekli olarak çalışan, verilerde bir değişiklik olup olmadığını kontrol eden ve ardından her bulduğunda bir mesaj gönderen bir döngüsü var mı? Öyleyse - bu süreci ne zaman bitireceğinizi nasıl bileceksiniz?

Veya - PHP betiği sadece mesajı gönderiyor ve sonra bitiyor mu (HTML5Rocks örneğinde görüldüğü gibi)? Öyleyse - sürekli güncellemeleri nasıl alırsınız? Tarayıcı, PHP sayfasını düzenli aralıklarla sorguluyor mu? Eğer öyleyse - bu nasıl bir "sunucu tarafından gönderilen olay"? Bunun JavaScript'te bir PHP sayfasını düzenli aralıklarla çağırmak için AJAX kullanan bir setInterval işlevi yazmaktan farkı nedir?

Üzgünüm - bu muhtemelen inanılmaz derecede saf bir soru. Ancak bulabildiğim örneklerin hiçbiri bunu netleştirmiyor.

[GÜNCELLEME]

Sanırım sorum kötü yazılmıştı, işte bazı açıklamalar.

Diyelim ki Apple hisselerinin en son fiyatını göstermesi gereken bir web sayfam var.

Kullanıcı sayfayı ilk açtığında, sayfa "akışımın" URL'siyle bir Etkinlik Kaynağı oluşturur.

var source = new EventSource('stream.php');

Sorum şu - "stream.php" nasıl çalışmalı?

Bunun gibi? (sözde kod):

<?php
    header('Content-Type: text/event-stream');
    header('Cache-Control: no-cache'); // recommended to prevent caching of event data.
    function sendMsg($msg) {
        echo "data: $msg" . PHP_EOL;
        echo PHP_EOL;
        flush();
    }

    while (some condition) {
        // check whether Apple's stock price has changed
        // e.g., by querying a database, or calling a web service
        // if it HAS changed, sendMsg with new price to client
        // otherwise, do nothing (until next loop)
        sleep (n) // wait n seconds until checking again
    }
?>

Başka bir deyişle - istemci "bağlı" olduğu sürece "stream.php" açık kalır mı?

Eğer öyleyse - bu, stream.phpeşzamanlı kullanıcılarınız olduğu kadar çok sayıda iş parçacığınız olduğu anlamına mı geliyor ? Eğer öyleyse - bu uzaktan uygulanabilir mi, yoksa bir uygulama oluşturmanın uygun bir yolu mu? Ve bir örneğini ne zaman sonlandırabileceğinizi nasıl anlarsınız stream.php?

Saf izlenimim, eğer durum buysa, PHP bu tür bir sunucu için uygun bir teknoloji olmadığıdır . Ancak şimdiye kadar gördüğüm tüm demolar PHP'nin bunun için iyi olduğunu ima ediyor, bu yüzden kafam karıştı ...


Bir geliştiricinin kendi başına kodlaması gereken kısım budur. Verileri elde etmenin yolları web soketleri / uzun yoklama vb. Yoluyladır, ancak işin püf noktası olayı tetikleyen şeydir . Şahsen, birkaç yaklaşımlar denedi ve ben beğendiği bir yaklaşım (ama değildi ettik o MySQL tetikleyici her zaman bir şey belirli bir tabloda sokulmuş bir konsol programı yapıyordu fail-safe). Konsol programı, değiştirilen / eklenen kayıt hakkındaki bilgileri alacak ve WebSockets aracılığıyla ilgili kullanıcıya bildirim gönderecektir. Temelde etrafa mesaj göndermek için bekleyen bir PHP daemon'um vardı.
NB

Bununla ilgili bir sorun, SSE'nin IE tarafından desteklenmemesi: - / Ayrıca bu prodigyproductionsllc.com/articles/programming/javascript/… Sanırım çok fazla çocuk probleminden kaçınmak için bir port kullanıyor ama genel olarak onun gibi görünüyor SSE'den kaçınmak tavsiye edilir. Değerinden çok daha fazla sorun gibi görünüyor, IMO.
PJ Brunet

Şu anda IE11 veya Android Tarayıcı tarafından desteklenmemektedir caniuse.com/eventsource
PJ Brunet


4
Ben aynı soru vardı ve ben derinden derken ne demek istediğine anlıyorum ... sunucudaki olay neyin tetiklediği . Bir nesnesini oluşturduğunuzda EventSource('stream.php'), istemci stream.phponu ajax ile çağırmak gibi bir bağlantı açar . BU bağlantı, sunucu tarafı kodunuzu tetikler ve sunucu tarafı kodunuzun söyleyecek bir şeyi olduğu sürece bağlantıyı açık tutar. Ardından bağlantı kapanır ve kısa bir gecikmeden sonra (kromda 3 saniye sanırım) istemci, stream.phpdosyanızı yeniden tetikleyen bağlantıyı yeniden açar .
Ahmad Maleki

Yanıtlar:


29

"..." stream.php ", istemci" bağlı "olduğu sürece açık kalır mı?"

Evet ve sözde kodunuz makul bir yaklaşım.

"Ve stream.php örneğini ne zaman SONLANDIRabileceğinizi nasıl anlarsınız?"

En tipik durumda, bu, kullanıcı sitenizden ayrıldığında gerçekleşir. (Apache kapalı soketi tanır ve PHP örneğini öldürür.) Soketi sunucu tarafından kapatmanızın ana zamanı, bir süre veri olmayacağını biliyorsanız; Müşteriye gönderdiğiniz son mesaj, onlara belirli bir zamanda geri gelmelerini söylemektir. Örneğin, hisse senedi akış durumunuzda, bağlantıyı saat 20: 00'de kapatabilir ve müşterilere 8 saat içinde geri gelmelerini söyleyebilirsiniz (NASDAQ'ın 04: 00-20: 00 arası fiyat tekliflerine açık olduğunu varsayarak). Cuma akşamı onlara Pazartesi sabahı geri gelmelerini söylüyorsun. (SSE hakkında yakında çıkacak bir kitabım var ve bu konuyla ilgili birkaç bölüm ayırıyorum.)

"... durum buysa, PHP bu tür bir sunucu için uygun bir teknoloji değildir. Ancak şimdiye kadar gördüğüm tüm demolar, PHP'nin bunun için iyi olduğunu ima ediyor, bu yüzden ben öyle Şaşkın..."

İnsanlar, PHP'nin normal web siteleri için uygun bir teknoloji olmadığını savunuyorlar ve haklılar: LAMP yığınınızın tamamını C ++ ile değiştirirseniz, bunu çok daha az bellek ve CPU döngüsü ile yapabilirsiniz. Bununla birlikte, buna rağmen, PHP dışarıdaki sitelerin çoğuna iyi güç sağlar. Tanıdık bir C benzeri sözdizimi ve pek çok kitaplığın bir kombinasyonu nedeniyle web çalışması için çok üretken bir dildir ve yöneticiler için işe alması gereken çok sayıda PHP programcısı, çok sayıda kitap ve diğer kaynaklar ve bazı büyük kullanım durumları (ör. Facebook ve Wikipedia). Bunlar temelde akış teknolojiniz olarak PHP'yi seçmenizin nedenleriyle aynıdır.

Tipik kurulum, her PHP-instance'ı için NASDAQ'a tek bir bağlantı olmayacaktır. Bunun yerine, NASDAQ'a tek bir bağlantıyla veya belki de kümenizdeki her makineden NASDAQ'a tek bir bağlantıyla başka bir işleme sahip olacaksınız. Bu daha sonra fiyatları bir SQL / NoSQL sunucusuna veya paylaşılan belleğe iter. Ardından PHP, paylaşılan belleği (veya veritabanını) yoklar ve verileri dışarı iter. Veya bir veri toplama sunucunuz olsun ve her PHP örneği o sunucuya bir soket bağlantısı açar. Veri toplama sunucusu, güncellemeleri alırken her bir PHP istemcisine gönderir ve karşılığında bu verileri istemcilere gönderir.

Akış için Apache + PHP kullanımıyla ilgili temel ölçeklenebilirlik sorunu, her Apache işleminin belleğidir. Donanımın bellek sınırına ulaştığınızda, kümeye başka bir makine eklemek veya Apache'yi döngüden çıkarmak için iş kararını verin ve özel bir HTTP sunucusu yazın. İkincisi PHP'de yapılabilir, böylece mevcut tüm bilginiz ve kodunuz yeniden kullanılabilir veya tüm uygulamayı başka bir dilde yeniden yazabilirsiniz. İçimdeki saf geliştirici, C ++ 'da özel, modern bir HTTP sunucusu yazardı. İçimdeki yönetici bir kutu daha eklerdi.


Her Apache bağlantı işlemi bellek tükettiğinden, bunun yerine Nginx kullanmak daha iyi olur mu?
Zhang Buzz

@ZhangBuzz Apache + PHP dediğim yerde gerçekten "web sunucusu + PHP süreci" anlamına geliyor, bu nedenle temelde farklı bir web sunucusu kullanmakla hiçbir fark yok.
Darren Cook


Ayrıca Nginx'in bunun için çok daha verimli olabileceğini unutmayın, çünkü daha az bellek kullanır: blog.webfaction.com/2008/12/…
Enrique

32

Sunucu tarafından gönderilen olaylar, sunucu tarafından istemci tarafına gerçek zamanlı güncelleme içindir. İlk örnekte, sunucudan gelen bağlantı korunmaz ve istemci her 3 saniyede bir yeniden bağlanmaya çalışır ve sunucu tarafından gönderilen olayların ajax sorgulamasından farksız olmasını sağlar.

Bu nedenle, bağlantının kalıcı olmasını sağlamak için, kodunuzu bir döngü içine almanız ve sürekli güncellemeleri kontrol etmeniz gerekir.

PHP iş parçacığı tabanlıdır ve daha fazla bağlı kullanıcı sunucunun kaynaklarının tükenmesine neden olur. Bu, komut dosyası yürütme süresini kontrol ederek çözülebilir ve belirli bir süreyi aştığında (yani 10 dakika) komut dosyasını sonlandırabilir. EventSourceGecikme kabul edilebilir bir aralık içinde olacak şekilde API otomatik olarak yeniden bağlanır.

Ayrıca, Sunucu tarafından gönderilen olaylar için PHP kitaplığıma bakın , PHP'de sunucu tarafından gönderilen olayların nasıl yapılacağı hakkında daha fazla bilgi edinebilir ve kodlamayı kolaylaştırabilirsiniz.


5
"Bu, komut dosyası yürütme süresini kontrol ederek çözülebilir ve belirli bir süreyi aştığında betiği sonlandırabilir" konusunu biraz daha detaylandırabilir misiniz? Aşırı sayıda kullanıcınız varsa, kullanıcı 3 saniye içinde tekrar bağlanacağı için bağlantıyı kapatarak kaynak kullanımını gerçekten çok artırır mı?
Luke

4

Sse techink'in her iki gecikme verisini istemciye gönderdiğini fark ettim (Ajax havuzlama verileri istemci sayfasından havuzlama veri teknik bağlantısını tersine çevirmek gibi bir şey), bu yüzden bu sorunun üstesinden gelmek için bunu bir sseServer.php sayfasında yaptım:

<?php
        session_start();
        header('Content-Type: text/event-stream');
        header('Cache-Control: no-cache'); // recommended to prevent caching of event data
        require 'sse.php';
        if ($_POST['message'] != ""){
                $_SESSION['message'] = $_POST['message'];
                $_SESSION['serverTime'] = time();
        }
        sendMsg($_SESSION['serverTime'], $_SESSION['message'] );
?>

ve sse.php:

<?php
function sendMsg($id, $msg) {
  echo "id: $id" . PHP_EOL;
  echo "data: $msg" . PHP_EOL;
  echo PHP_EOL;
  ob_flush();
  flush();
}
?>

SseSerer.php'de bir oturum başlattığıma ve bir oturum değişkeni kullandığıma dikkat edin! sorunun üstesinden gelmek için.

Ayrıca sseServer.php'yi Ajax aracılığıyla (gönderme ve değeri ayarlama variable message) her "güncellemek" mesajı istediğimde çağırıyorum .

Şimdi jQuery'de (javascript) şöyle bir şey yapıyorum: 1.) bir global değişken bildiriyorum var timeStamp = 0; 2.) Bir sonraki algoritmayı kullanıyorum:

if(typeof(EventSource)!=="undefined"){
        var source=new EventSource("sseServer.php");
        source.onmessage=function(event)
        if ((timeStamp!=event.lastEventId) && (timeStamp!=0)){
                /* this is initialization */
                timeStamp=event.lastEventId;
                $.notify("Please refresh "+event.data, "info");
        } else {
                if (timeStamp==0){
                         timeStamp=event.lastEventId;
                }
        } /* fi */

} else {
        document.getElementById("result").innerHTML="Sorry, your browser does not support server-sent events...";
} /* fi */

Satırında: $.notify("Please refresh "+event.data, "info"); mesajı idare edebilecek misin?

Durumum için bir jQuery bildirimi gönderirdim.

SseServer.php "sonsuz döngü" gibi bir şey yaptığından "mesajı" POST aracılığıyla iletmek için POSIX BORULARI veya bir DB Tablosu kullanabilirsiniz.

O zamanki sorunum, yukarıdaki kodun "mesajı" tüm istemcilere GÖNDERMEMESİ, ancak yalnızca çifte (sseServer.php'yi çağıran istemci her çifte ayrı ayrı çalışır), bu yüzden tekniği ve bir "Mesaj" ı tetiklemek istediğim sayfadan DB güncellemesi ve ardından mesajı POST yoluyla almak için sseServer.php yerine DB tablosundan alacak.

Umarım yardımım vardır!


3

Bu gerçekten başvurunuzla ilgili yapısal bir sorudur. Gerçek zamanlı olaylar, baştan düşünmek isteyeceğiniz bir şeydir, böylece uygulamanızı onun etrafında tasarlayabilirsiniz. mysql(i)_queryDize sorguları kullanarak bir dizi rastgele yöntem çalıştıran ve bunları herhangi bir aracıdan geçirmeyen bir uygulama yazdıysanız, çoğu zaman uygulamanızın çoğunu yeniden yazmaktan başka seçeneğiniz olmayacak ya da sabit sunucu tarafı yoklama.

Bununla birlikte, varlıklarınızı nesne olarak yönetir ve onları bir tür ara sınıftan geçirirseniz, bu sürece bağlanabilirsiniz. Şu örneğe bakın:

<?php
class MyQueryManager {
    public function find($myObject, $objectId) {
        // Issue a select query against the database to get this object
    }

    public function save($myObject) {
        // Issue a query that saves the object to the database
        // Fire a new "save" event for the type of object passed to this method
    }

    public function delete($myObject) {
        // Fire a "delete" event for the type of object
    }
}

Uygulamanızda, kaydetmeye hazır olduğunuzda:

<?php
$someObject = $queryManager->find("MyObjectName", 1);
$someObject->setDateTimeUpdated(time());
$queryManager->save($someObject);

Bu en zarif örnek değil ama iyi bir yapı taşı olarak hizmet etmelidir. Bu olayları tetiklemek için gerçek kalıcılık katmanınıza bağlanabilirsiniz. Ardından, sunucunuzu zorlamadan (veritabanınızı sürekli sorgulamanıza ve bir şeylerin değişip değişmediğini görmenize gerek olmadığından) hemen (alabildiği kadar gerçek zamanlı) alırsınız.

Açıkça bu şekilde veritabanında manuel olarak yapılan değişiklikleri yakalayamazsınız - ancak veritabanınızda herhangi bir sıklıkta manuel olarak herhangi bir şey yapıyorsanız, şunlardan birini yapmalısınız:

  • El ile değişiklik yapmanızı gerektiren sorunu düzeltin
  • Süreci hızlandırmak ve bu olayları tetiklemek için bir araç oluşturun

3
Colin - cevabınız için teşekkürler. Benim hatam - sorum net değil - ama gerçekten sorduğum bu değil. Ne demek sormak burası ... Eğer "server" olarak PHP kullanıyorsanız - does çalıştırmak istemci muhtaç EventSource gelen çağrı PHP süre boyunca istemci ona bağlı? Bu, 1000 eşzamanlı kullanıcınız varsa, PHP betiğinizin 1000 eşzamanlı örneğini çalıştıran 1.000 ayrı iş parçacığınız olduğu anlamına mı gelir? Bu uygulanabilir mi? Ve, php betiğini ne zaman bitireceğinizi nasıl bilebilirsiniz ("canlı" kalmak için döngünün olduğunu varsayarak)?
mattstuehler

-7

Temel olarak, PHP bu tür şeyler için uygun bir teknoloji değildir. Evet, çalıştırabilirsiniz, ancak yüksek yükte felaket olur. Web soketleri aracılığıyla binlerce kullanıcıya stok değişim sinyalleri gönderen hisse senedi sunucuları çalıştırıyoruz - ve bunun için php kullansaydık ... Yapabilirdik, ama bu ev yapımı döngüler - sadece bir kabus. Her bir bağlantı, sunucuda ayrı bir işlem yapacaktır veya bir tür veritabanından gelen bağlantıları yönetmeniz gerekir.

Sadece nodejs ve socket.io'yu kullanın. Birkaç gün içinde kolayca başlamanıza ve çalışan bir sunucuya sahip olmanıza izin verecektir. Nodejs de kendi sınırlamalarına sahiptir, ancak web soketleri (ve SSE) bağlantıları için artık en güçlü teknolojidir.

Ve ayrıca - SSE göründüğü kadar iyi değil. Web soketlerinin tek avantajı - paketlerin yerel olarak gzip ile sıkıştırılmasıdır (ws gzip ile sıkıştırılmamıştır), ancak olumsuz tarafı SSE'nin tek taraflı bağlantı olmasıdır. Kullanıcı, aboneliğe başka bir hisse senedi sembolü eklemek isterse, ajax talebinde bulunmalıdır (menşe kontrolüyle ilgili tüm sorunlar dahil ve istek yavaş olacaktır). Web soketlerinde istemci ve sunucu, tek bir açık bağlantıda her iki yolla iletişim kurar, bu nedenle kullanıcı bir alım satım sinyali gönderir veya kotaya abone olursa, yalnızca zaten açık olan bağlantıda bir dizi gönderir. Ve hızlı.


2
React.php'yi node.js'deki olay döngüsü ile aynı şekilde kullanabilirsiniz.
Matěj Koubík

3
PHP'nin en iyi seçenek olmadığını söylemek güzel olsa da, en azından OP'nin istediği bir şeyi eklemeniz gerektiğini düşünüyorum.
Nishchal Gautam
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.