Một số phương pháp để tăng perfomance PHP Web Server

Định nghĩa performace của mình:

Performace một hệ thống là khả năng đáp ứng yêu cầu của một hệ thống trong một khoảng thời gian. Để phân biệt với Scale, Performace thường nói đến việc làm sao để đáp ứng nhiều nhất yêu cầu đến hệ thống mà không cần phải update phần cứng. Scale là việc update thêm phần cứng để có thể đáp ứng được nhiều yêu cầu hơn. Trong bài viết mình chỉ nói đến Performace mà không trình bày về Scale bởi phần đó không phải thế mạnh của mình.

Tư trưởng chung của bài viết: thông thường cách tăng performace trình bày ở đây là làm sao để đáp ứng 1 thằng nhanh hơn => trong cùng 1 khoảng thời gian sẽ đáp ứng được nhiều yêu cầu hơn. Cách làm thì có rất nhiều, mình sẽ đưa ra một số  giải pháp hiệu quả nhất trước (Hiệu quả = Do less get more)

--------------------------------------------

1. Web Performance Best Practices: 

Đây là những lý thuyết chung về những cách làm tốt nhất để tăng performace cho web đã được một số công ty rất lớn trên thế giới tổng hợp lại. Nội dung cụ thể phần này mọi người có thể xem tại: 

Google: https://developers.google.com/speed/docs/best-practices/rules_intro

Yahoo:  http://developer.yahoo.com/performance/rules.html

Slide của mình tại PHP Day 2010: http://www.slideshare.net/lonelywolf/web-optimization

Bạn nên đọc kỹ những nội dung bên trên bởi nó có RẤT RẤT nhiều thứ rất hay
 

Đọc phần này mọi người chắc sẽ thấy rất nhiều thứ rất nhiều lý thuyết và cần biết rất nhiều thứ mới áp dụng được. Ngoài ra để sử dụng chúng ta cũng cần refactor rất rất nhiều code và khả năng làm được với những dự án cũ là rất ít.

Tuy nhiên google đã đơn giản hóa những việc này bằng cách bạn chỉ cần cài 1 extension của apache hoặc nginx (Tùy thuộc bạn dùng webserver nào). Sau đó chỉ cần config trên server mà không cần động đến một dòng code nào là có thể áp dụng được 80-90% những phần trình bày của google ở bên trên. Bạn có thể tham khảo tại:

Apache: http://code.google.com/p/modpagespeed/

Nginx: https://github.com/pagespeed/ngx_pagespeed

Công cụ đo lường hiệu quả của việc tối ưu này: Đánh giá dưới client thông qua các extension

- Yahoo Slow: http://developer.yahoo.com/yslow/

- Page Speed: https://developers.google.com/speed/pagespeed/insights_extensions 
- Firebug hoặc console của Chrome: https://addons.mozilla.org/en-US/firefox/addon/firebug

Đánh giá chung:

- Độ phức tạp: 2/5 (chỉ cần bạn có 1 chút hiểu biết về việc quản lý server là làm được)

- Hiệu quả 4/5 (Tương đối hiệu quả): bạn cần biết thêm một số lý thuyết kết hợp với cấu hình webserver hiệu quả sẽ có được kết quả rất tốt. Tăng performane lên khoảng 2-3 lần.

2. Opcode Cache

Như mọi người biết PHP là ngôn ngữ lập trinh thông dịch, điều đó có nghĩ là mỗi khi có request đến server thì server mới dịch file PHP thành mã máy và chạy đoạn code đó. Thông thường mỗi request gọi đến server có đến hàng chục thậm chí hàng trăm file được include vào. Như vậy mỗi request lại lặp lại thao tác compiler hàng trăm file. Có một cách để xử lý việc này, làm cho PHP chạy tương tự như những ngôn ngữ biên dịch. Đó là sử dụng Opcode Cache. Opcode cache làm một việc là lần đầu, khi file chưa được dịch, hệ thống vẫn dịch file php như thường, sau đó nội dung sau khi dịch sẽ được lưu vào trong bộ nhớ. Từ lần thứ hai, khi gọi đến file php đó, hệ thống sẽ gọi luôn cache trong bộ nhớ mà không cần phải thực hiện lại việc biên dịch. Có một số extension của PHP để làm việc này trong đó phổ biến nhất là APC. Danh sách extension là:

