在現今網路環境下,影音串流平臺的效能與可擴充套件性至關重要。本文將以 FlixTube 為例,深入探討如何運用微服務架構,結合 Kubernetes 和 Azure Storage,打造一個兼具高效率與彈性的影音串流平臺。從多閘道模式的設計、影片上傳機制、串流程的最佳化,到程式碼範例的解析,本文將逐步揭示 FlixTube 的核心技術與架構設計理念。同時,透過 Mermaid 圖表,更清晰地呈現系統架構與資料流程,幫助讀者理解微服務之間的互動與協作。

圖 10.9:FlixTube 的多閘道模式未來展望

圖 10.9 顯示了 FlixTube 可能的未來架構,其中包含多個閘道,每個閘道對應一個前端應用程式。這種模式可以幫助滿足不同前端的特殊需求,但也增加了系統的複雜度。

建立高效能的影音上傳系統

為了實作高效能的影音上傳系統,我們需要設計一個可靠且可擴充套件的架構。首先,我們需要建立一個 handler 來處理上傳的影音檔案。這個 handler 將會將上傳的檔案串流到本地檔案中,以便於後續的處理。

多閘道架構

如果我們要擴充套件系統以支援多個閘道(gateways),我們需要使用不同的主機名稱或子網域名稱來存取每個閘道。例如,瀏覽器的主要閘道可以使用 flix-tube.com 這個網域名稱,而行動裝置的閘道可以使用 m.flix-tube.com 這個網域名稱。為了實作這個功能,我們需要使用 DNS 服務提供者來購買網域名稱並組態每個網域名稱指向特定的閘道微服務的 IP 地址。

閘道微服務

閘道微服務的主要工作之一是將 HTTP 請求轉發到叢集內部。稍後的章節中,我們將看到這個過程的程式碼範例。一個更先進的閘道(FlixTube 目前尚未達到這個級別)將具有 REST API 路由,可以向多個內部微服務發出請求,然後整合多個回應成一個單一的回應,傳回給前端。例如,假設有一個 REST API 需要取得使用者的歷史記錄,這可能需要向使用者帳戶微服務和歷史記錄微服務發出 HTTP 請求,然後整合兩個回應成一個單一的回應,傳送給前端。

Kubernetes 與微服務架構

在 Kubernetes 的環境下,我們可以輕鬆地佈署和管理微服務架構。瀏覽器閘道、行動裝置閘道、FlixTube 等都可以作為獨立的微服務,彼此之間可以透過 REST API 進行通訊。這樣的架構可以提供高度的可擴充套件性和靈活性,方便未來的發展和維護。

圖表翻譯:

  graph LR
    Browser[瀏覽器] -->|HTTP 請求| Gateway[閘道]
    Gateway -->|轉發請求| Microservice1[微服務 1]
    Gateway -->|轉發請求| Microservice2[微服務 2]
    Microservice1 -->|回應| Gateway
    Microservice2 -->|回應| Gateway
    Gateway -->|整合回應| Browser

這個圖表展示了閘道如何將 HTTP 請求轉發到多個微服務,然後整合多個回應成一個單一的回應,傳回給瀏覽器。

微服務架構概覽

在現代軟體開發中,微服務架構是一種流行的設計模式。它將一個大型應用程式分解為多個小型、獨立的服務,每個服務負責一個特定的業務邏輯。這種架構使得開發、佈署和維護更加靈活和高效。

微服務的優點

  1. 彈性擴充套件:每個微服務可以獨立擴充套件,無需影響其他服務。
  2. 容錯性:如果一個微服務出現問題,其他服務可以繼續執行,減少整體系統的風險。
  3. 技術多樣性:不同的微服務可以使用不同的程式設計語言、框架和資料函式庫,提高開發效率和適應性。

Admin Gateway 和 FlixTube 的應用

在 FlixTube 的例子中,Admin Gateway 和 Mobile Application 是兩個不同的入口點,它們都與相同的內部微服務進行互動。這種設計使得系統可以根據不同的使用者需求提供不同的入口點和使用體驗。

微服務之間的互動

每個入口點(如 Admin Gateway 和 Mobile Application)都可以與多個內部微服務進行互動,以實作業務邏輯。這種互動可以透過 API、訊息佇列或其他通訊機制實作。

內容解密:

  graph LR
    A[Admin Gateway] -->|HTTP Request| B[Authentication Service]
    B -->|Token| C[User Service]
    C -->|User Data| A
    D[Mobile Application] -->|HTTP Request| B
    B -->|Token| C
    C -->|User Data| D

圖表翻譯:

