Sự kiện do máy chủ gửi và php - điều gì kích hoạt sự kiện trên máy chủ?


91

Tất cả,

HTML5 Rocks có một hướng dẫn dành cho người mới bắt đầu về Sự kiện do máy chủ gửi (SSE):

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

Tuy nhiên, tôi không hiểu một khái niệm quan trọng - điều gì kích hoạt sự kiện trên máy chủ khiến thư được gửi?

Nói cách khác - trong ví dụ HTML5 - máy chủ chỉ gửi một dấu thời gian một lần :

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

Nếu tôi đang xây dựng một ví dụ thực tế - ví dụ: "bức tường" kiểu Facebook hoặc mã cổ phiếu, trong đó máy chủ sẽ "đẩy" một thông báo mới đến máy khách mỗi khi một số phần dữ liệu thay đổi, thì điều đó hoạt động như thế nào?

Nói cách khác ... Tập lệnh PHP có một vòng lặp chạy liên tục, kiểm tra sự thay đổi trong dữ liệu, sau đó gửi thông báo mỗi khi nó tìm thấy một vòng lặp? Nếu vậy - làm thế nào để bạn biết khi nào kết thúc quá trình đó?

Hoặc - tập lệnh PHP có đơn giản chỉ gửi tin nhắn, sau đó kết thúc (như trường hợp trong ví dụ HTML5Rocks)? Nếu vậy - làm cách nào để bạn nhận được các bản cập nhật liên tục? Trình duyệt chỉ đơn giản là thăm dò trang PHP theo định kỳ? Nếu vậy - đó là "sự kiện do máy chủ gửi" như thế nào? Điều này khác với việc viết hàm setInterval trong JavaScript sử dụng AJAX để gọi một trang PHP như thế nào?

Xin lỗi - đây có lẽ là một câu hỏi cực kỳ ngây thơ. Nhưng không có ví dụ nào tôi có thể tìm thấy làm rõ điều này.

[CẬP NHẬT]

Tôi nghĩ rằng câu hỏi của tôi được diễn đạt kém, vì vậy đây là một số giải thích rõ ràng.

Giả sử tôi có một trang web hiển thị giá gần đây nhất của cổ phiếu Apple.

Khi người dùng mở trang lần đầu, trang sẽ tạo EventSource với URL của "luồng" của tôi.

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

Câu hỏi của tôi là - "stream.php" nên hoạt động như thế nào?

Như thế này? (mã giả):

<?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
    }
?>

Nói cách khác - "stream.php" có mở miễn là máy khách "kết nối" với nó không?

Nếu vậy - điều đó có nghĩa là bạn có bao nhiêu luồng chạy stream.phpnhư bạn có người dùng đồng thời? Nếu vậy - điều đó có khả thi từ xa hay là một cách thích hợp để xây dựng một ứng dụng? Và làm thế nào để bạn biết khi nào bạn có thể KẾT THÚC một phiên bản stream.php?

Ấn tượng ngây thơ của tôi là, nếu đúng như vậy, PHP không phải là một công nghệ phù hợp cho loại máy chủ này. Nhưng tất cả các bản demo mà tôi đã xem cho đến nay đều ngụ ý rằng PHP rất tốt cho việc này, đó là lý do tại sao tôi rất bối rối ...


Đó là phần mà nhà phát triển phải tự viết mã. Phương tiện lấy dữ liệu là thông qua websockets / long polling, v.v. tuy nhiên, thủ thuật là thứ gây ra sự kiện. Cá nhân, tôi đã thử nghiệm với một vài cách tiếp cận và một cách tiếp cận mà tôi thích (nhưng nó không phải không an toàn) đã làm MySQL kích hoạt một chương trình giao diện điều khiển mỗi khi một cái gì đó đã được chèn vào trong một bảng cụ thể. Chương trình bảng điều khiển sẽ nhận được thông tin về bản ghi đã thay đổi / được chèn và nó sẽ gửi thông báo đến người dùng tương ứng qua WebSockets. Về cơ bản, tôi có một trình nền PHP đang chờ để gửi tin nhắn xung quanh.
NB