APC: http://php.net/manual/en/book.apc.php

Xcache http://xcache.lighttpd.net/

eAccelerator:  http://eaccelerator.net/

.... và còn nhiều extension tương tự nữa

Đánh giá chung:

+ Độ phức tạp 1/5 (rất dễ): chỉ cần một bước nhỏ để cài đặt extension của PHP, không cần thêm cài đặt gì là có thể bắt đầu (nếu nâng cao thì cài đặt thêm 1 vài tham số)

+ Mức độ hiệu quả (3/5 khá hiệu quả): có thể năng tốc độc giảm thời gian compile xuống chỉ bằng 1/10 đến 1/20, khiến giảm thời gian sử lý trên server 30%-60% tùy thuộc vào từng dự án.

Xem thêm giới thiệu về Opcode Cache 
http://cerberusweb.com/book/latest/_images/xcache_flowchart.png

3. Code Optiomization 

Hai cách bên trên mình trình bày là những cách đơn giản. Tuy nhiên thường nó chỉ làm giảm thời gian xử lý chung, thời gian xử lý nhiều nhất trên server thông thường là thời gian xử lý code HTML. Để giảm thời gian sử lý này thông thường ta phải động đến code. Cách làm chung là ta phải khoanh vùng, xác định những phần bị chậm, những nút cổ chai để tiến đến việc khắc phục. 

Ở phần này, trước khi bắt đầu mình giới thiệu 2 công cụ để có thể đánh giá được hiệu năng xử lý Server: 

- Công cụ monitor hệ thống: http://newrelic.com Mình chỉ cần cài agent trên server của mình, nó sẽ gửi thông tin lên server của new relic, tiến hành phân tích trên đó và dưa ra kết quả. Trong đó phần thông tin quan trọng nhất (account free cũng dùng được) là phần phân tích những URL nào chạy nhiều nhất, chậm nhất....

- Công cụ Profiling: Sau khi sử dụng new relic ta sẽ biết được những URL nào tốn nhiều tài nguyên hệ thống nhất để tiến hành tối ưu. Công cụ profiling sẽ phân tích URL đó rồi đưa ra report về toàn bộ quá trình chạy URL đó: Nó chạy qua những hàm nào, tổng thời gian xử lý mất bao nhiêu micro giây, trong mỗi hàm lại gọi đến hàm nào, hàm đó lại xử lý mất bao nhiêu micro second. Sử dụng cách này ta có thể khoang vùng những lỗi chậm và xử lý. Công cụ mình đề xuất ở đây là xhprof: http://php.net/manual/en/book.xhprof.php

Sau khi tìm được những phần chậm ta đánh giá lý do và xử lý. Tùy thuộc vào code mà ta có một số những cách xử lý chung như sau:

- Làm cho những việc xử lý có thể tái sử dụng (hay nói cách khác là cache): Có rất nhiều mức độ cache bao gồm cache file (có thể kết hợp với APC để cache bằng việc include PHP file, cực kỳ hiệu quả); cache trong Database (mysql, mongodb); cache qua những cơ sở dữ liệu chạy trên RAM như memcache, redis...; Cache bằng biến static, global dể sử dụng trong cùng 1 request. Chú ý việc cài đặt Memcache hay Redis rất đơn giản chỉ cần vài dòng lệnh là ta có thể thử sử dụng rồi.

- Giảm bớt việc kết nối IO ngoài bằng cách làm 1 lần lấy nhiều thứ hơn: Lỗi này hay gặp với việc kết nối cơ sở dữ liệu. Cách đơn giản nhất là các bạn hãy in tất cả những câu query ra rồi xem liệu có những câu query nào có thể gọi chung được.

