답변:
처음에 생각했던 것보다 간단합니다. 기본적으로 보내려는 데이터를 사용할 수있을 때까지 (예 : 새 메시지가 도착할 때) 아무 작업도 수행하지 않는 페이지가 있습니다.
다음은 2-10 초 후에 간단한 문자열을 보내는 매우 기본적인 예입니다. 오류 404를 리턴 할 가능성의 3 분의 1 (다음 Javascript 예제에서 오류 처리를 표시하기 위해)
msgsrv.php
<?php
if(rand(1,3) == 1){
/* Fake an error */
header("HTTP/1.0 404 Not Found");
die();
}
/* Send a string after a random number of seconds (2-10) */
sleep(rand(2,10));
echo("Hi! Have a random number: " . rand(1,10));
?>
참고 : 실제 사이트의 경우 Apache와 같은 일반 웹 서버에서이를 실행하면 모든 "작업자 스레드"가 빠르게 연결되어 다른 요청에 응답 할 수 없게됩니다.이 방법에는 여러 가지 방법이 있지만 작성하는 것이 좋습니다. 파이썬의 twisted 와 같은 "장점 서버" 는 요청 당 하나의 스레드에 의존하지 않습니다. cometD 는 널리 사용되는 언어이며 여러 언어로 제공되며 Tornado 는 이러한 작업을 위해 특별히 만들어진 새로운 프레임 워크입니다 (FriendFeed의 긴 폴링 코드 용으로 빌드 됨). 그러나 간단한 예로 Apache가 적합합니다. ! 이 스크립트는 어떤 언어로도 쉽게 작성할 수 있습니다 (아파치 / PHP는 매우 일반적이므로 로컬로 실행하고 있습니다)
그런 다음 Javascript에서 위 파일 ( msg_srv.php
) 을 요청하고 응답을 기다립니다. 하나를 얻으면 데이터에 따라 행동합니다. 그런 다음 파일을 요청하고 다시 기다렸다가 데이터를 처리하고 반복하십시오.
다음은 이러한 페이지의 예입니다. 페이지가로드되면 msgsrv.php
파일에 대한 초기 요청을 전송 합니다. 성공하면 #messages
div에 메시지를 추가 한 후 1 초 후에 waitForMsg 함수를 다시 호출합니다. 대기를 트리거합니다.
1 초 setTimeout()
는 실제로 기본 속도 제한 기이며, 이것 없이는 잘 작동하지만 msgsrv.php
항상 즉시 오류 (예 : 구문 오류와 함께)가 반환되면 브라우저가 넘쳐 빠르게 정지 할 수 있습니다. 파일에 유효한 JSON 응답이 포함되어 있는지 확인하거나 분당 총 요청 수를 유지하고 적절하게 일시 중지하는 것이 좋습니다.
페이지 오류가 발생하면 #messages
div에 오류가 추가되고 15 초 동안 기다렸다가 다시 시도합니다 (각 메시지 후 1 초 동안 대기하는 방식과 동일)
이 방법의 좋은 점은 복원력이 뛰어나다는 것입니다. 클라이언트의 인터넷 연결이 끊어지면 시간 초과되고 다시 연결됩니다. 이는 폴링 작동 시간에 내재되어 있으며 복잡한 오류 처리가 필요하지 않습니다.
어쨌든 long_poller.htm
jQuery 프레임 워크를 사용 하는 코드 :
<html>
<head>
<title>BargePoller</title>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.2.6/jquery.min.js" type="text/javascript" charset="utf-8"></script>
<style type="text/css" media="screen">
body{ background:#000;color:#fff;font-size:.9em; }
.msg{ background:#aaa;padding:.2em; border-bottom:1px #000 solid}
.old{ background-color:#246499;}
.new{ background-color:#3B9957;}
.error{ background-color:#992E36;}
</style>
<script type="text/javascript" charset="utf-8">
function addmsg(type, msg){
/* Simple helper to add a div.
type is the name of a CSS class (old/new/error).
msg is the contents of the div */
$("#messages").append(
"<div class='msg "+ type +"'>"+ msg +"</div>"
);
}
function waitForMsg(){
/* This requests the url "msgsrv.php"
When it complete (or errors)*/
$.ajax({
type: "GET",
url: "msgsrv.php",
async: true, /* If set to non-async, browser shows page as "Loading.."*/
cache: false,
timeout:50000, /* Timeout in ms */
success: function(data){ /* called when request to barge.php completes */
addmsg("new", data); /* Add response to a .msg div (with the "new" class)*/
setTimeout(
waitForMsg, /* Request next message */
1000 /* ..after 1 seconds */
);
},
error: function(XMLHttpRequest, textStatus, errorThrown){
addmsg("error", textStatus + " (" + errorThrown + ")");
setTimeout(
waitForMsg, /* Try again after.. */
15000); /* milliseconds (15seconds) */
}
});
};
$(document).ready(function(){
waitForMsg(); /* Start the inital request */
});
</script>
</head>
<body>
<div id="messages">
<div class="msg old">
BargePoll message requester!
</div>
</div>
</body>
</html>
sleep(rand(2,10));
무엇 을하는 것 입니까? 아무것도하지 않으려면 100 밀리 초마다 데이터베이스를 폴링합니까? 언제 죽기로 결정합니까?
편집 : (모든 사람이 여기에 코드를 붙여 넣었으므로)
long-polling 및 slosh를 사용하는 완전한 JSON 기반 다중 사용자 채팅 입니다. 이것은 호출을 수행하는 방법에 대한 데모 이므로 XSS 문제를 무시하십시오. 아무도 먼저 소독하지 않고 배포해서는 안됩니다.
클라이언트는 항상 서버에 연결되어 있으며 누구나 메시지를 보내면 즉시 거의 즉시 볼 수 있어야합니다.
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<!-- Copyright (c) 2008 Dustin Sallings <dustin+html@spy.net> -->
<html lang="en">
<head>
<title>slosh chat</title>
<script type="text/javascript"
src="http://code.jquery.com/jquery-latest.js"></script>
<link title="Default" rel="stylesheet" media="screen" href="style.css" />
</head>
<body>
<h1>Welcome to Slosh Chat</h1>
<div id="messages">
<div>
<span class="from">First!:</span>
<span class="msg">Welcome to chat. Please don't hurt each other.</span>
</div>
</div>
<form method="post" action="#">
<div>Nick: <input id='from' type="text" name="from"/></div>
<div>Message:</div>
<div><textarea id='msg' name="msg"></textarea></div>
<div><input type="submit" value="Say it" id="submit"/></div>
</form>
<script type="text/javascript">
function gotData(json, st) {
var msgs=$('#messages');
$.each(json.res, function(idx, p) {
var from = p.from[0]
var msg = p.msg[0]
msgs.append("<div><span class='from'>" + from + ":</span>" +
" <span class='msg'>" + msg + "</span></div>");
});
// The jQuery wrapped msgs above does not work here.
var msgs=document.getElementById("messages");
msgs.scrollTop = msgs.scrollHeight;
}
function getNewComments() {
$.getJSON('/topics/chat.json', gotData);
}
$(document).ready(function() {
$(document).ajaxStop(getNewComments);
$("form").submit(function() {
$.post('/topics/chat', $('form').serialize());
return false;
});
getNewComments();
});
</script>
</body>
</html>
토네이도 는 긴 폴링을 위해 설계되었으며 서버 코드와 JS 클라이언트 코드를 포함하여 / examples / chatdemo에 아주 적은 (수백 줄의 Python) 채팅 앱 을 포함합니다. 다음과 같이 작동합니다.
클라이언트는 JS를 사용하여 이후의 업데이트를 요청합니다 (마지막 메시지 수). 서버 URLHandler는이를 수신하고 클라이언트에 대한 콜백을 큐에 추가합니다.
서버가 새 메시지를 받으면 onmessage 이벤트가 발생하고 콜백을 반복하여 메시지를 보냅니다.
클라이언트 측 JS는 메시지를 수신하여 페이지에 추가 한 다음이 새 메시지 ID 이후로 업데이트를 요청합니다.
클라이언트가 정상적인 비동기 AJAX 요청처럼 보이지만 다시 돌아 오는 데 시간이 오래 걸릴 것으로 예상합니다.
그러면 서버는 다음과 같습니다.
while (!hasNewData())
usleep(50);
outputNewData();
따라서 AJAX 요청은 서버에 전송되며, 아마도 마지막 업데이트 시간을 포함하여 hasNewData()
이미 가지고있는 데이터를 알 수 있습니다. 그런 다음 서버는 새 데이터를 사용할 수있을 때까지 루프 휴면 상태에 있습니다. 그 동안 AJAX 요청은 여전히 연결되어 있으며 데이터를 기다리는 중입니다. 마지막으로, 새 데이터를 사용할 수있게되면 서버는 해당 데이터를 AJAX 요청에 제공하고 연결을 닫습니다.
다음 은 C #에서 롱 폴링에 사용하는 클래스입니다. 기본적으로 6 개의 수업이 있습니다 (아래 참조).
다음은 PHP 및 jQuery를 사용하여 긴 폴링을 수행하는 방법에 대한 멋진 5 분 스크린 캐스트입니다. http://screenr.com/SNH
코드는 위의 dbr 예제 와 매우 유사합니다 .
다음은 Erik Dubbelboer 가 Content-type: multipart/x-mixed-replace
헤더를 사용하는 PHP의 간단한 폴링 예제입니다 .
<?
header('Content-type: multipart/x-mixed-replace; boundary=endofsection');
// Keep in mind that the empty line is important to separate the headers
// from the content.
echo 'Content-type: text/plain
After 5 seconds this will go away and a cat will appear...
--endofsection
';
flush(); // Don't forget to flush the content to the browser.
sleep(5);
echo 'Content-type: image/jpg
';
$stream = fopen('cat.jpg', 'rb');
fpassthru($stream);
fclose($stream);
echo '
--endofsection
';
그리고 여기 데모가 있습니다 :
아래는 Inform8 Web을 위해 개발 한 긴 폴링 솔루션입니다. 기본적으로 클래스를 재정의하고 loadData 메서드를 구현합니다. loadData가 값을 반환하거나 작업 시간이 초과되면 결과가 인쇄되고 반환됩니다.
스크립트 처리에 30 초 이상 걸릴 수있는 경우 set_time_limit () 호출을 더 길게 변경해야 할 수도 있습니다.
아파치 2.0 라이센스. github의 최신 버전 https://github.com/ryanhend/Inform8/blob/master/Inform8-web/src/config/lib/Inform8/longpoll/LongPoller.php
라이언
abstract class LongPoller {
protected $sleepTime = 5;
protected $timeoutTime = 30;
function __construct() {
}
function setTimeout($timeout) {
$this->timeoutTime = $timeout;
}
function setSleep($sleep) {
$this->sleepTime = $sleepTime;
}
public function run() {
$data = NULL;
$timeout = 0;
set_time_limit($this->timeoutTime + $this->sleepTime + 15);
//Query database for data
while($data == NULL && $timeout < $this->timeoutTime) {
$data = $this->loadData();
if($data == NULL){
//No new orders, flush to notify php still alive
flush();
//Wait for new Messages
sleep($this->sleepTime);
$timeout += $this->sleepTime;
}else{
echo $data;
flush();
}
}
}
protected abstract function loadData();
}
코드 dbr 감사합니다 . long_poller.htm에 작은 오타 가 있습니다.
1000 /* ..after 1 seconds */
나는 그것이 있어야한다고 생각
"1000"); /* ..after 1 seconds */
작동합니다.
관심있는 사람들을 위해 Django와 동등한 것을 시도했습니다. 긴 폴링을 위해 lp 로 새 장고 프로젝트를 시작하십시오 .
django-admin.py startproject lp
메시지 서버 의 앱 msgsrv 를 호출하십시오 .
python manage.py startapp msgsrv
settings.py 에 다음 줄을 추가하여 템플릿 디렉토리를 만듭니다 .
import os.path
PROJECT_DIR = os.path.dirname(__file__)
TEMPLATE_DIRS = (
os.path.join(PROJECT_DIR, 'templates'),
)
urls.py 에서 URL 패턴을 다음과 같이 정의하십시오 .
from django.views.generic.simple import direct_to_template
from lp.msgsrv.views import retmsg
urlpatterns = patterns('',
(r'^msgsrv\.php$', retmsg),
(r'^long_poller\.htm$', direct_to_template, {'template': 'long_poller.htm'}),
)
그리고 msgsrv / views.py 는 다음과 같아야 합니다 :
from random import randint
from time import sleep
from django.http import HttpResponse, HttpResponseNotFound
def retmsg(request):
if randint(1,3) == 1:
return HttpResponseNotFound('<h1>Page not found</h1>')
else:
sleep(randint(2,10))
return HttpResponse('Hi! Have a random number: %s' % str(randint(1,10)))
마지막으로, templates / long_poller.htm 은 오타가 수정 된 위와 동일해야합니다. 도움이 되었기를 바랍니다.
"15000"
구문 오류입니다. setTimeout은 두 번째 매개 변수로 정수를 사용합니다.
이것은 PHP가 매우 좋지 않은 시나리오 중 하나입니다. 앞에서 언급했듯이 모든 Apache 작업자를 다음과 같이 매우 빠르게 묶을 수 있습니다. PHP는 시작, 실행, 중지를 위해 만들어졌습니다. 시작, 대기 ... 실행, 중지를 위해 만들어지지 않았습니다. 서버를 매우 빠르게 시작하고 놀라운 확장 문제가 있음을 알게됩니다.
즉, 여전히 PHP 로이 작업을 수행 할 수 있으며 nginx HttpPushStreamModule을 사용하여 서버를 죽이지 않도록 할 수 있습니다 : http://wiki.nginx.org/HttpPushStreamModule
Apache (또는 다른 것) 앞에 nginx를 설정하면 동시 연결을 열어 놓는 작업을 처리합니다. 백그라운드 작업으로 할 수있는 내부 주소로 데이터를 보내거나 새로운 요청이 올 때마다 대기중인 사람들에게 메시지를 보내도록하여 페이로드로 응답하기 만하면됩니다. 이렇게하면 긴 폴링 중에 PHP 프로세스가 열려 있지 않습니다.
이것은 PHP에만 국한된 것이 아니며 모든 백엔드 언어와 함께 nginx를 사용하여 수행 할 수 있습니다. 동시 개방 연결로드는 Node.js와 동일하므로 가장 큰 장점은 이와 같은 이유로 NEEDING Node에서 벗어날 수 있다는 것입니다.
긴 폴링을 달성하기 위해 다른 언어 라이브러리를 언급하는 다른 사람들이 많이 있습니다. 그럴만한 이유가 있습니다. PHP는 이러한 유형의 행동을 위해 자연스럽게 잘 구축되지 않았습니다.
WS-I 그룹 은 분명히 상호 운용 되는 Glass Fish 및 .NET 구현 이 있는 "신뢰할 수있는 보안 프로필" 을 게시했습니다 .
운이 좋으면 Javascript 구현도 있습니다.
HTTP Duplex 를 사용하는 Silverlight 구현도 있습니다 . 푸시가 발생할 때 콜백을 얻기 위해 Javascript를 Silverlight 객체에 연결할 수 있습니다 .
도 있습니다 상용 유료 버전 뿐만 아니라이.
ASP.NET MVC 구현의 경우 NuGet에서 사용할 수 있는 SignalR 을 참조하십시오. NuGet은 종종 커밋이 자주 발생 하는 Git 소스 에서 오래되었습니다 .
Scott Hanselman 의 블로그에서 SignalR에 대해 자세히 알아보십시오.
libevent로 빌드 된 C1000K C ++ 혜성 서버 인 icomet ( https://github.com/ideawu/icomet )을 사용해 볼 수 있습니다 . icomet은 JavaScript 라이브러리도 제공합니다.
var comet = new iComet({
sign_url: 'http://' + app_host + '/sign?obj=' + obj,
sub_url: 'http://' + icomet_host + '/sub',
callback: function(msg){
// on server push
alert(msg.content);
}
});
icomet은 Safari (iOS, Mac), IE (Windows), Firefox, Chrome 등 광범위한 브라우저 및 OS를 지원합니다.
가장 간단한 NodeJS
const http = require('http');
const server = http.createServer((req, res) => {
SomeVeryLongAction(res);
});
server.on('clientError', (err, socket) => {
socket.end('HTTP/1.1 400 Bad Request\r\n\r\n');
});
server.listen(8000);
// the long running task - simplified to setTimeout here
// but can be async, wait from websocket service - whatever really
function SomeVeryLongAction(response) {
setTimeout(response.end, 10000);
}
response
미들웨어에서 얻을 수 있는 뛰어난 현명한 프로덕션 시나리오 . 수행해야 할 작업을 수행하고, 긴 폴링 된 모든 메소드를 맵 또는 다른 플로우에 표시되는 <Response> response.end()
것으로 범위를 지정하고 준비가 될 때마다 호출 할 수 있습니다. 긴 폴링 연결에는 특별한 것이 없습니다. 나머지는 응용 프로그램을 정상적으로 구성하는 방법입니다.
당신이 내가 스코핑함으로써 의미하는 것을 모른다면, 이것은 당신에게 아이디어를 줄 것입니다
const http = require('http');
var responsesArray = [];
const server = http.createServer((req, res) => {
// not dealing with connection
// put it on stack (array in this case)
responsesArray.push(res);
// end this is where normal api flow ends
});
server.on('clientError', (err, socket) => {
socket.end('HTTP/1.1 400 Bad Request\r\n\r\n');
});
// and eventually when we are ready to resolve
// that if is there just to ensure you actually
// called endpoint before the timeout kicks in
function SomeVeryLongAction() {
if ( responsesArray.length ) {
let localResponse = responsesArray.shift();
localResponse.end();
}
}
// simulate some action out of endpoint flow
setTimeout(SomeVeryLongAction, 10000);
server.listen(8000);
보시다시피, 모든 연결에 실제로 응답 할 수 있습니다. 이 id
당신이 API 호출에서지도 및 액세스 특정을 사용할 수 있어야하므로 모든 요청에 대해.