Hiệu suất ứng dụng rất quan trọng đối với sự thành công của sản phẩm của bạn. Trong môi trường mà người dùng mong đợi thời gian phản hồi của trang web dưới một giây, hậu quả của việc ứng dụng chậm có thể được tính bằng đô la và xu. Ngay cả khi bạn không bán bất cứ thứ gì, tải trang nhanh sẽ cải thiện trải nghiệm truy cập trang web của bạn.
Mọi thứ xảy ra trên máy chủ từ thời điểm nó nhận được yêu cầu đến thời điểm nó trả về phản hồi sẽ làm tăng lượng thời gian tải trang. Theo nguyên tắc chung, bạn có thể loại bỏ càng nhiều xử lý trên máy chủ, thì ứng dụng của bạn sẽ hoạt động càng nhanh. Lưu vào bộ đệm dữ liệu sau khi nó đã được xử lý và sau đó phục vụ nó từ bộ đệm vào lần tiếp theo khi nó được yêu cầu là một cách để giảm bớt căng thẳng cho máy chủ. Trong hướng dẫn này, chúng tôi sẽ khám phá một số yếu tố làm hỏng ứng dụng của bạn và chúng tôi sẽ trình bày cách triển khai bộ nhớ đệm với Redis để chống lại các tác động của chúng.
Tiền thưởng miễn phí: Nhấp vào đây để có quyền truy cập vào Hướng dẫn tài nguyên học tập Django (PDF) miễn phí chỉ cho bạn các mẹo và thủ thuật cũng như các cạm bẫy thường gặp cần tránh khi xây dựng ứng dụng web Python + Django.
Redis là gì?
Redis là một kho lưu trữ cấu trúc dữ liệu trong bộ nhớ có thể được sử dụng như một công cụ lưu vào bộ nhớ đệm. Vì nó giữ dữ liệu trong RAM nên Redis có thể phân phối dữ liệu rất nhanh. Redis không phải là sản phẩm duy nhất mà chúng tôi có thể sử dụng để lưu vào bộ nhớ đệm. Memcached là một hệ thống bộ nhớ đệm trong bộ nhớ phổ biến khác, nhưng nhiều người đồng ý rằng Redis vượt trội hơn Memcached trong hầu hết các trường hợp. Về mặt cá nhân, chúng tôi thích cách dễ dàng thiết lập và sử dụng Redis cho các mục đích khác như Hàng đợi Redis.
Bắt đầu
Chúng tôi đã tạo một ứng dụng ví dụ để giới thiệu cho bạn khái niệm về bộ nhớ đệm. Ứng dụng của chúng tôi sử dụng:
- Django (v1.9.8)
- Thanh công cụ gỡ lỗi Django (v1.4)
- django-redis (v4.4.3)
- Redis (v3.2.0)
Cài đặt ứng dụng
Trước khi bạn sao chép kho lưu trữ, hãy cài đặt virtualenvwrapper, nếu bạn chưa có. Đây là một công cụ cho phép bạn cài đặt các phụ thuộc Python cụ thể mà dự án của bạn cần, cho phép bạn nhắm mục tiêu các phiên bản và thư viện mà ứng dụng của bạn yêu cầu.
Tiếp theo, thay đổi các thư mục thành nơi bạn lưu giữ các dự án và sao chép kho ứng dụng mẫu. Sau khi hoàn tất, hãy thay đổi các thư mục thành kho lưu trữ được sao chép, sau đó tạo một môi trường ảo mới cho ứng dụng mẫu bằng cách sử dụng mkvirtualenv
lệnh:
$ mkvirtualenv django-redis
(django-redis)$
LƯU Ý: Tạo môi trường ảo với
mkvirtualenv
cũng kích hoạt nó.
Cài đặt tất cả các phụ thuộc Python được yêu cầu với pip
, và sau đó kiểm tra thẻ sau:
(django-redis)$ git checkout tags/1
Hoàn tất thiết lập ứng dụng mẫu bằng cách xây dựng cơ sở dữ liệu và điền vào nó với dữ liệu mẫu. Đảm bảo bạn cũng tạo superuser để bạn có thể đăng nhập vào trang quản trị. Làm theo các ví dụ mã bên dưới, sau đó thử chạy ứng dụng để đảm bảo rằng ứng dụng đang hoạt động chính xác. Truy cập trang quản trị trong trình duyệt để xác nhận rằng dữ liệu đã được tải đúng cách.
(django-redis)$ python manage.py makemigrations cookbook
(django-redis)$ python manage.py migrate
(django-redis)$ python manage.py createsuperuser
(django-redis)$ python manage.py loaddata cookbook/fixtures/cookbook.json
(django-redis)$ python manage.py runserver
Khi bạn đã chạy ứng dụng Django, hãy chuyển sang cài đặt Redis.
Cài đặt Redis
Tải xuống và cài đặt Redis theo hướng dẫn được cung cấp trong tài liệu. Ngoài ra, bạn có thể cài đặt Redis bằng trình quản lý gói như apt-get hoặc homebrew tùy thuộc vào hệ điều hành của bạn.
Chạy máy chủ Redis từ một cửa sổ đầu cuối mới.
$ redis-server
Tiếp theo, khởi động giao diện dòng lệnh Redis (CLI) trong một cửa sổ đầu cuối khác và kiểm tra xem nó có kết nối với máy chủ Redis hay không. Chúng tôi sẽ sử dụng Redis CLI để kiểm tra các khóa mà chúng tôi thêm vào bộ nhớ cache.
$ redis-cli ping
PONG
Redis cung cấp một API với các lệnh khác nhau mà nhà phát triển có thể sử dụng để hành động trên kho dữ liệu. Django sử dụng django-redis để thực hiện các lệnh trong Redis.
Nhìn vào ứng dụng mẫu của chúng tôi trong trình soạn thảo văn bản, chúng tôi có thể thấy cấu hình Redis trong settings.py tập tin. Chúng tôi xác định một bộ đệm ẩn mặc định với CACHES
cài đặt, sử dụng django-redis tích hợp sẵn bộ nhớ cache làm chương trình phụ trợ của chúng tôi. Redis chạy trên cổng 6379 theo mặc định và chúng tôi trỏ đến vị trí đó trong cài đặt của chúng tôi. Một điều cuối cùng cần đề cập là django-redis nối các tên khóa với tiền tố và phiên bản để giúp phân biệt các khóa tương tự. Trong trường hợp này, chúng tôi đã xác định tiền tố là “example”.
CACHES = {
"default": {
"BACKEND": "django_redis.cache.RedisCache",
"LOCATION": "redis://127.0.0.1:6379/1",
"OPTIONS": {
"CLIENT_CLASS": "django_redis.client.DefaultClient"
},
"KEY_PREFIX": "example"
}
}
LƯU Ý :Mặc dù chúng tôi đã định cấu hình phần phụ trợ bộ nhớ cache, nhưng không có chức năng xem nào triển khai bộ nhớ đệm.
Hiệu suất ứng dụng
Như chúng tôi đã đề cập ở phần đầu của hướng dẫn này, mọi thứ mà máy chủ thực hiện để xử lý một yêu cầu sẽ làm chậm thời gian tải ứng dụng. Chi phí xử lý của việc chạy các mẫu kết xuất và logic nghiệp vụ có thể rất đáng kể. Độ trễ mạng ảnh hưởng đến thời gian cần thiết để truy vấn cơ sở dữ liệu. Các yếu tố này phát huy tác dụng mỗi khi máy khách gửi yêu cầu HTTP đến máy chủ. Khi người dùng bắt đầu nhiều yêu cầu mỗi giây, ảnh hưởng đến hiệu suất trở nên đáng chú ý vì máy chủ hoạt động để xử lý tất cả.
Khi chúng tôi triển khai bộ nhớ đệm, chúng tôi để máy chủ xử lý yêu cầu một lần và sau đó chúng tôi lưu trữ nó trong bộ nhớ cache của mình. Khi ứng dụng của chúng tôi nhận được các yêu cầu cho cùng một URL, máy chủ sẽ lấy các kết quả từ bộ nhớ đệm thay vì xử lý lại chúng mỗi lần. Thông thường, chúng tôi đặt thời gian tồn tại trên các kết quả đã lưu trong bộ nhớ cache để dữ liệu có thể được làm mới định kỳ, đây là bước quan trọng cần triển khai để tránh cung cấp dữ liệu cũ.
Bạn nên cân nhắc vào bộ nhớ đệm kết quả của một yêu cầu khi các trường hợp sau là đúng:
- hiển thị trang liên quan đến rất nhiều truy vấn cơ sở dữ liệu và / hoặc logic nghiệp vụ,
- trang được người dùng của bạn truy cập thường xuyên,
- dữ liệu giống nhau đối với mọi người dùng,
- và dữ liệu không thường xuyên thay đổi.
Bắt đầu bằng cách đo lường hiệu suất
Bắt đầu bằng cách kiểm tra tốc độ của từng trang trong ứng dụng của bạn bằng cách đánh giá điểm chuẩn mức độ nhanh chóng mà ứng dụng của bạn trả lại phản hồi sau khi nhận được yêu cầu.
Để đạt được điều này, chúng tôi sẽ đưa ra từng trang với một loạt yêu cầu bằng cách sử dụng loadtest, một trình tạo tải HTTP và sau đó chú ý đến tỷ lệ yêu cầu. Truy cập liên kết trên để cài đặt. Sau khi cài đặt, hãy kiểm tra kết quả đối với /cookbook/
Đường dẫn URL:
$ loadtest -n 100 -k http://localhost:8000/cookbook/
Lưu ý rằng chúng tôi đang xử lý khoảng 16 yêu cầu mỗi giây:
Requests per second: 16
Khi chúng tôi xem mã đang làm gì, chúng tôi có thể đưa ra quyết định về cách thực hiện các thay đổi để cải thiện hiệu suất. Ứng dụng thực hiện 3 cuộc gọi mạng tới cơ sở dữ liệu với mỗi yêu cầu đến /cookbook/
và phải mất thời gian cho mỗi cuộc gọi để mở một kết nối và thực hiện một truy vấn. Truy cập /cookbook/
URL trong trình duyệt của bạn và mở rộng tab Thanh công cụ gỡ lỗi Django để xác nhận hành vi này. Tìm menu có nhãn “SQL” và đọc số lượng truy vấn:
cookbook / services.py
from cookbook.models import Recipe
def get_recipes():
# Queries 3 tables: cookbook_recipe, cookbook_ingredient,
# and cookbook_food.
return list(Recipe.objects.prefetch_related('ingredient_set__food'))
cookbook / views.py
from django.shortcuts import render
from cookbook.services import get_recipes
def recipes_view(request):
return render(request, 'cookbook/recipes.html', {
'recipes': get_recipes()
})
Ứng dụng cũng hiển thị một mẫu với một số logic đắt tiền.
<html>
<head>
<title>Recipes</title>
</head>
<body>
{% for recipe in recipes %}
<h1>{{ recipe.name }}</h1>
<p>{{ recipe.desc }}</p>
<h2>Ingredients</h2>
<ul>
{% for ingredient in recipe.ingredient_set.all %}
<li>{{ ingredient.desc }}</li>
{% endfor %}
</ul>
<h2>Instructions</h2>
<p>{{ recipe.instructions }}</p>
{% endfor %}
</body>
</html>
Triển khai bộ nhớ đệm
Hãy tưởng tượng tổng số cuộc gọi mạng mà ứng dụng của chúng tôi sẽ thực hiện khi người dùng bắt đầu truy cập trang web của chúng tôi. Nếu 1.000 người dùng nhấn vào API để truy xuất các công thức nấu ăn, thì ứng dụng của chúng tôi sẽ truy vấn cơ sở dữ liệu 3.000 lần và một mẫu mới sẽ được hiển thị với mỗi yêu cầu. Con số đó chỉ tăng lên khi ứng dụng của chúng tôi mở rộng. May mắn thay, chế độ xem này là một ứng cử viên tuyệt vời cho bộ nhớ đệm. Các công thức nấu ăn trong sách dạy nấu ăn hiếm khi thay đổi, nếu có. Ngoài ra, vì xem sách nấu ăn là chủ đề chính của ứng dụng, nên API truy xuất các công thức nấu ăn được đảm bảo sẽ được gọi thường xuyên.
Trong ví dụ dưới đây, chúng tôi sửa đổi chức năng xem để sử dụng bộ nhớ đệm. Khi chức năng chạy, nó sẽ kiểm tra xem phím xem có trong bộ nhớ đệm hay không. Nếu khóa tồn tại, thì ứng dụng sẽ truy xuất dữ liệu từ bộ nhớ cache và trả về. Nếu không, Django truy vấn cơ sở dữ liệu và sau đó lưu trữ kết quả vào bộ nhớ cache bằng phím xem. Lần đầu tiên chức năng này được chạy, Django sẽ truy vấn cơ sở dữ liệu và hiển thị mẫu, sau đó cũng sẽ thực hiện cuộc gọi mạng tới Redis để lưu trữ dữ liệu trong bộ nhớ cache. Mỗi lần gọi hàm tiếp theo sẽ hoàn toàn bỏ qua cơ sở dữ liệu và logic nghiệp vụ và sẽ truy vấn bộ đệm Redis.
example / settings.py
# Cache time to live is 15 minutes.
CACHE_TTL = 60 * 15
cookbook / views.py
from django.conf import settings
from django.core.cache.backends.base import DEFAULT_TIMEOUT
from django.shortcuts import render
from django.views.decorators.cache import cache_page
from cookbook.services import get_recipes
CACHE_TTL = getattr(settings, 'CACHE_TTL', DEFAULT_TIMEOUT)
@cache_page(CACHE_TTL)
def recipes_view(request):
return render(request, 'cookbook/recipes.html', {
'recipes': get_recipes()
})
Lưu ý rằng chúng tôi đã thêm @cache_page()
trang trí cho chức năng xem, cùng với thời gian sống. Truy cập /cookbook/
URL một lần nữa và kiểm tra Thanh công cụ gỡ lỗi Django. Chúng ta thấy rằng 3 truy vấn cơ sở dữ liệu được thực hiện và 3 cuộc gọi được thực hiện đến bộ đệm để kiểm tra khóa và sau đó lưu nó. Django lưu hai khóa (1 khóa cho tiêu đề và 1 khóa cho nội dung trang được kết xuất). Tải lại trang và quan sát hoạt động của trang thay đổi như thế nào. Lần thứ hai, 0 cuộc gọi được thực hiện đến cơ sở dữ liệu và 2 cuộc gọi được thực hiện vào bộ nhớ cache. Trang của chúng tôi hiện đang được cung cấp từ bộ nhớ cache!
Khi chạy lại các bài kiểm tra hiệu suất của mình, chúng tôi thấy rằng ứng dụng của mình đang tải nhanh hơn.
$ loadtest -n 100 -k http://localhost:8000/cookbook/
Bộ nhớ đệm đã cải thiện tổng tải và hiện chúng tôi đang giải quyết 21 yêu cầu mỗi giây, nhiều hơn 5 yêu cầu so với đường cơ sở của chúng tôi:
Requests per second: 21
Kiểm tra Redis bằng CLI
Tại thời điểm này, chúng ta có thể sử dụng Redis CLI để xem những gì được lưu trữ trên máy chủ Redis. Trong dòng lệnh Redis, nhập các phím keys *
, trả về tất cả các phím phù hợp với bất kỳ mẫu nào. Bạn sẽ thấy một khóa có tên “example:1:views.decorators.cache.cache_page”. Hãy nhớ rằng “example” là tiền tố khóa của chúng tôi, “1” là phiên bản và “views.decorators.cache.cache_page” là tên mà Django đặt cho khóa. Sao chép tên khóa và nhập bằng get
yêu cầu. Bạn sẽ thấy chuỗi HTML được hiển thị.
$ redis-cli -n 1
127.0.0.1:6379[1]> keys *
1) "example:1:views.decorators.cache.cache_header"
2) "example:1:views.decorators.cache.cache_page"
127.0.0.1:6379[1]> get "example:1:views.decorators.cache.cache_page"
LƯU Ý: Chạy
flushall
trên Redis CLI để xóa tất cả các khóa khỏi kho dữ liệu. Sau đó, bạn có thể chạy lại các bước trong hướng dẫn này mà không cần phải đợi bộ nhớ cache hết hạn.
Kết thúc
Việc xử lý các yêu cầu HTTP rất tốn kém và chi phí đó sẽ tăng lên khi ứng dụng của bạn ngày càng phổ biến. Trong một số trường hợp, bạn có thể giảm đáng kể số lượng xử lý mà máy chủ của bạn thực hiện bằng cách triển khai bộ nhớ đệm. Hướng dẫn này đã đề cập đến những điều cơ bản về bộ nhớ đệm trong Django với Redis, nhưng nó chỉ lướt qua bề mặt của một chủ đề phức tạp.
Việc triển khai bộ nhớ đệm trong một ứng dụng mạnh mẽ có nhiều cạm bẫy và khó khăn. Kiểm soát những gì được lưu trong bộ nhớ cache và trong bao lâu là một việc khó khăn. Vô hiệu bộ nhớ cache là một trong những điều khó khăn trong Khoa học máy tính. Đảm bảo rằng dữ liệu riêng tư chỉ có thể được truy cập bởi những người dùng dự định của nó là một vấn đề bảo mật và phải được xử lý rất cẩn thận khi lưu vào bộ nhớ đệm.
Tiền thưởng miễn phí: Nhấp vào đây để có quyền truy cập vào Hướng dẫn tài nguyên học tập Django (PDF) miễn phí chỉ cho bạn các mẹo và thủ thuật cũng như các cạm bẫy thường gặp cần tránh khi xây dựng ứng dụng web Python + Django.
Hãy thử với mã nguồn trong ứng dụng mẫu và khi bạn tiếp tục phát triển với Django, hãy nhớ luôn ghi nhớ hiệu suất.