Một ví dụ đơn giản: Khi hiển thị 10 bài post mới nhất, với những comment kèm theo, thông tin người gửi. Cách làm thông thường là bạn sẽ lấy ra 10 post mới nhất bằng 1 câu query, sau đó dùng vòng for để lấy tất cả thông tin comment và người gửi của từng post, sau đó lại for theo từng comment để lấy thông tin user. => ít nhất 20-100 query cần dùng. Có 1 cách đơn giản là bạn có thể query tất cả comment mà postid thuộc danh sách trên, với user cũng tương tự => chỉ mất đúng 3 query là có tất cả thông tin. Đây là ví dụ đơn giản nhưng là vấn đề khá phổ biến mà kể cả những LTV lâu năm vẫn gặp phải. Chú ý phương pháp này có thể kết hợp với việc sử dụng cache sẽ rất hiệu quả.

- Xử lý theo batch: Xử lý theo batch một loạt những câu lệnh query dữ liệu hay insert dữ liệu cũng là một cách rất tốt để giảm thời gian xử lý. Hầu hết các Hệ quản trị CSDL hỗ trợ việc này. Từ MySQL, MongoDB, Redis, Memcache đều hỗ trợ việc này, hãy tận dụng nó khi có thể. Phần này có thể kết hợp với việc sử lý bất đồng bộ mình trình bay ở phần sau.

- Lựa chọn những cách làm tương đương mà ít thời gian hơn: Trong nhiều trường hợp ta luôn có những cách xử lý tương đương mà thời gian ít hơn một vài lần. Cái này yêu cầu kinh nghiệm xử lý code trong từng trường hợp, liên quan đến vấn đề kiến trúc hệ thống và kiến trúc code. 

Xem thêm: 

http://www.mdproductions.ca/guides/50-best-practices-to-optimize-php-code-performance

http://www.chazzuka.com/63-best-practice-to-optimize-php-code-performances-58/

- Xử lý bất đồng bộ (Asynchonous): 

Trên tất cả đây là một trong những cách hiệu quả nhất để tăng hiệu năng xử lý và là cách làm chung của những hệ thống lớn. Ý tưởng là ta chỉ sử lý những phần cần thiết cho việc trả về kết quả ở client còn những phần khác ta sẽ xử lý sau. Nhất là những tác vụ yêu cầu thời gian nhiều như: gửi mail, edit file, insert dữ liệu vào db, tính toán với dữ liệu lớn... Bởi hạn chế của PHP, sau khi trả kết quả về client thì cũng là lúc kết thúc process xử lý request, do đó ta không thể yêu cầu server xử lý thêm (Trên java thì việc này khá đơn giản). Để giải quyết ta có mấy cách xử lý sau: 

  • Sử dụng ngôn ngữ lập trình cho phép thạy liên tục những thead để xử lý việc này (chú ý cách xử lý theo bach) => Cách này yêu cầu hiểu biết thêm ngôn ngữ lập trình khác
  • Gọi ra việc xử lý dòng lệnh hoặc bằng việc xử lý bởi đoạn code PHP thông qua các hàm gọi command line của hệ thống: Ví dụ exec, system, pcntl_exec, passthru, popen....
  • Sử dụng Cronjob để gọi ra tiến trình xử lý bằng PHP, cách này đơn giản hơn, dù hiệu quả không cao bằng cách 1.
  • Sử dụng Javascript gọi ngầm từ client để giả lập cronjob (Việc này trên share hotsting cũng làm được, tuy nhiên cũng có hạn chế về việc tiến trình có thể bị ngắt khi client ngắt kết nối hoặc chuyển sang trang khác).

Đánh giá chung: 

+ Độ khó (3-4/5 hơi khó): Tùy thuộc vào từng phần mà bạn cần có những kinh nghiệm khác nhau.
+ Hiệu quả (3-5): Tùy vào từng dự án mà có thể có những hiệu quả khác nhau. Với những dự án mà sử dụng opensource và code ngoài, bạn không nắm được code thì hiệu quả không được như mong đợi.

Via Tuyen Van Doan