Sunucu tarafından gönderilen etkinliklerle güncelleme akışı

Sunucu tarafından gönderilen etkinlikler (SSE'ler), HTTP ile bir sunucudan istemciye otomatik güncellemeler gönderir. bağlantı. Bağlantı kurulduktan sonra sunucular verileri başlatabilir iletim.

Web uygulamanızdan push bildirimleri göndermek için SSE'leri kullanabilirsiniz. SSE'ler bilgileri tek bir yönde gönderdiğinden gerekir.

SSE kavramı size tanıdık gelebilir. Web uygulaması "abone olur" akışa sunucu tarafından oluşturulan güncellemeler ve yeni bir etkinlik gerçekleştiğinde bir bildirim gönderilir. Ama sunucu tarafından gönderilen etkinlikleri gerçekten anlamak için önceki AJAX öncülerinin sınırlamalarını anlayın. Bunlardan bazıları:

  • Anketleme: Uygulama, sunucuyu veriler için sürekli olarak yoklar. Bu teknik AJAX uygulamalarının çoğu tarafından kullanılır. HTTP protokolüyle, istek ve yanıt formatı etrafında şekillenir. İstemci bir istek gönderir ve sunucunun verilerle yanıt vermesini bekler. Yoksa, boş bir hatası döndürülür. Ekstra yoklama, daha fazla HTTP ek yükü oluşturur.

  • Uzun yoklama (Asılı GET / COMET): Sunucuda veri yoksa kullanılabilir durumdaysa sunucu, yeni veriler gelene kadar isteği açık tutar. Dolayısıyla, bu teknik genellikle "Asılı GET" olarak adlandırılır. Zaman sunucu yanıt verir, bağlantıyı kapatır ve ve süreç tekrarlanır. Bu nedenle, sunucu sürekli olarak yeni veriler oluşturabilirsiniz. Geliştiriciler bunu ayarlamak için genellikle komut dosyası etiketlerini bir 'sonsuz' iframe'dir.

Sunucu tarafından gönderilen etkinlikler, baştan sona verimli olacak şekilde tasarlanmıştır. SSE'lerle iletişim kurarken bir sunucu, SSE'lerinize veri ve uygulamayı ilk kez istekte bulunmaya gerek kalmadan uygulamaya aktarabilirsiniz. Başka bir deyişle, güncellemeler gerçekleştikçe sunucudan istemciye aktarılabilir. SSE'ler Sunucu ve istemci arasında tek bir tek yönlü kanal açmak.

Sunucu tarafından gönderilen etkinlikler ile uzun anket arasındaki temel fark, SSE'lerin doğrudan tarayıcı tarafından işlenir ve kullanıcının yalnızca iletileri dinlemesi gerekir.

Sunucu tarafından gönderilen etkinlikler ile WebSocket'lerin karşılaştırılması

Neden WebSockets yerine sunucu tarafından gönderilen etkinlikleri tercih edersiniz? Güzel soru.

WebSockets, aşağıdakileri içeren zengin bir protokole sahiptir: iki yönlü, tam çift yönlü iletişim. İki yönlü bir kanal, Google Play Games Beta ile neredeyse gerçek zamanlı güncellemelere ihtiyaç duyduğunuz sağlayabilirsiniz.

Ancak bazen bir sunucudan yalnızca tek yönlü iletişime ihtiyacınız olur. Örneğin, bir arkadaşınız durumunu, hisse senedi borsalarını, haber feed'lerini veya diğer otomatik veri aktarma mekanizmalarını kullanabilirsiniz. Başka bir deyişle, istemci tarafında yapılan Web SQL Veritabanı veya IndexedDB nesne deposu güncellemesi. Bir sunucuya veri göndermeniz gerekiyorsa, XMLHttpRequest her zaman arkadaşınızdır.

SSE'ler HTTP üzerinden gönderilir. Özel bir protokol veya sunucu yoktur uygulamaya karar vermemiz gerekir. WebSocket'ler tam çift yönlü gerektirir bağlantıları ve yeni WebSocket sunucularıyla çalışır.

Buna ek olarak, sunucu tarafından gönderilen etkinlikler WebSocket'lerde bulunmayan çeşitli özelliklere sahiptir ve otomatik olarak yeniden bağlanma, etkinlik kimlikleri ve isteğe bağlıdır.

JavaScript ile EventSource oluşturma

Bir etkinlik akışına abone olmak için EventSource nesnesi oluşturun ve bunu Akışınızın URL'si:

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

Sonra, message etkinliği için bir işleyici ayarlayın. İsterseniz open ve error dinle:

source.addEventListener('message', (e) => {
  console.log(e.data);
});

source.addEventListener('open', (e) => {
  // Connection was opened.
});

source.addEventListener('error', (e) => {
  if (e.readyState == EventSource.CLOSED) {
    // Connection was closed.
  }
});

Güncellemeler sunucudan aktarıldığında onmessage işleyicisi etkinleşir ve e.data mülkünde yeni veriler var. İşin sihirli kısmı bağlantı her kapatıldığında tarayıcının otomatik olarak kaynağı. Sunucu uygulamanız, hakkında daha fazla bilgi edinin.

Bu kadar basit. Müşteriniz artık stream.php takvimindeki etkinlikleri işleyebilir.

Etkinlik akışı biçimi

Kaynağından bir etkinlik akışı göndermek, text/event-stream İçerik Türü ile sunulan düz metin yanıtı, şu SSE biçimine uyar. Yanıt, temel biçiminde bir data: satırı ve onu takip eden bir mesajı ve ardından iki "\n" karakteri ekleyin:

data: My message\n\n

Çok satırlı veriler

Mesajınız daha uzunsa birden fazla data: satırı kullanarak bölebilirsiniz. data: ile başlayan iki veya daha fazla ardışık satır, karakter olarak kabul edilir tek bir veri parçasına sahip olduğundan yalnızca bir message etkinliği tetiklenir.

Her satır tek bir "\n" ile sona ermelidir (sonun olması gereken sonuncu (iki ile birlikte). message işleyicinize iletilen sonuç tek bir dizedir yeni satır karakterleriyle birleştirilir. Örneğin:

data: first line\n
data: second line\n\n</pre>

Bu, "ilk satır\nikinci satırı" oluşturur e.data içinde. Bir kullanıcı daha sonra "\n" sans mesajını yeniden oluşturmak için e.data.split('\n').join('') karakteri ekleyin.

JSON verilerini gönder

Birden çok satır kullanmak, söz dizimini bozmadan JSON göndermenize yardımcı olur:

data: {\n
data: "msg": "hello world",\n
data: "id": 12345\n
data: }\n\n

Ayrıca, bu akışı işlemek için olası istemci tarafı kodu:

source.addEventListener('message', (e) => {
  const data = JSON.parse(e.data);
  console.log(data.id, data.msg);
});

Bir etkinlikle kimliği ilişkilendirme

Şu şekilde başlayan bir satır ekleyerek akış etkinliğiyle benzersiz bir kimlik gönderebilirsiniz: id::

id: 12345\n
data: GOOG\n
data: 556\n\n

Bir kimlik ayarlamak, tarayıcının tetiklenen son etkinliği izlemesini sağlar. Böylece bağlantı kesilirse, özel bir HTTP üstbilgisi (Last-Event-ID) yeni istekle ayarlanır. Bu sayede tarayıcı, hangi etkinliğin tetiklenmek için uygun olduğunu belirleyebilir. message etkinliği, bir e.lastEventId özelliği içerir.

Yeniden bağlanma zaman aşımını kontrol etme

Tarayıcı yaklaşık 3 saniye içinde kaynağa yeniden bağlanmayı dener her bağlantı kapatıldıktan sonra gelir. Bu zaman aşımı süresini retry: ile başlayan ve ardından milisaniye sayısı ile devam eden satır bağlanmayı denemeden önce bekleyin.

Aşağıdaki örnekte, 10 saniye sonra yeniden bağlanma teşebbüsünde bulunulmuştur:

retry: 10000\n
data: hello world\n\n

Bir etkinlik adı belirtin

Tek bir etkinlik kaynağı, aşağıdakileri yaparak farklı türlerde etkinlikler oluşturabilir: etkinlik adı. event: ile başlayan bir satır varsa ve ardından etkinlik için benzersiz bir ad girerseniz etkinlik bu adla ilişkilendirilir. İstemcide, söz konusu etkinliği dinleyecek bir etkinlik işleyici ayarlanabilir.

Örneğin, aşağıdaki sunucu çıkışı üç tür etkinlik gönderir: genel bir "mesaj" event, "userlogon" ve "update" etkinlik:

data: {"msg": "First message"}\n\n
event: userlogon\n
data: {"username": "John123"}\n\n
event: update\n
data: {"username": "John123", "emotion": "happy"}\n\n

İstemcide etkinlik işleyiciler ayarlandığında:

source.addEventListener('message', (e) => {
  const data = JSON.parse(e.data);
  console.log(data.msg);
});

source.addEventListener('userlogon', (e) => {
  const data = JSON.parse(e.data);
  console.log(`User login: ${data.username}`);
});

source.addEventListener('update', (e) => {
  const data = JSON.parse(e.data);
  console.log(`${data.username} is now ${data.emotion}`);
};

Sunucu örnekleri

Aşağıda, PHP'deki temel bir sunucu uygulaması verilmiştir:

<?php
header('Content-Type: text/event-stream');
header('Cache-Control: no-cache'); // recommended to prevent caching of event data.

/**
* Constructs the SSE data format and flushes that data to the client.
*
* @param string $id Timestamp/id of this connection.
* @param string $msg Line of text that should be transmitted.
**/

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()));
?>

Aşağıda, Node JS kullanılarak Ekspres işleyici:

app.get('/events', (req, res) => {
    // Send the SSE header.
    res.writeHead(200, {
        'Content-Type': 'text/event-stream',
        'Cache-Control': 'no-cache',
        'Connection': 'keep-alive'
    });

    // Sends an event to the client where the data is the current date,
    // then schedules the event to happen again after 5 seconds.
    const sendEvent = () => {
        const data = (new Date()).toLocaleTimeString();
        res.write("data: " + data + '\n\n');
        setTimeout(sendEvent, 5000);
    };

    // Send the initial event immediately.
    sendEvent();
});

sse-node.html

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
  </head>
  <body>
    <script>
    const source = new EventSource('/events');
    source.onmessage = (e) => {
        const content = document.createElement('div');
        content.textContent = e.data;
        document.body.append(content);
    };
    </script>
  </body>
</html>

Etkinlik akışını iptal etme

Normalde, bağlantı kurulduğunda tarayıcı etkinlik kaynağına otomatik olarak yeniden bağlanır. kapalıdır, ancak bu davranış istemci veya sunucudan iptal edilebilir.

İstemciden canlı yayını iptal etmek için şu numarayı arayın:

source.close();

Sunucudan bir akışı iptal etmek için text/event-stream dışında bir seçenekle yanıt verin. Content-Type veya 200 OK dışında bir HTTP durumu döndür (ör. 404 Not Found).

Her iki yöntem de tarayıcının bağlantıyı yeniden kurmasını engeller.

Güvenlikle ilgili söz

EventSource tarafından oluşturulan istekler getirme gibi diğer ağ API'lerini kullanabilir. Sunucunuzda SSE uç noktasına ihtiyacınız varsa daha fazla bilgi edinmek istiyorsanız, Kaynaklar Arası Kaynak Paylaşımı (CORS).