Tag Archive stream

NGINX: แสดงผลลัพธ์ทันที

บางครั้งที่ทำงานที่ใช้เวลานาน เช่น การเปลี่ยนค่าข้อมูลในตารางทั้งตาราง แต่ php จะแสดงผลลัพธ์ออกมาหลังจากที่มันทำงานเสร็จแล้ว user ก็คิดว่ามัน error ก็เลยปิดไปซะ T_T วิธีที่ใช้ได้คือ การปล่อย flush output ออกมาเป็นระยะโดยเล่นกับพวก function ob_flush เหมือนในที่เคยเขียนไว้ jQuery: Ajax รับข้อมูลแบบ stream แต่กลับไม่ทำงานบน nginx ซะงั้น

วิธีแก้คือใส่[code language=”php”]header(‘X-Accel-Buffering: no’);[/code]เพื่อให้ nginx มันส่งข้อมูลที่อมเอาไว้ออกมาซะดีๆ (ถ้าไม่ echo อะไรเลยระหว่างการทำงานเอนจิ้นเอ็ก มันก็ error อยู่ดีเอาใจยากจริงๆ) นี่เป็นตัวอย่าง code ใหม่[code language=”php” title=”ob.liveResults.php”]<?php
header(‘X-Accel-Buffering: no’);

set_time_limit(0);

ob_implicit_flush(true);
ob_end_flush();

$steps = 100;
for ($step = 1; $step <= $steps; $step++) {
$time = rand(1, 10);
sleep($time);
echo ‘<br>ขั้นตอนที่ ‘, $step, ‘ จาก ‘, $steps, ‘ ใช้เวลาทำงาน ‘, $time, ‘ วินาที’;
}[/code]

อ่านเพิ่มเติม

jQuery: Ajax รับข้อมูลแบบ stream

บางงานต้องใช้เวลาในการทำงาน ถ้าปล่อยให้ user รออย่างเดียวก็จะกังวลว่ามันจะทำงานสำเร็จหรือมี error รึเปล่า เคยเขียนวิธี jQuery Ajax แบบอนุกรมตามลำดับ ไว้ แต่อาจจะไม่เหมาะถ้า task บางอย่างมันยาวนานกว่านั้น ถึงลองเขียนแบบ flush stream / streaming ดู

ตัวจำลองเวลาโหลดโดยสุ่มถ่วงเวลาให้เหมือนจริง[code language=”php” title=”stream.php”]<?php

set_time_limit(0);

header(‘Cache-Control: no-cache’);
header(‘Content-Type: text/event-stream’);

function task($ad, $message, $progress = ”)
{
$data = [
‘id’ => $ad,
‘message’ => $message,
‘progress’ => $progress,
];

echo json_encode($data);

ob_flush();
flush();
}

/* loop processing */
for ($a = 1; $a <= 10; $a++) {
task($a, ‘on iteration ‘ . $a . ‘ of 10’, $a * 10);

sleep(rand(1, 10));
}

task(‘CLOSE’, ‘Process complete’);[/code]

ตัวอย่างการใช้ jQuery.ajax()[code language=”html” title=”stream_jquery.html”]<!DOCTYPE>
<html>

<head>
<title>Flushed ajax test</title>
<meta charset="UTF-8" />
<style>
#results {
background: #eee;
border: 1px solid #c00000;
height: 250px;
overflow: auto;
padding: 10px;
width: 98%;
}
</style>
</head>

<body>
<div id="results" style=""></div>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.8.3/jquery.min.js"></script>
<script>
var last_response_len = false;
$.ajax(‘stream.php’, {
xhrFields: {
onprogress: function(e) {
var this_response, response = e.currentTarget.response;
if (last_response_len === false) {
this_response = response;
last_response_len = response.length;
} else {
this_response = response.substring(last_response_len);
last_response_len = response.length;
}
console.log(this_response);
data = $.parseJSON(this_response);
console.log(data);
$(‘#results’).append(‘<br>’ + data.message);
}
}
})
.done(function(data) {
console.log(‘Complete response = ‘ + data);
$(‘#results’).append(‘<br><br>Complete response = ‘ + data);
})
.fail(function(data) {
console.log(‘Error: ‘, data);
});
console.log(‘Request Sent’);
</script>
</body>

</html>[/code]

ถ้าดูใน console.log จะเห็นว่าตัวแปร data มันจะโดนเพิ่มขึ้นมาเรื่องๆ ดังนั้นถ้ามี response มาจาก server เยอะๆ ก็ต้องแก้ไข code ด้วย

update วิธีเขียนให้ง่ายขึ้นและทำงานบน nginx ได้ ตามที่เขียนใน NGINX: แสดงผลลัพธ์ทันที[code language=”php” title=”stream.php”]<?php

set_time_limit(0);

header(‘Cache-Control: no-cache’);
header(‘Content-Type: text/event-stream’);
header(‘X-Accel-Buffering: no’);

set_time_limit(0);

ob_implicit_flush(true);
ob_end_flush();

function task($ad, $message, $progress = ”)
{
$data = [
‘id’ => $ad,
‘message’ => $message,
‘progress’ => $progress,
];

echo json_encode($data);
}

/* loop processing */
for ($a = 1; $a <= 10; $a++) {
task($a, ‘on iteration ‘ . $a . ‘ of 10’, $a * 10);

sleep(rand(1, 10));
}

task(‘CLOSE’, ‘Process complete’);
[/code]