Một vấn đề với điều này, SSE không được IE hỗ trợ: - / Ngoài ra, tôi cũng sẽ đọc sản phẩm này prodigyproductionsllc.com/articles/programming/javascript/… Tôi nghĩ anh ấy đang sử dụng một cổng để tránh vấn đề quá nhiều trẻ em nhưng nhìn chung thì có vẻ giống như anh ấy khuyến nghị là tránh SSE. Có vẻ rắc rối hơn mức đáng có, IMO.
PJ Brunet

Hiện nay không được hỗ trợ bởi IE11 hoặc Trình duyệt Android caniuse.com/eventsource
PJ Brunet


4
Tôi có cùng một câu hỏi và tôi nghĩ rằng tôi sâu sắc hiểu những gì bạn có nghĩa là bởi những gì gây nên sự kiện trên máy chủ ... . Khi bạn tạo một đối tượng EventSource('stream.php'), máy khách sẽ mở một kết nối với stream.phpnó giống như gọi nó bằng ajax. Kết nối NÀY kích hoạt mã phía máy chủ của bạn và giữ kết nối mở miễn là mã phía máy chủ của bạn có điều gì đó để nói. Sau đó, kết nối đóng và sau một khoảng thời gian ngắn (tôi nghĩ là 3 giây trong chrome), ứng dụng khách sẽ mở lại kết nối và kích hoạt lại stream.phptệp của bạn .
Ahmad Maleki,

Câu trả lời:


28

"..." stream.php "có mở miễn là máy khách" kết nối "với nó không?"

Có, và mã giả của bạn là một cách tiếp cận hợp lý.

"Và làm thế nào để bạn biết khi nào bạn có thể KẾT THÚC một phiên bản của stream.php?"

Trong trường hợp điển hình nhất, điều này xảy ra khi người dùng rời khỏi trang web của bạn. (Apache nhận dạng socket đã đóng và giết phiên bản PHP.) Thời điểm chính bạn có thể đóng socket từ phía máy chủ là nếu bạn biết rằng sẽ không có dữ liệu trong một thời gian; thông điệp cuối cùng bạn gửi cho khách hàng là yêu cầu họ quay lại vào một thời điểm nhất định. Ví dụ: trong trường hợp phát trực tuyến chứng khoán của bạn, bạn có thể đóng kết nối lúc 8 giờ tối và yêu cầu khách hàng quay lại sau 8 giờ (giả sử NASDAQ mở cửa cho báo giá từ 4 giờ sáng đến 8 giờ tối). Tối thứ sáu bạn bảo họ quay lại vào sáng thứ hai. (Tôi có một cuốn sách sắp ra mắt về SSE và dành một vài phần về chủ đề này.)

"... nếu đúng như vậy, PHP không phải là công nghệ phù hợp cho loại máy chủ này. Nhưng tất cả các bản demo mà tôi đã xem cho đến nay đều ngụ ý rằng PHP phù hợp với điều này, đó là lý do tại sao tôi lại như vậy bối rối..."

Mọi người tranh luận rằng PHP không phải là công nghệ phù hợp cho các trang web bình thường và họ đã đúng: bạn có thể làm điều đó với bộ nhớ và chu kỳ CPU ít hơn nhiều nếu bạn thay thế toàn bộ ngăn xếp LAMP của mình bằng C ++. Tuy nhiên, mặc dù vậy, PHP cung cấp sức mạnh cho hầu hết các trang web hiện có. Nó là một ngôn ngữ rất hiệu quả cho công việc web, do sự kết hợp của cú pháp giống C quen thuộc và rất nhiều thư viện, và là một ngôn ngữ an ủi cho các nhà quản lý vì có rất nhiều lập trình viên PHP để thuê, nhiều sách và các tài nguyên khác, và một số thư viện lớn các trường hợp sử dụng (ví dụ: Facebook và Wikipedia). Về cơ bản, đó cũng là những lý do bạn có thể chọn PHP làm công nghệ phát trực tuyến của mình.

