ไม่ได้ส่งความยาวเนื้อหาเมื่อเปิดใช้งานการบีบอัด gzip ใน Apache?


13

ฉันขอขอบคุณที่ช่วยให้เข้าใจพฤติกรรมของ Apache นี้

ฉันกำลังสื่อสารกับ PHP จากแอป iPhone Objective-C ใน application / json การบีบอัด Gzip เปิดใช้งานบนเซิร์ฟเวอร์และลูกค้าร้องขอ

จาก. htaccess ของฉัน:

AddOutputFilterByType DEFLATE text/html text/plain text/xml application/x-httpd-php application/json

สำหรับคำขอขนาดเล็ก Apache กำลังตั้งค่าส่วนหัว 'ความยาวเนื้อหา' ตัวอย่างเช่น (ค่าเหล่านี้เป็นเอาต์พุตใน Objective-C จากส่วนหัว):

Connection = "Keep-Alive";
"Content-Encoding" = gzip;
"Content-Length" = 185;     <-------------
"Content-Type" = "application/json";
Date = "Wed, 22 Sep 2010 12:20:27 GMT";
"Keep-Alive" = "timeout=3, max=149";
Server = Apache;
Vary = "Accept-Encoding";
"X-Powered-By" = "PHP/5.2.13";
"X-Uncompressed-Content-Length" = 217;

X-Uncompressed-Content-Lengthเป็นส่วนหัวที่ฉันเพิ่มการตั้งค่าให้กับขนาดของสตริง JSON ที่ไม่บีบอัด

อย่างที่คุณเห็นคำขอนี้มีขนาดเล็กมาก (217 ไบต์)

นี่คือส่วนหัวจากคำขอที่มีขนาดใหญ่กว่า (282888 ไบต์):

Connection = "Keep-Alive";
"Content-Encoding" = gzip;
"Content-Type" = "application/json";
Date = "Wed, 22 Sep 2010 12:20:29 GMT";
"Keep-Alive" = "timeout=3, max=148";
Server = Apache;
"Transfer-Encoding" = Identity;
Vary = "Accept-Encoding";
"X-Powered-By" = "PHP/5.2.13";
"X-Uncompressed-Content-Length" = 282888;

โปรดสังเกตว่าไม่ได้รับความยาวเนื้อหา

คำถามของฉัน:

  1. ทำไม Apache ไม่ส่งความยาวเนื้อหาสำหรับคำขอที่ใหญ่กว่า
  2. ความจริงที่ว่า 'Contend-Encoding = gzip' ถูกตั้งค่าหมายความว่าการบีบอัด gzip ยังคงทำงานตามคำขอที่ใหญ่ขึ้นแม้ว่าฉันจะไม่สามารถตรวจสอบความแตกต่างของขนาดได้หรือไม่
  3. มีวิธีที่ฉันสามารถให้ Apache รวมความยาวเนื้อหาจริงสำหรับคำขอที่มีขนาดใหญ่ขึ้นเหล่านี้เพื่อรายงานการใช้ข้อมูลให้กับผู้ใช้อย่างแม่นยำมากขึ้นหรือไม่?

แอพนี้สามารถใช้กับแผนข้อมูลที่มีราคาแพงดังนั้นความปรารถนาของฉันในการรายงานการใช้งานจริงให้กับผู้ใช้ไม่ใช่การใช้งานที่สูงเกินจริง 30-70% (ไม่กี่ร้อย KB พิเศษอาจไม่เหมือนมาก - แต่แผนเหล่านี้อาจมีราคาระหว่าง $ 1 และ $ 10 ต่อ MB!)

ขอบคุณล่วงหน้า.

คำตอบ:


14

นอกเหนือจากคำตอบของ Martin Fjordvalds:

Apache ใช้การเข้ารหัสแบบ chunked เฉพาะในกรณีที่ขนาดไฟล์ที่บีบอัดมีขนาดใหญ่กว่า DeflateBufferSize การเพิ่มขนาดบัฟเฟอร์นี้จะป้องกันเซิร์ฟเวอร์โดยใช้การเข้ารหัสแบบ chunked สำหรับไฟล์ขนาดใหญ่ทำให้ความยาวเนื้อหาถูกส่งแม้สำหรับข้อมูลที่ซิป

ข้อมูลเพิ่มเติมสามารถดูได้ที่นี่: http://httpd.apache.org/docs/2.2/mod/mod_deflate.html#deflatebuffersize


ทำได้ดีนี่. นี่อาจเป็นวิธีที่เร็วที่สุดในการแก้ปัญหานี้ หากใครต้องการระดับที่สูงขึ้นของการปรับแต่ง (เช่นการร้องขอบางอย่างไม่ใช่คำขออื่น) ดูคำตอบserverfault.com/a/183856/54957ของฉันสำหรับวิธีแก้ปัญหาด้วยตนเอง
William Denniss

7

เสียงเหมือน Apache กำลังทำการเข้ารหัสแบบ chunked ซึ่งหมายความว่าสามารถส่งข้อมูลได้เนื่องจากกำลังถูก gzipped แทนที่จะรอการตอบกลับแบบเต็มเพื่อให้ gzipped เป็นวิธีปฏิบัติที่ค่อนข้างมาตรฐานฉันไม่คุ้นเคยกับ Apache มากพอที่จะบอกว่าสามารถใช้งานได้หรือไม่


ขอบคุณสำหรับข้อมูลคุณชี้ให้ฉันในทิศทางที่ถูกต้องและฉันแก้ไขมัน
William Denniss

