昨天(Day 24)我們把快取污染的問題講清楚:
同樣內容的頁面,卻因為 URL 帶了不同參數或順序,生成了多份快取,浪費了 Redis 空間,也拉低了命中率。
今天就是「解方落地」的一天。
我們要做的,就是在 Magento 生成 Full Page Cache 識別符的最後一步,把 URL 正規化,確保「一樣的內容 → 一樣的 Key」。
Magento 的 Full Page Cache 識別符是由 Magento\Framework\App\PageCache\Identifier::getValue()
產生的。
我們透過 Plugin 包住這個方法,先攔截請求、處理參數,最後再交回快取系統。
這樣做的好處是:
核心思路延續 Day 24:
白名單過濾
字母序排序
正規化 URL
生成穩定識別符
sha1
產生最終快取 key。為了讓這個功能能「隨時開關」、「隨時調整」,我們在後台新增了設定:
這讓整個機制更彈性,不會成為風險。
public function aroundGetValue(Identifier $subject, \Closure $proceed)
{
try {
$params = $this->request->getParams();
if (empty($params) || !$this->isEnabled()) {
return $proceed();
}
// 建立白名單
$productAttributeCodes = $this->getAllProductAttributeCodes();
$defaultFilterCodes = ['product_list_order','product_list_dir','q','p','cat','s','np'];
$configCustomCodes = $this->getCustomAllowedParams();
$allowedKeys = array_merge($productAttributeCodes, $defaultFilterCodes, $configCustomCodes);
// 過濾 + 排序
$filteredParams = $this->filterParamsByAllowedFields($params, $allowedKeys);
ksort($filteredParams);
// 正規化 URL
$baseUrl = $this->getBaseUrl();
$processedUrl = !empty($filteredParams)
? $baseUrl . '?' . http_build_query($filteredParams)
: $baseUrl;
// 生成識別符
$data = [
$this->request->isSecure(),
$processedUrl,
$this->request->get(\Magento\Framework\App\Response\Http::COOKIE_VARY_STRING)
?: $this->context->getVaryString()
];
return sha1($this->serializer->serialize($data));
} catch (\Exception $e) {
ObjectManager::getInstance()->get('Psr\Log\LoggerInterface')
->error('Error in page cache getValue: ' . $e->getMessage());
return $proceed();
}
}
/catalogsearch/result/index/?MRL_filters_coftable=341&abc=222&MRL_filters_preorder=134&product_list_dir=asc&q=sofa&product_list_order=name&gad_source=1&gclid=xxx
/catalogsearch/result/index/?MRL_filters_coftable=341&MRL_filters_preorder=134&product_list_dir=asc&product_list_order=name&q=sofa
abc=222
、gad_source=1
、gclid=xxx
→ 被移除Day 25,我們完成了最關鍵的一步:
讓 Magento 的快取 key,不再被無用參數污染。
這是一個 Plugin + 白名單 + 正規化 的實戰案例,不只解決了效能問題,也保留了彈性。
明天(Day 26),我們會進入數據觀察與成效驗證,看看這套機制帶來的真實改變。