Thiết lập điển hình sẽ không phải là một kết nối với NASDAQ cho mỗi phiên bản PHP. Thay vào đó, bạn sẽ có một quy trình khác với một kết nối duy nhất tới NASDAQ, hoặc có thể là một kết nối duy nhất từ ​​mỗi máy trong cụm của bạn với NASDAQ. Sau đó, điều đó sẽ đẩy giá vào máy chủ SQL / NoSQL hoặc vào bộ nhớ dùng chung. Sau đó PHP chỉ cần thăm dò bộ nhớ được chia sẻ (hoặc cơ sở dữ liệu) và đẩy dữ liệu ra ngoài. Hoặc, có một máy chủ thu thập dữ liệu và mỗi phiên bản PHP sẽ mở một kết nối socket đến máy chủ đó. Máy chủ thu thập dữ liệu đẩy ra các bản cập nhật cho từng máy khách PHP của nó, khi nó nhận được chúng và đến lượt chúng, chúng sẽ đẩy dữ liệu đó ra máy khách của họ.

Vấn đề chính về khả năng mở rộng khi sử dụng Apache + PHP để phát trực tuyến là bộ nhớ cho mỗi quy trình Apache. Khi bạn đạt đến giới hạn bộ nhớ của phần cứng, hãy đưa ra quyết định kinh doanh để thêm một máy khác vào cụm hoặc cắt Apache ra khỏi vòng lặp và viết một máy chủ HTTP chuyên dụng. Phần sau có thể được thực hiện bằng PHP để tất cả kiến ​​thức và mã hiện có của bạn có thể được sử dụng lại hoặc bạn có thể viết lại toàn bộ ứng dụng bằng một ngôn ngữ khác. Nhà phát triển thuần túy trong tôi sẽ viết một máy chủ HTTP chuyên dụng, được sắp xếp hợp lý bằng C ++. Người quản lý trong tôi sẽ thêm một hộp khác.


Khi mỗi quá trình kết nối Apache tiêu tốn bộ nhớ, sẽ tốt hơn nếu sử dụng Nginx thay thế?
Zhang Buzz

@ZhangBuzz Nơi tôi đã nói Apache + PHP, nó thực sự có nghĩa là "máy chủ web + quy trình PHP", vì vậy về cơ bản không có sự khác biệt khi sử dụng một máy chủ web khác.
Darren Cook

Có thể máy chủ như thế này? github.com/hoaproject/Eventsource hoặc github.com/hhxsv5/php-sse
Enrique

Cũng lưu ý rằng Nginx có thể hiệu quả hơn cho việc này vì sử dụng ít bộ nhớ: blog.webfaction.com/2008/12/...
Enrique

31

Sự kiện do máy chủ gửi là để cập nhật thời gian thực từ phía máy chủ sang phía máy khách. Trong ví dụ đầu tiên, kết nối từ máy chủ không được giữ và máy khách cố gắng kết nối lại sau mỗi 3 giây và làm cho các sự kiện do máy chủ gửi không có sự khác biệt với cuộc thăm dò ajax.

Vì vậy, để kết nối bền vững, bạn cần quấn mã của mình trong một vòng lặp và kiểm tra các bản cập nhật liên tục.

PHP dựa trên luồng và nhiều người dùng được kết nối hơn sẽ làm cho máy chủ cạn kiệt tài nguyên. Điều này có thể được giải quyết bằng cách kiểm soát thời gian thực thi tập lệnh và kết thúc tập lệnh khi nó vượt quá một khoảng thời gian (tức là 10 phút). Các EventSourceAPI sẽ tự động kết nối lại để sự trì hoãn là trong một phạm vi chấp nhận được.