上述 Mermaid 圖表展示了 Admin Gateway 和 Mobile Application 如何與內部微服務(Authentication Service 和 User Service)進行互動。當使用者透過 Admin Gateway 或 Mobile Application 登入時,會向 Authentication Service 傳送 HTTP 請求,以獲得授權令牌。然後,令牌會被傳遞給 User Service,以取得使用者資料。這種設計使得系統可以實作單一登入和授權,提高使用者經驗和安全性。

FlixTube 的使用者互動與介面設計

FlixTube 的使用者可以透過多種方式與應用程式進行互動。目前的版本只有一個網頁前端的入口,但未來可以擴充套件到多個入口。

FlixTube 的 UI 介面

如果您尚未探索 FlixTube 的 UI,現在是時候了。根據 10.5.2 節的說明,建置並啟動應用程式,然後在網頁瀏覽器中導航到 FlixTube 的首頁。圖 10.10 顯示了 FlixTube 首頁(影片列表)在上傳一些影片後的樣子。我們可以點選任何影片來觀看,也可以點選導航列中的「影片」、「上傳」和「歷史」來切換主要頁面。

FlixTube 實作為傳統的伺服器端渲染網頁,而非現代的單頁應用程式(SPA)。這裡呈現的 FlixTube 前端是一個多頁應用程式(MPA)。如果這是一個真正的商業應用程式,我可能會使用 React、Angular 或 Vue 等框架來實作 SPA,而非 MPA。但這並不意味著 SPA 比 MPA 更好;兩者都有其折衷和優缺點。

為什麼不使用現代的 SPA 框架?

原因很簡單:這超出了本章的範圍。本章關注的是後端和微服務的實作,而非 UI 的細節。因此,前端的實作盡可能簡單。

FlixTube 使用 Express 和 Handlebars 範本引擎,結合 vanilla JavaScript,在前端實作伺服器端渲染。FlixTube 的前端是純粹的 HTML、CSS 和 JavaScript,沒有使用任何現代化的框架。

實作細節

以下是 gateway 微服務的主要程式碼檔案 (chapter-10/gateway/src/index.js) 中的一個摘錄,展示了渲染首頁的 HTTP GET 路由。這個路由處理器使用 Axios 從資料函式庫微服務中取回資料,然後使用 video-list 範本渲染網頁,並將影片列表作為範本資料傳遞。

app.get("/", async (req, res) => {
  const videosResponse = await axios.get("http://metadata:3000/videos");
  res.render("video-list", {
    videos: videosResponse.data.videos
  });
});

雖然我沒有使用 JavaScript 框架,但我使用了 Tailwind CSS 這個 CSS 框架,以便在不需要深入瞭解 CSS 細節的情況下建立一個漂亮的 UI。

首頁範本

以下是 FlixTube 首頁的 Handlebars 範本 (chapter-10/gateway/src/views/video-list.hbs)。這是一個包含在 HTML 檔案中的 Handlebars 範本。Handlebars 是一個簡單而強大的範本函式庫,允許我們根據資料生成網頁。如果您回顧一下之前的程式碼,您會看到從資料函式庫微服務中取回的影片列表被作為範本資料傳遞給這個範本。資料和範本的結合生成了最終顯示給使用者的 HTML。

<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <title>FlixTube: Home</title>
  <!--... -->
</head>
<body>
  <div class="flex flex-col">
    <!--... -->
  </div>
</body>
</html>

內容解密:

上述程式碼展示瞭如何使用 Express 和 Handlebars 來渲染一個網頁,並將資料從資料函式庫微服務中取回。這是一個基本的範例,展示瞭如何在 FlixTube 中實作伺服器端渲染。

圖表翻譯:

  graph LR
    A[使用者請求] -->|HTTP GET| B[Express 路由]
    B -->|Axios 請求| C[資料函式庫微服務]
    C -->|資料回應| B
    B -->|渲染範本| D[Handlebars 範本]
    D -->|HTML 回應| A

這個圖表展示了使用者請求、Express 路由、Axios 請求、資料函式庫微服務、Handlebars 範本和最終的 HTML 回應之間的流程。

影片列表網頁的實作

在本文中,我們將實作一個影片列表網頁,展示所有上傳的影片。首先,我們需要定義一個HTTP GET路由處理器,負責渲染影片列表網頁。

定義路由處理器

以下是定義路由處理器的程式碼:

// 定義HTTP GET路由處理器
app.get('/videos', (req, res) => {
  // 從metadata服務中取得影片列表
  axios.get('https://metadata-service.com/videos')
   .then(response => {
      const videos = response.data;
      // 渲染影片列表網頁
      res.render('video-list', { videos });
    })
   .catch(error => {
      console.error(error);
      res.status(500).send('Error fetching videos');
    });
});