ได้รับการยอมรับ สำหรับใครก็ตามที่อ่านคำถามนี้ - โปรดอ่านคำตอบของฉันสำหรับคำตอบอย่างละเอียด โดยทั่วไปคุณสามารถหลีกเลี่ยงการ chunking (และทำให้เนื้อหามีความยาวเป็นศูนย์) โดยการบัฟเฟอร์และบีบอัดการตอบกลับด้วยตนเอง
William Denniss

มันค่อนข้างสับสนว่าคำตอบที่ยอมรับไม่ใช่คำตอบของคำถามต้นฉบับ แต่เป็นสิ่งที่ช่วยให้คุณได้รับ บางทีคุณควรยอมรับคำตอบที่คุณโพสต์ด้านล่างเพื่อทำให้สิ่งต่าง ๆ ชัดเจนยิ่งขึ้น
redbmk

@redbmk fair point ฉันแค่ไม่อยากดูเนรคุณ ฟิลิปมีการแก้ไขที่สมบูรณ์แบบสำหรับเรื่องนี้ดังนั้นฉันจึงยอมรับเขาไปแล้ว
William Denniss

5

ตกลงฉันจัดการเพื่อแก้ปัญหานี้ ขณะที่ Martin F ชี้ให้เห็นอย่างถูกต้อง Apache กำลังตรวจสอบการตอบกลับดังนั้นจึงไม่ทราบขนาดเนื้อหา สำหรับหลาย ๆ คนสิ่งนี้เป็นสิ่งที่พึงประสงค์ (โหลดหน้าเร็วขึ้น) นี่เป็นค่าใช้จ่ายที่ไม่สามารถรายงานความคืบหน้าการดาวน์โหลด

สำหรับคนอย่างฉันที่ต้องการรายงานความคืบหน้าการดาวน์โหลดหากคุณใช้ Apache หรือ PHP สนับสนุน gzip โดยอัตโนมัติคุณก็สามารถทำได้เพียงเล็กน้อย การแก้ปัญหาคือการทำด้วยตนเอง ง่ายกว่าฟัง:

หากคุณกำลังส่งไฟล์ทั้งหมดนี่เป็นตัวอย่างที่ดีใน PHP ที่จะบังคับให้โหลดไฟล์เดียว (ด้วยความยาวเนื้อหา): http://www.php.net/manual/en/function.ob-start.php # 94741

หากคุณกำลังส่งข้อมูลที่สร้างขึ้นให้ใช้ gzencode เพื่อเข้ารหัสข้อมูลของคุณเช่นในตัวอย่างด้านบน สิ่งที่ต้องมีก่อนคือข้อมูลเอาท์พุททั้งหมดของคุณจะถูกเก็บไว้ในตัวแปร (คุณสามารถใช้ ob_start เพื่อช่วยในเรื่องนี้หากคุณต้องการบัฟเฟอร์จากนั้นรับเนื้อหาของบัฟเฟอร์)

        // $replyBody is the entire contents of your reply

        header("Content-Type: application/json");  // or whatever yours is

        // checks if gzip is supported by client
        $pack = true;
        if(empty($_SERVER["HTTP_ACCEPT_ENCODING"]) || strpos($_SERVER["HTTP_ACCEPT_ENCODING"], 'gzip') === false)
        {
            $pack = false;
        }

        // if supported, gzips data
        if($pack) {
            header("Content-Encoding: gzip");
            $replyBody = gzencode($replyBody, 9, FORCE_GZIP);
        }

        // compressed or not, sets the Content-Length           
        header("Content-Length: " . mb_strlen($replyBody, 'latin1'));

        // outputs reply & exits
        echo $replyBody;
        exit;

และ voila!

ข้อดีอีกอย่างของการทำมันคือคุณสามารถตั้งค่าระดับการบีบอัดได้ นี่เป็นสิ่งที่ยอดเยี่ยมสำหรับแอปพลิเคชันมือถือของฉันเนื่องจากฉันสามารถตั้งค่าระดับการบีบอัดสูงสุด (ดังนั้นผู้ใช้ของฉันจึงจ่ายน้อยลงสำหรับข้อมูล!) - ในขณะที่เซิร์ฟเวอร์อาจใช้ระดับการบีบอัดขนาดกลางเท่านั้น ระดับการบีบอัดเป็นสิ่งที่ฉันเชื่อว่าคุณสามารถเปลี่ยนแปลงได้หากคุณสามารถแก้ไข httpd.conf (ซึ่งในพื้นที่สาธารณะที่ฉันไม่สามารถทำได้)

ดังนั้นฉันจึงเก็บคำสั่ง. htaccess ไว้สำหรับทุกอย่างยกเว้นการตอบกลับของแอปพลิเคชัน / json ซึ่งตอนนี้ฉันเข้ารหัสในแบบข้างต้น

ขอขอบคุณมาร์ตินเอฟอีกครั้งคุณให้ประกายที่ฉันต้องการเพื่อแก้ปัญหานี้ :)


1
อนึ่งการประหยัดด้วยข้อมูล JSON (ด้วยคีย์ซ้ำอย่างมาก) นั้นใหญ่มากลด 77% ในกรณีเดียว นั่นเป็นเรื่องใหญ่ที่ $ 1 ต่อ MB ...
วิลเลียม Denniss

1
คุณอาจจะเพียงแค่ใช้แทนstrlen($replyBody) mb_strlen($replyBody, 'latin1')ความยาวเนื้อหาเป็นเพียงจำนวนไบต์ (ไม่ใช่ตัวอักษร) ซึ่งเป็นสิ่งที่ strlen () ให้คุณ การใช้ mb_strlen () กับการเรียงลำดับของงาน 'latin1' เนื่องจากตัวอักษร latin1 เป็น 8 บิตเสมอ แต่อาจมีปัญหากับการเข้ารหัสที่สร้างไบต์ที่ไม่ใช่อักขระ latin1 ที่ถูกต้อง
orrd
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.