Ngoài ra, hãy xem thư viện PHP của tôi để biết các sự kiện do Máy chủ gửi , bạn có thể hiểu thêm về cách thực hiện các sự kiện do máy chủ gửi trong PHP và làm cho mã dễ dàng hơn.


5
Bạn có thể giải thích thêm về "Điều này có thể được giải quyết bằng cách kiểm soát thời gian thực hiện tập lệnh và kết thúc tập lệnh khi nó vượt quá một khoảng thời gian"? Nếu bạn có quá nhiều người dùng, liệu có thực sự cải thiện việc sử dụng tài nguyên nhiều bằng cách đóng kết nối vì người dùng sẽ kết nối lại sau 3 giây không?
Luke

4

Tôi nhận thấy rằng sse techink gửi một vài dữ liệu trễ đến máy khách (somtething giống như đảo ngược dữ liệu gộp techink từ trang máy khách, ví dụ dữ liệu gộp Ajax.) Vì vậy để khắc phục vấn đề này, tôi đã thực hiện điều này tại trang sseServer.php:

<?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'] );
?>

và sse.php là:

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

Lưu ý rằng tại sseSerer.php, tôi bắt đầu một phiên và sử dụng một biến phiên! để khắc phục sự cố.

Ngoài ra, tôi gọi sseServer.php qua Ajax (đăng và đặt giá trị thành variable message) mỗi khi tôi muốn "cập nhật" thông báo.

Bây giờ tại jQuery (javascript), tôi làm điều gì đó như sau: 1) Tôi khai báo một biến toàn cục var timeStamp = 0; Thứ 2) tôi sử dụng thuật toán tiếp theo:

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 */

Tại dòng: $.notify("Please refresh "+event.data, "info"); bạn có thể xử lý tin nhắn không.

Đối với trường hợp của tôi, tôi đã từng gửi thông báo jQuery.

Bạn có thể sử dụng POSIX PIPES hoặc DB Table để chuyển "thông báo" qua POST vì sseServer.php thực hiện một cái gì đó giống như một "vòng lặp vô hạn".

Vấn đề của tôi tại thời điểm đó là đoạn mã trên KHÔNG GỬI "thông báo" cho tất cả các máy khách mà chỉ gửi cho từng cặp (ứng dụng được gọi là sseServer.php hoạt động riêng lẻ cho từng cặp) vì vậy tôi sẽ thay đổi kỹ thuật và thành một Cập nhật DB từ trang mà tôi muốn kích hoạt "thông báo" và sau đó thay vào đó sseServer.php để nhận thông báo qua POST, nó sẽ lấy nó từ bảng DB.

Tôi hy vọng rằng tôi có sự giúp đỡ!


3

Đây thực sự là một câu hỏi cấu trúc về ứng dụng của bạn. Các sự kiện thời gian thực là thứ mà bạn muốn nghĩ đến ngay từ đầu, vì vậy bạn có thể thiết kế ứng dụng của mình xung quanh nó. Nếu bạn đã viết một ứng dụng chỉ chạy một loạt các mysql(i)_queryphương thức ngẫu nhiên bằng cách sử dụng các truy vấn chuỗi và không chuyển chúng qua bất kỳ loại trung gian nào, thì nhiều khi bạn sẽ không có lựa chọn nào khác ngoài việc viết lại nhiều ứng dụng của mình hoặc làm thăm dò ý kiến ​​phía máy chủ liên tục.

Tuy nhiên, nếu bạn quản lý các thực thể của mình dưới dạng các đối tượng và chuyển chúng qua một số loại lớp trung gian, bạn có thể tham gia vào quá trình đó. Hãy xem ví dụ này:

<?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
    }
}

Trong ứng dụng của bạn, khi bạn sẵn sàng lưu:

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

