ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Wagtail 性能优化实战:缓存、图片渲染与模板片段缓存的完整指南

Wagtail 性能优化实战:缓存、图片渲染与模板片段缓存的完整指南 Wagtail 性能优化实战缓存、图片渲染与模板片段缓存的完整指南【免费下载链接】wagtailA Django content management system focused on flexibility and user experience项目地址: https://gitcode.com/GitHub_Trending/wa/wagtailWagtail 从设计之初就兼顾编辑器界面与前端页面的速度但当流量达到较高规模时仍需要针对缓存、图片处理、查询优化等环节做细致调优。本文以 Wagtail 官方性能指南为骨架结合仓库内源码实现系统讲解 Redis 缓存后端配置、动态图片服务视图、rendition 预取、前端缓存代理联动、模板片段缓存与页面缓存键等核心技术帮助你榨干安装的性能潜力。Cache为 Wagtail 配置高性能缓存后端Wagtail 努力将可运行安装的外部依赖降到最低以简化上手流程但默认设置并非为高性能场景而设计。对于生产环境官方推荐使用 Redis 作为快速、持久的缓存。安装 Redis 可通过系统包管理器完成Debian 或 Ubuntu 上执行sudo apt-get install redis-server随后将django-redis加入requirements.txt并在 Django 设置中启用缓存后端CACHES { default: { BACKEND: django_redis.cache.RedisCache, LOCATION: redis://127.0.0.1:6379/dbname, # for django-redis 3.8.0, use: # LOCATION: 127.0.0.1:6379, OPTIONS: { CLIENT_CLASS: django_redis.client.DefaultClient, }, } }注意LOCATION的写法新版 django-redis 使用redis://URL 形式并允许指定数据库名而 3.8.0 之前的旧版本只接受127.0.0.1:6379这样的 host:port 形式代码注释中已明确标注了这一兼容性差异。为图片 rendition 单独配置缓存后端Wagtail 会缓存图片 rendition渲染版本的查找结果这对包含大量图片的页面有明显性能收益。默认情况下Wagtail 尝试使用名为renditions的缓存若该缓存不存在则回退到默认缓存详见 Caching image renditions。如果你希望用与主缓存不同的后端来缓存图片 rendition可显式配置 renditions 后端CACHES { default: {...}, renditions: { BACKEND: django.core.cache.backends.memcached.MemcachedCache, LOCATION: 127.0.0.1:11211, TIMEOUT: 600, OPTIONS: {MAX_ENTRIES: 1000}, }, }上面的示例将 rendition 查找缓存放到 Memcached并设置了 600 秒超时与最多 1000 条缓存条目。从源码看renditions.md 中描述的缓存查找逻辑位于wagtail.images.models的AbstractImage模型上你可以通过重写find_existing_rendition、create_rendition等方法自定义这一行为。Image URLs用动态图片服务视图加速页面响应如果你只需要图片的 URL例如用于 meta 标签或其他标签属性使用图片服务视图与{% image_url %}标签往往比{% image %}更高效meta propertyog:image content{% image_url page.hero_image width-600 %} /两者的关键区别在于执行时机{% image %}标签会在页面请求中同步查找或创建 rendition阻塞响应而图片服务视图把这项工作卸载到一个独立视图只有当用户真正请求图片时才创建 rendition若已存在则直接返回。当页面包含大量图片时这可以显著加速页面加载。这一方案也有潜在代价如果使用外部存储后端例如 Amazon S3动态图片服务会增加 Wagtail 处理的请求数量需要权衡取舍。降低图片处理错误的波及范围图片服务视图还有一个附带好处——防止图片转换错误导致页面报错。如果图片尺寸过大超出 Willow 图像库的处理能力可通过WAGTAILIMAGES_MAX_IMAGE_PIXELS设置约束图片大小上限Willow 可能崩溃。由于缩放操作发生在页面加载之外图片虽然会缺失但页面其余内容不受影响。在 Python 中生成动态图片 URL同样的效果也可以在 Python 中通过generate_image_url实现对应文档中的 dynamic_image_urls 小节。从 serve.py 的源码可以看到其签名def generate_image_url(image, filter_spec, viewnamewagtailimages_serve, keyNone):一个典型的用法是在视图中生成 URL 并传给模板from wagtail.images.views.serve import generate_image_url def display_image(request, image_id): image get_object_or_404(Image, idimage_id) return render( request, display_image.html, {image_url: generate_image_url(image, fill-100x100)}, )图片操作可以通过|字符串联例如generate_image_url(image, fill-100x100|jpegquality-40)。{% image_url %}标签同样支持可选的自定义视图名参数默认使用wagtailimages_serve{% load wagtailimages_tags %} ... !-- Get the url for the image scaled to a width of 400 pixels: -- {% image_url page.photo width-400 %} !-- Again, but this time as a square thumbnail: -- {% image_url page.photo fill-100x100|jpegquality-40 %} !-- This time using our custom image serve view: -- {% image_url page.photo width-400 mycustomview_serve %}若要启用动态服务视图需要在 URL 配置中显式注册ServeView且必须放在默认页面路由之前详见 image_serve_view.md。该视图还支持actionredirect改为 301 重定向、通过SendFileView集成 django-sendfile 将图片数据传输卸载给 Web 服务器等高级配置。Prefetch image rendition一条查询批量预取图片渲染当用 queryset 渲染一组图片或带有图片的对象列表时可以用预取 rendition的方式用一条额外的查询拿到所有需要的 rendition。对于很长的条目列表、或每个条目使用多种 rendition 的场景这会带来显著的性能提升。Image QuerySet 场景处理 Image QuerySet 时可直接使用 Wagtail 内置的prefetch_renditionsqueryset 方法def get_images_uploaded_by_user(user): # 基础写法 return ImageModel.objects.filter(uploaded_by_useruser) # 预取全部 renditions return ImageModel.objects.filter(uploaded_by_useruser).prefetch_renditions() # 只预取渲染所需的特定 filters return ImageModel.objects.filter(uploaded_by_useruser).prefetch_renditions( fill-700x586, min-600x400, max-940x680 )如果项目中图片的 rendition 数量通常很大且你提前知道自己需要哪些建议像第三个示例那样只选择渲染所需的特定 filters避免无谓地预取全部 rendition。非 Image 模型场景处理非图片模型时可以借助 Django 内置的prefetch_related()来预取 rendition。示例渲染一组带缩略图的事件页面。# 基础写法 def get_events(): return EventPage.objects.live().select_related(listing_image) # 预取 listing_image 的全部 renditions def get_events(): return ( EventPage.objects.live() .select_related(listing_image) .prefetch_related(listing_image__renditions) )若想只取所需的那几种 rendition可结合 Django 的Prefetch对象与prefetch_renditions精确控制from django.db.models import Prefetch from wagtail.images import get_image_model def get_events(): Image get_image_model() filters [fill-300x186, fill-600x400, fill-940x680] # Prefetch 用于只获取所需的 renditions prefetch_images_and_renditions Prefetch( listing_image, querysetImage.objects.prefetch_renditions(*filters) ) return EventPage.objects.live().prefetch_related(prefetch_images_and_renditions)另外如果需要在 Python 中直接生成单个或多个 rendition可以使用get_rendition()与更高效的get_renditions()方法后者接受多个规格字符串或Filter实例一次性批量生成详见 renditions.md。Frontend caching proxy接入前端缓存代理并联动失效许多高流量网站使用 Varnish、Squid、Cloudflare 或 CloudFront 等前端缓存来获得出色的响应速度。前端缓存的缺点是内容更新后不会立即失效常常在页面更新后仍保留旧版本。Wagtail 支持与众多 CDN 集成当页面变化时能主动通知它们立即清空缓存让用户更快看到更新参见 frontendcache 参考文档。其底层机制可在 pages.py 中找到痕迹Page.get_cached_paths()返回需要在前端缓存中失效的路径列表默认返回[/]你可以按需覆写。如果你配置了多个前端后端例如一个站点用 Cloudflare、另一个用 CloudFront官方建议设置HOSTNAMES键列出该后端可以清理的主机名列表以避免产生多余的清理请求。Page URLs复用请求上下文减少查询要完整解析页面 URLWagtail 需要来自多个来源的信息。Page.get_url和Page.get_full_url等方法可选地接受request和current_site参数。传入这些参数后底层站点级 URL 信息可以在当前请求中被大量复用。在导航菜单生成、以及页面内容中出现的链接这类场景下提供request或current_site可以大幅减少单次页面加载产生的缓存或数据库查询数量。当使用{% pageurl %}或{% fullpageurl %}模板标签时request 会被自动传入因此无需再做额外优化。Search为全文搜索配置 ElasticsearchWagtail 对 Elasticsearch 有完善的支持——无论是在编辑器界面还是站点用户侧——但在没有 Elasticsearch 时也能回退到数据库搜索。对于文本搜索Elasticsearch 比 Django ORM 更快、更强大官方建议安装它或使用 Searchly 之类的托管服务。Elasticsearch 的具体配置方式可参考wagtailsearch_backends_elasticsearch对应的搜索后端文档docs/topics/search 目录下可找到搜索后端相关说明。Database数据库选型与图片懒加载Wagtail 在 PostgreSQL、SQLite、MySQL 和 MariaDB 上经过测试也可能在部分第三方数据库后端上工作但不作保证。官方推荐生产环境使用 PostgreSQL不过数据库的最终选择取决于个人偏好、团队经验与具体项目需求等多种因素最重要的是所选数据库能满足项目的性能与扩展性要求。图片属性懒加载与异步解码对部分图片使用懒加载可以让页面其余部分继续加载。可以在站点级别统一配置见 adding_default_attributes_to_images也可以按单张图片配置见 image_tag_alt。常见优化包括loadinglazy与decodingasync属性。在管理后台中这一优化已自动为图片处理完毕无需额外配置。Template fragment caching安全地缓存模板片段Django 支持模板片段缓存允许缓存模板的一部分。但直接在本机使用 Django 的{% cache %}标签与 Wagtail 组合可能存在风险参考 wagtail/wagtail#5074因为它可能导致预览内容被展示给终端用户。为此Wagtail 提供了两个额外的模板标签{% wagtailcache %}与{% wagtailpagecache %}两者都规避了上述问题。从 wagtail_cache.py 的源码可以看到它们的实现原理WagtailCacheNode继承自 Django 的CacheNode在渲染时检查context[request]是否存在、以及request.is_preview是否为真。当请求缺失或处于预览状态时直接绕过缓存渲染内容保证预览内容永远不会污染缓存WagtailPageCacheNode进一步针对页面片段做了优化它会自动把page.cache_key和当前Site的主键追加到vary_on列表并在渲染前通过Site.find_for_request(request)将站点注入上下文。也就是说缓存键天然随页面状态与站点变化片段缓存自动按页面和站点隔离。两个标签的用法与 Django 的{% cache %}一致需要至少两个参数缓存时长与片段名支持可选的using指定缓存后端。如果上下文假设不成立例如没有page变量建议直接使用更通用的{% wagtailcache %}。Page cache key以整页状态为键的缓存方案很多时候需要基于整个页面而非某个具体值来缓存结果。此时可以使用Page.cache_key属性获取代表页面当前状态的唯一值——只要页面发生任何变化缓存键就会随之改变。从 pages.py 的源码可以看出其实现get_cache_key_components()默认返回[self.id, self.url_path, self.last_published_at]last_published_at为空时取Nonecache_key属性则用安全 MD5 对这些组件依次做哈希后返回十六进制摘要。你可以在使用 Django 缓存框架时用它拼出更长、更具体的缓存键例如from django.core.cache import cache result page.expensive_operation() cache.set(expensive_result_ page.cache_key, result, 3600) # Later... cache.get(expensive_result_ page.cache_key)自定义缓存键组件要修改缓存键例如纳入自定义模型字段的值可以覆写get_cache_key_componentsdef get_cache_key_components(self): components super().get_cache_key_components() components.append(self.external_slug) return components一个需要注意的细节手动更新页面如直接修改字段并保存可能不会让缓存键发生变化除非默认组件的字段值被直接修改。若要确保缓存键一定变化建议把变更保存为Revision然后再发布它——发布流程会更新last_published_at等默认组件从而改变缓存键。Django遵循通用性能建议Wagtail 构建于 Django 之上Django 官方文档中关于性能的建议如数据库索引、查询优化、连接池、静态文件处理、缓存策略等对 Wagtail 项目同样适用。将 Wagtail 特有的优化缓存后端、rendition 预取、模板片段缓存、页面缓存键与 Django 层面的通用最佳实践结合使用才能获得完整的性能收益。小结性能调优的要点可以归纳为四层缓存层Redis 主缓存 独立的 renditions 缓存、图片层动态服务视图、rendition 预取、懒加载属性、查询层复用 request/current_site、Elasticsearch 搜索与模板层wagtailcache/wagtailpagecache片段缓存、cache_key键控缓存。其中模板片段缓存的实现细节位于 wagtail_cache.py页面缓存键的哈希逻辑位于 pages.py图片相关能力集中在 renditions.md 与 image_serve_view.md 两份文档中可作为继续深入阅读的入口。【免费下载链接】wagtailA Django content management system focused on flexibility and user experience项目地址: https://gitcode.com/GitHub_Trending/wa/wagtail创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表