使用邊緣端包含
閘道快取是提升網站效能的好方法。但它們有一個限制:它們只能快取整個頁面。如果您的頁面包含動態區塊,例如使用者名稱或購物車,您就束手無策了。幸運的是,Symfony 為這些情況提供了解決方案,基於一種稱為 ESI 或邊緣端包含的技術。Akamai 在 2001 年編寫了此規範,它允許頁面的特定部分具有與主頁面不同的快取策略。
ESI 規範描述了您可以嵌入在頁面中的標籤,以與閘道快取進行通訊。Symfony 中僅實作了一個標籤 include
,因為這是 Akamai 環境之外唯一有用的標籤
1 2 3 4 5 6 7 8 9 10 11
<!DOCTYPE html>
<html>
<body>
<!-- ... some content -->
<!-- Embed the content of another page here -->
<esi:include src="http://..."/>
<!-- ... more content -->
</body>
</html>
注意
從範例中注意到,每個 ESI 標籤都需要一個完整的 URL。ESI 標籤代表一個可以通過給定的 URL 獲取的頁面片段。
當處理請求時,閘道快取會從其快取中提取整個頁面,或從後端應用程式請求頁面。如果回應包含一個或多個 ESI 標籤,這些標籤會以相同的方式處理。換句話說,閘道快取要麼從其快取中檢索包含的頁面片段,要麼再次從後端應用程式請求頁面片段。當所有 ESI 標籤都已解析時,閘道快取會將每個標籤合併到主頁面中,並將最終內容發送到客戶端。
所有這些都在閘道快取層級透明地發生(即在您的應用程式之外)。正如您將看到的,如果您選擇利用 ESI 標籤,Symfony 使包含它們的過程幾乎毫不費力。
在 Symfony 中使用 ESI
首先,要使用 ESI,請確保在您的應用程式配置中啟用它
1 2 3 4
# config/packages/framework.yaml
framework:
# ...
esi: true
現在,假設您有一個相對靜態的頁面,除了內容底部的跑馬燈新聞。使用 ESI,您可以將跑馬燈新聞與頁面的其餘部分獨立快取
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
// src/Controller/DefaultController.php
namespace App\Controller;
// ...
class DefaultController extends AbstractController
{
public function about(): Response
{
$response = $this->render('static/about.html.twig');
$response->setPublic();
$response->setMaxAge(600);
return $response;
}
}
在這個範例中,回應被標記為公開,使整個頁面對於所有請求都可快取,生命週期為十分鐘。接下來,通過嵌入一個動作,將跑馬燈新聞包含在模板中。這是通過 render()
助手完成的(更多詳細資訊,請參閱如何在模板中嵌入控制器)。
由於嵌入的內容來自另一個頁面(或控制器),Symfony 使用標準的 render
助手來配置 ESI 標籤
1 2 3 4 5 6 7
{# templates/static/about.html.twig #}
{# you can use a controller reference #}
{{ render_esi(controller('App\\Controller\\NewsController::latest', { 'maxPerPage': 5 })) }}
{# ... or a URL #}
{{ render_esi(url('latest_news', { 'maxPerPage': 5 })) }}
通過使用 esi
渲染器(通過 render_esi()
Twig 函數),您告訴 Symfony 該動作應渲染為 ESI 標籤。您可能想知道為什麼要使用助手而不是自己編寫 ESI 標籤。那是因為即使沒有安裝閘道快取,使用助手也能使您的應用程式正常運作。
提示
正如您在下面看到的,您傳遞的 maxPerPage
變數可用作控制器的參數(即 $maxPerPage
)。通過 render_esi
傳遞的變數也成為快取鍵的一部分,以便您為變數和值的每個組合擁有唯一的快取。
當使用預設的 render()
函數(或將渲染器設定為 inline
)時,Symfony 會在將回應發送到客戶端之前,將包含的頁面內容合併到主頁面中。但是,如果您使用 esi
渲染器(即調用 render_esi()
)*並且* Symfony 檢測到它正在與支援 ESI 的閘道快取進行通訊,它會生成一個 ESI include 標籤。但是,如果沒有閘道快取,或者它不支援 ESI,Symfony 只會將包含的頁面內容合併到主頁面中,就像您使用 render()
時一樣。
注意
如果閘道快取的請求包含 Surrogate-Capability
HTTP 標頭,並且該標頭的值在任何位置包含 ESI/1.0
字串,Symfony 則認為該閘道快取支援 ESI。
現在,嵌入的動作可以完全獨立於主頁面指定自己的快取規則
1 2 3 4 5 6 7 8 9 10 11 12 13 14
// src/Controller/NewsController.php
namespace App\Controller;
use Symfony\Component\HttpKernel\Attribute\Cache;
// ...
class NewsController extends AbstractController
{
#[Cache(smaxage: 60)]
public function latest(int $maxPerPage): Response
{
// ...
}
}
在這個範例中,嵌入的動作也被公開快取,因為內容對於所有請求都是相同的。但是,在其他情況下,您可能需要根據您的需求,使此回應成為非公開的,甚至不可快取的。
將以上所有程式碼放在一起,使用 ESI,整個頁面快取將有效 600 秒,但新聞組件快取僅持續 60 秒。
當使用控制器參考時,ESI 標籤應將嵌入的動作引用為可訪問的 URL,以便閘道快取可以獨立於頁面的其餘部分獲取它。Symfony 負責為任何控制器參考生成唯一的 URL,並且由於 FragmentListener 必須在您的配置中啟用,因此它能夠正確地路由它們
1 2 3 4
# config/packages/framework.yaml
framework:
# ...
fragments: { path: /_fragment }
ESI 渲染器的一個巨大優勢是,您可以根據需要使您的應用程式盡可能動態,同時盡可能少地訪問應用程式。
警告
片段監聽器僅回應已簽署的請求。僅當使用片段渲染器和 render_esi
Twig 函數時,請求才被簽署。
render_esi
助手支援另外三個有用的選項
alt
- 用作 ESI 標籤上的
alt
屬性,允許您指定在找不到src
時使用的替代 URL。 ignore_errors
- 如果設定為 true,則會將
onerror
屬性添加到 ESI,值為continue
,表示在發生故障時,閘道快取將靜默地移除 ESI 標籤。 absolute_uri
- 如果設定為 true,將生成絕對 URI。預設值:
false