Đây không phải là ví dụ duyên dáng nhất nhưng nó sẽ đóng vai trò như một khối xây dựng tốt. Bạn có thể kết nối vào lớp tồn tại thực tế của mình để xử lý việc kích hoạt các sự kiện này. Sau đó, bạn có được chúng ngay lập tức (theo thời gian thực có thể nhận được) mà không cần đập máy chủ (vì bạn không cần phải liên tục truy vấn cơ sở dữ liệu của mình và xem mọi thứ có thay đổi hay không).

Rõ ràng là bạn sẽ không bắt được các thay đổi thủ công đối với cơ sở dữ liệu theo cách này - nhưng nếu bạn đang làm bất cứ điều gì theo cách thủ công với cơ sở dữ liệu của mình với bất kỳ tần suất nào, bạn nên:

  • Khắc phục sự cố yêu cầu bạn phải thực hiện thay đổi thủ công
  • Xây dựng một công cụ để đẩy nhanh quá trình và kích hoạt các sự kiện này

3
Colin - cảm ơn câu trả lời của bạn. Lỗi của tôi - câu hỏi của tôi không rõ ràng - nhưng đây thực sự không phải là điều tôi đang hỏi. Ý tôi muốn hỏi là ... Nếu bạn đang sử dụng PHP làm "máy chủ" của mình - thì tập lệnh PHP mà bạn gọi từ EventSource trong máy khách của bạn có cần phải chạy trong toàn bộ thời gian máy khách được kết nối với nó không? Điều đó có nghĩa là, nếu bạn có 1.000 người dùng đồng thời, bạn có 1.000 luồng riêng biệt chạy 1.000 phiên bản đồng thời của tập lệnh PHP của bạn? Điều đó có khả thi không? Và, làm thế nào bạn biết khi nào kết thúc tập lệnh php (giả sử nó lặp lại để vẫn "sống")?
mattstuehler

-7

Về cơ bản, PHP không phải là công nghệ phù hợp cho những thứ này. Có, bạn có thể làm cho nó hoạt động, nhưng nó sẽ là một thảm họa khi tải cao. Chúng tôi chạy máy chủ lưu trữ gửi tín hiệu thay đổi cổ phiếu qua websockets đến hàng chục người dùng - và Nếu chúng tôi sử dụng php cho việc đó ... Chà, chúng tôi có thể, nhưng những chu kỳ tự chế đó - chỉ là một cơn ác mộng. Mỗi kết nối sẽ tạo ra một quá trình riêng biệt trên máy chủ hoặc bạn phải xử lý các kết nối từ một số loại cơ sở dữ liệu.

Chỉ cần sử dụng nodejs và socket.io. Nó sẽ cho phép bạn dễ dàng khởi động và có một máy chủ chạy trong vài ngày. Nodejs cũng có những hạn chế riêng, nhưng đối với kết nối websockets (và SSE) bây giờ là công nghệ mạnh mẽ nhất của nó.

Và nữa - SSE không tốt như nó có vẻ. Ưu điểm duy nhất của websockets - là các gói tin đang được giải nén nguyên bản (ws không được giải nén), nhưng nhược điểm là SSE là kết nối một phía. Người dùng của bạn, nếu anh ta muốn thêm một ký hiệu chứng khoán khác vào subscripton, sẽ phải thực hiện yêu cầu ajax (bao gồm tất cả các rắc rối với kiểm soát nguồn gốc và yêu cầu sẽ chậm). Trong websockets client và sever giao tiếp theo cả hai cách trong một kết nối đã mở duy nhất, vì vậy nếu người dùng gửi tín hiệu giao dịch hoặc đăng ký báo giá, anh ta chỉ cần gửi một chuỗi trong kết nối đã mở. Và nó nhanh chóng.


2
Về cơ bản, bạn có thể sử dụng React.php theo cách tương tự như vòng lặp sự kiện trong node.js.
Matěj Koubík

3
Mặc dù thật tốt khi nói rằng PHP không phải là lựa chọn tốt nhất, nhưng tôi nghĩ ít nhất bạn nên đưa vào một thứ mà OP yêu cầu.
Nishchal Gautam
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.