在這個例子中,我們使用Axios函式庫向metadata服務傳送HTTP GET請求,以取得影片列表。然後,我們使用Handlebars範本引擎渲染影片列表網頁,並將影片列表作為資料傳遞給範本。

Handlebars範本

以下是Handlebars範本的程式碼:

<!-- video-list.handlebars -->
<div class="m-4">
  <h1>影片列表</h1>
  <ul>
    {{#each videos}}
      <li>
        <a href="/videos/{{id}}">{{title}}</a>
      </li>
    {{/each}}
  </ul>
</div>

在這個範本中,我們使用{{#each}}助手迭代影片列表,並使用{{id}}{{title}}顯示每個影片的ID和標題。

網頁渲染

當路由處理器渲染網頁時,會將影片列表作為資料傳遞給Handlebars範本。然後,範本會使用這些資料渲染出最終的網頁。

內容解密:

  • 我們定義了一個HTTP GET路由處理器,負責渲染影片列表網頁。
  • 我們使用Axios函式庫向metadata服務傳送HTTP GET請求,以取得影片列表。
  • 我們使用Handlebars範本引擎渲染影片列表網頁,並將影片列表作為資料傳遞給範本。
  • 我們在Handlebars範本中使用{{#each}}助手迭代影片列表,並使用{{id}}{{title}}顯示每個影片的ID和標題。

圖表翻譯:

  flowchart TD
    A[HTTP GET請求] --> B[取得影片列表]
    B --> C[渲染影片列表網頁]
    C --> D[顯示影片列表]

在這個流程圖中,我們展示了HTTP GET請求、取得影片列表、渲染影片列表網頁和顯示影片列表的流程。

網頁內容與視訊串流

在網頁的頂部,我們可以看到主要內容的呈現。這裡有一個容器,用於顯示視訊清單。

視訊清單

當我們檢視視訊清單的程式碼時,可以看到以下結構:

{{#if videos}}
  {{#each videos}}
    <div class="mt-1">
      <a href="/video?id={{this._id}}">
        {{this.name}}
      </a>
    </div>
  {{/each}}
{{else}}
  No videos uploaded yet.
{{/if}}

這段程式碼使用 Handlebars 語法,來渲染視訊清單。當 videos 陣列不為空時,會使用 each 區塊來迴圈渲染每個視訊專案。如果 videos 陣列為空,則顯示 “No videos uploaded yet.” 的訊息。

視訊串流

現在,讓我們來看看視訊串流的過程。視訊串流是 FlixTube 的核心功能,我們在第 2 章就曾經介紹過。現在,我們要來看看視訊串流在完成的 FlixTube 應用程式中是如何運作的。

視訊串流程

以下是視訊串流的流程圖:

  flowchart TD
    A[外部雲端儲存] --> B[視訊儲存微服務]
    B --> C[閘道微服務]
    C --> D[使用者網頁瀏覽器]

這個流程圖顯示了視訊串流從外部雲端儲存開始,經過視訊儲存微服務、閘道微服務,最終到達使用者網頁瀏覽器。

視訊儲存微服務

讓我們來看看視訊儲存微服務的程式碼:

// chapter-10/azure-storage/src/index.js
app.get('/video', (req, res) => {
  // 從 Azure 儲存中取得視訊
  //...
  // 將視訊串流到 HTTP 回應
  //...
});

這段程式碼顯示瞭如何從 Azure 儲存中取得視訊,並將其串流到 HTTP 回應。

圖表翻譯

上述的流程圖可以用以下文字來描述:視訊串流的流程從外部雲端儲存開始,經過視訊儲存微服務、閘道微服務,最終到達使用者網頁瀏覽器。每個階段都有其特定的功能,例如視訊儲存微服務負責從雲端儲存中取得視訊,而閘道微服務則負責將視訊串流到使用者網頁瀏覽器。

Kubernetes 與 Azure Storage 的整合:FlixTube 的影片串流技術

在 FlixTube 的架構中,影片串流的過程涉及多個微服務和外部雲端儲存。當使用者透過網頁瀏覽器存取影片時,影片資料會從外部雲端儲存(Azure Storage)直接傳遞到使用者的瀏覽器中。這個過程中,資料會被轉發從一個微服務到另一個微服務,由玄貓負責管理和協調。

FlixTube 的影片串流路徑

如圖 10.11 所示,FlixTube 的影片串流路徑涉及以下步驟:

  1. 使用者透過網頁瀏覽器傳送請求,要求播放特定的影片。
  2. 請求被轉發到 FlixTube 的閘道器(Gateway)。
  3. 閘道器將請求轉發到負責影片串流的微服務。
  4. 微服務從外部雲端儲存(Azure Storage)中讀取影片資料。
  5. 影片資料被直接傳遞到使用者的瀏覽器中。

實作影片串流的 Node.js 程式碼

以下是 FlixTube 中實作影片串流的 Node.js 程式碼(chapter-10/video-streaming/src/index.js):

app.get("/video", async (req, res) => {
  const videoId = req.query.id;
  const blobService = createBlobService();

  const containerClient =
    blobService.getContainerClient(STORAGE_CONTAINER_NAME);
  const blobClient = containerClient.getBlobClient(videoId);
  const properties = await blobClient.getProperties();

  res.writeHead(200, {
    "Content-Type": "video/mp4",
  });
  const response = await blobClient.download();
  response.readableStreamBody.pipe(res);
});

這段程式碼負責處理使用者的請求,從外部雲端儲存中讀取影片資料,並直接傳遞給使用者的瀏覽器。這個過程中,玄貓負責管理和協調微服務之間的資料轉發,確保影片串流的順暢進行。

內容解密:

  • app.get("/video", async (req, res) => {... });:定義了一個 HTTP GET 請求的路由,負責處理使用者的影片播放請求。
  • const videoId = req.query.id;:從請求中提取影片的 ID。
  • const blobService = createBlobService();:建立了一個 Azure Blob Storage 服務的例項。
  • const containerClient = blobService.getContainerClient(STORAGE_CONTAINER_NAME);:取得了指定容器的客戶端例項。
  • const blobClient = containerClient.getBlobClient(videoId);:取得了指定 Blob 的客戶端例項。
  • const properties = await blobClient.getProperties();:取得了 Blob 的屬性。
  • res.writeHead(200, { "Content-Type": "video/mp4" });:設定了 HTTP 回應的狀態碼和內容型別。
  • const response = await blobClient.download();:下載了 Blob 的內容。
  • response.readableStreamBody.pipe(res);:將下載的內容直接傳遞給使用者的瀏覽器。

圖表翻譯:

  flowchart TD
    A[使用者請求] --> B[閘道器]
    B --> C[微服務]
    C --> D[外部雲端儲存]
    D --> E[下載影片資料]
    E --> F[傳遞給使用者瀏覽器]

這個流程圖描述了 FlixTube 中影片串流的過程,從使用者請求到影片資料被傳遞給使用者瀏覽器。

使用 Node.js 實作影片串流

在設計影片串流微服務時,我們需要考慮如何將影片資料從 Azure 儲存體傳輸到使用者端。為了實作這一功能,我們可以使用 Node.js 的串流功能。

影片串流路由

首先,我們需要定義一個 HTTP GET 路由,來處理影片串流請求。這個路由需要從 Azure 儲存體中取得影片資料,並將其傳輸到使用者端。

app.get("/video", (req, res) => {
  const videoId = req.query.id;
  //...
});

在這個路由中,我們需要取得影片 ID,並使用它來從 Azure 儲存體中取得影片資料。

使用 Axios 進行 HTTP 請求

為了從 Azure 儲存體中取得影片資料,我們可以使用 Axios 進行 HTTP 請求。Axios 是一個根據 Promise 的 HTTP 客戶端,提供了一個簡單且強大的 API,用於傳送 HTTP 請求。

const response = await axios({
  method: "GET",
  data: req,
  responseType: "stream",
  //...
});

在這個請求中,我們需要設定 responseTypestream,以便 Axios 傳回一個串流物件。

將串流物件傳輸到使用者端

一旦我們取得了影片資料的串流物件,我們就可以將其傳輸到使用者端。為了實作這一功能,我們可以使用 Node.js 的 pipe 方法。

response.data.pipe(res);

這行程式碼將串流物件傳輸到使用者端,讓使用者端可以接收影片資料。

傳送「影片觀看」訊息

除了傳輸影片資料外,我們還需要傳送一個「影片觀看」訊息給其他微服務。這個訊息可以用來記錄使用者的觀看歷史。

broadcastViewedMessage(messageChannel, videoId);

這行程式碼發送了一個「影片觀看」訊息給其他微服務,讓它們可以接收並處理這個訊息。

閘道微服務

最後,影片串流會經過閘道微服務,然後才傳輸到使用者端。閘道微服務負責將影片串流從影片串流微服務傳輸到自己的 HTTP 回應中。

app.get("/api/video", (req, res) => {
  const response = await axios({
    method: "GET",
    data: req,
    responseType: "stream",
    //...
  });
  response.data.pipe(res);
});

這行程式碼將影片串流從影片串流微服務傳輸到自己的 HTTP 回應中,讓使用者端可以接收影片資料。

影片串流技術實作

在前端,使用 HTML 的 video 元素來播放影片,如下所示:

<video controls autoplay muted>
  <source src={{video.url}} type="video/mp4">
  Your browser does not support the video tag.
</video>

這個 video 元素會向後端傳送 HTTP GET 請求,以取得影片的 URL。後端的閘道器服務(gateway)會接收到這個請求,並將其轉發給影片串流服務(video-streaming)。影片串流服務會再次將請求轉發給影片儲存服務(video-storage),並從中取得影片資料。

取得影片資料後,影片儲存服務會將其傳回給影片串流服務,然後影片串流服務會將其傳回給閘道器服務,最後閘道器服務會將其傳回給前端。前端的 video 元素會接收到影片資料,並將其播放給使用者。

影片上傳

除了影片串流,另一個重要的功能是影片上傳。影片上傳是指使用者將自己的影片上傳到 FlixTube 的過程。這個過程與影片串流類別似,但有一些不同之處。

首先,閘道器服務需要定義一個 HTTP POST 路由,用於接收使用者上傳的影片。然後,閘道器服務會將請求轉發給影片上傳服務(video-upload)。影片上傳服務會處理使用者上傳的影片,並將其儲存到影片儲存服務中。

以下是影片上傳的 Node.js 程式碼範例:

const express = require('express');
const app = express();
const multer = require('multer');

const upload = multer({ dest: 'uploads/' });

app.post('/upload', upload.single('video'), (req, res) => {
  // 處理使用者上傳的影片
  const video = req.file;
  // 將影片儲存到影片儲存服務中
  //...
  res.send('影片上傳成功');
});

這個程式碼使用 multer 中介軟體來處理使用者上傳的影片,並將其儲存到 uploads/ 目錄中。然後,閘道器服務會將請求轉發給影片上傳服務,影片上傳服務會處理使用者上傳的影片,並將其儲存到影片儲存服務中。

內容解密:
  • 使用 HTML 的 video 元素來播放影片
  • 定義 HTTP GET 路由,用於取得影片的 URL
  • 使用 Node.js 的 multer 中介軟體來處理使用者上傳的影片
  • 將使用者上傳的影片儲存到影片儲存服務中

圖表翻譯:

  graph LR
    A[使用者] -->|上傳影片| B[閘道器服務]
    B -->|轉發請求| C[影片上傳服務]
    C -->|處理請求| D[影片儲存服務]
    D -->|儲存影片| C
    C -->|傳回結果| B
    B -->|傳回結果| A

這個圖表展示了使用者上傳影片的過程,從使用者上傳影片到閘道器服務,然後轉發請求給影片上傳服務,最後儲存到影片儲存服務中。

影片上傳流程深度剖析

在 FlixTube 的架構中,影片上傳流程是一個複雜的過程,涉及多個微服務和外部雲端儲存。下面,我們將深入探討這個流程,從使用者選擇影片檔案到影片被安全儲存於外部雲端儲存的整個過程。

上傳流程概覽

當使用者選擇了一個影片檔案並上傳時,該檔案會先到達叢集中的入口微服務(Gateway Microservice)。入口微服務負責接收使用者的請求並將其轉發給適當的微服務。在這種情況下,入口微服務會將上傳的影片轉發給影片上傳微服務(Video Upload Microservice)。

從系統架構的視角來看,FlixTube 的影片串流和上傳機制已展現出良好的擴充套件性和彈性。透過微服務架構,FlixTube 將不同的功能模組拆分,例如影片上傳、影片儲存、影片串流等,使其更容易獨立開發、佈署和維護。 然而,多閘道模式的引入雖然提升了系統對不同前端應用程式的適應性,但也增加了系統複雜度和管理成本。技術團隊需要關注閘道之間的負載平衡、請求路由和錯誤處理等問題,才能確保系統的穩定性和效能。 玄貓認為,未來發展方向應著重於服務網格(Service Mesh)技術的整合,以簡化微服務之間的通訊和管理,並提升系統的可觀測性。隨著影片資料量的增長,FlixTube 也需要持續最佳化影片儲存和串流策略,例如採用更先進的編碼技術和內容分發網路(CDN)加速,以提供更流暢的使用者經驗。對於重視長期發展的團隊而言,提前佈局這些關鍵技術將有助於提升 FlixTube 的市場競爭力。