Độ dài nội dung không được gửi khi nén gzip được bật trong Apache?


13

Tôi thực sự sẽ đánh giá cao một số trợ giúp để hiểu hành vi Apache này.

Tôi đang liên lạc với PHP từ ứng dụng iPhone Objective-C trong ứng dụng / json. Nén Gzip được kích hoạt trên máy chủ và được khách hàng yêu cầu.

Từ .htaccess của tôi:

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

Đối với các yêu cầu nhỏ, Apache đang đặt tiêu đề 'Độ dài nội dung'. Ví dụ: (các giá trị này là đầu ra trong Objective-C từ tiêu đề):

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-Uncompced-Content-Length là một tiêu đề tôi đang thêm được đặt thành kích thước của chuỗi JSON không nén.

Như bạn có thể thấy, yêu cầu này rất nhỏ (217 byte).

Đây là các tiêu đề từ một yêu cầu lớn hơn (282888 byte):

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;

Lưu ý rằng Độ dài nội dung không được đưa ra.

Những câu hỏi của tôi:

  1. Tại sao Apache không gửi Độ dài nội dung cho yêu cầu lớn hơn?
  2. Có phải thực tế là 'Contend-Encoding = gzip' có nghĩa là nén gzip vẫn hoạt động theo yêu cầu lớn hơn, mặc dù tôi không thể xác minh sự khác biệt kích thước?
  3. Có cách nào để tôi có thể khiến Apache đưa vào Độ dài nội dung thực tế cho các yêu cầu lớn hơn này để báo cáo chính xác hơn việc sử dụng dữ liệu cho người dùng không?

Ứng dụng này có thể được sử dụng cho các gói dữ liệu đắt tiền, do đó tôi muốn báo cáo mức sử dụng thực tế cho người dùng, chứ không phải mức sử dụng tăng 30-70% (vài trăm KB có thể không có vẻ nhiều - nhưng các gói này có thể có giá từ 1 đô la và $ 10 mỗi MB!).

Cảm ơn trước.

Câu trả lời:


14

Ngoài câu trả lời của Martin Fjordvalds:

Apache chỉ sử dụng mã hóa khối nếu kích thước tệp nén lớn hơn DeflateBufferSize. Do đó, việc tăng kích thước bộ đệm này sẽ ngăn máy chủ sử dụng mã hóa khối cho các tệp lớn hơn, khiến cho Độ dài nội dung được gửi ngay cả đối với dữ liệu được nén.

Thêm thông tin có sẵn ở đây: http://httpd.apache.org/docs/2.2/mod/mod_deflate.html#deflatebuffersize


Đẹp một. Đây có lẽ là cách nhanh nhất để giải quyết vấn đề này. Nếu bất cứ ai cần mức độ tùy biến cao hơn (ví dụ: một số yêu cầu, không phải yêu cầu khác), hãy xem câu trả lời của tôi serverfault.com/a/183856/54957 để biết giải pháp thủ công.
William Denniss

7

Âm thanh như Apache đang thực hiện mã hóa khối, điều này có nghĩa là nó có thể gửi dữ liệu khi nó được nén, thay vì chờ phản hồi đầy đủ được nén. Đó là cách thực hành khá chuẩn, tuy nhiên tôi không đủ quen thuộc với Apache để nói liệu nó có thể bị vô hiệu hóa hay không.


Cảm ơn thông tin, bạn đã chỉ cho tôi đi đúng hướng, và tôi đã giải quyết nó.
William Denniss

Đã được chấp nhận. Đối với bất cứ ai đọc câu hỏi này mặc dù - xin vui lòng đọc câu trả lời của tôi cho một giải pháp chi tiết. Về cơ bản, bạn có thể tránh phân đoạn (và do đó độ dài nội dung bằng 0) bằng cách đệm và nén trả lời theo cách thủ công.
William Denniss

Có một chút khó hiểu rằng câu trả lời được chấp nhận không phải là câu trả lời cho câu hỏi ban đầu, mà là thứ gì đó giúp bạn có được nó. Có lẽ bạn nên chấp nhận câu trả lời bạn đã đăng dưới đây để làm cho mọi thứ rõ ràng hơn một chút.
redbmk

@redbmk điểm công bằng, tôi chỉ không muốn tỏ ra vô duyên. Philippe thực sự có bản sửa lỗi đơn giản hoàn hảo cho việc này, vì vậy tôi đã chấp nhận bản sửa lỗi của mình.
William Denniss

5

OK, tôi quản lý để giải quyết điều này. Như Martin F đã chỉ ra một cách chính xác, Apache đang kiểm tra trả lời để không biết kích thước nội dung. Đối với nhiều người điều này là mong muốn (tải trang nhanh hơn). Điều này đi kèm với chi phí không thể báo cáo tiến trình tải xuống.

Đối với những người như tôi thực sự muốn báo cáo tiến trình tải xuống, nếu bạn sử dụng hỗ trợ gzip tự động của Apache hoặc PHP, bạn có thể làm được rất ít. Giải pháp là làm bằng tay. Nó dễ hơn âm thanh:

Nếu bạn đang gửi toàn bộ tệp, thì đây là một ví dụ tuyệt vời trong PHP để buộc một đoạn đơn (với Độ dài nội dung): http://www.php.net/manual/en/feft.ob-start.php # 94741

Nếu bạn đang gửi dữ liệu được tạo, thì hãy sử dụng gzencode để mã hóa dữ liệu của bạn, như trong mẫu ở trên. Một điều kiện tiên quyết là tất cả dữ liệu đầu ra của bạn được lưu trữ trong một biến (bạn có thể sử dụng ob_start để trợ giúp điều này nếu bạn cần đệm, sau đó lấy nội dung của bộ đệm).

        // $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;

Và Voila!

Một lợi ích tuyệt vời khác của việc tự làm là bạn có thể đặt mức nén. Điều này rất tốt cho ứng dụng di động của tôi, vì tôi có thể đặt ở mức nén cao nhất (vì vậy người dùng của tôi trả ít tiền hơn cho dữ liệu!) - trong khi máy chủ có thể chỉ sử dụng mức nén trung bình để đánh đổi CPU / kích thước tốt hơn. Mức nén là thứ tôi tin rằng bạn chỉ có thể thay đổi nếu bạn có thể chỉnh sửa httpd.conf (trên lưu trữ được chia sẻ, tôi không thể).

Vì vậy, tôi đã giữ chỉ thị DEFLATE .htaccess của mình cho mọi thứ trừ ứng dụng / json của tôi trả lời mà bây giờ tôi mã hóa theo cách trên.

Cảm ơn một lần nữa Martin F, bạn đã cho tôi tia lửa tôi cần để giải quyết điều này :)


1
Ngẫu nhiên, mức tiết kiệm với dữ liệu JSON (với các khóa được lặp lại nhiều) là rất lớn , giảm 77% trong một trường hợp. Đó là một thỏa thuận lớn với giá 1 đô la mỗi MB ...
William Denniss

1
Bạn có lẽ chỉ nên sử dụng strlen($replyBody)thay vì mb_strlen($replyBody, 'latin1'). Độ dài nội dung chỉ là số byte (không phải ký tự), đây là thứ mà strlen () mang lại cho bạn. Sử dụng mb_strlen () với loại công việc 'latin1' vì các ký tự latin1 luôn là 8 bit, nhưng nó có thể có vấn đề với các mã hóa tạo ra các byte không có ký tự latin1 hợp lệ.
orrd
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.