什麼是正向代理和反向代理?

萤火架构發表於2024-03-26

從字面意思上看,代理就是代替處理的意思,一個物件有能力代替另一個物件處理某一件事。

代理,這個詞在我們的日常生活中也不陌生,比如在購物、旅遊等場景中,我們經常會委託別人代替我們完成某些任務。在技術領域,這個概念也被廣泛應用,尤其是在計算機網路通訊和程式設計中,代理扮演著相當重要的角色,涉及控制訪問、安全保護、能力擴充套件等複雜而強大的方面。

網路通訊中的代理

在計算機網路中,說到代理,經常會談到正向代理和反向代理的概念。

在詳細展開前,我們先使用一個比喻來形象的理解下這兩個概念:小明去飯館吃飯,正向代理就像是小明的朋友幫他去點餐,服務員並不知道最終吃飯的人是小明;而反向代理則像是飯館的服務員,他們決定把小明的訂單送到哪個廚師手裡去做。透過這個比喻,我們可以初步感受到正向代理和反向代理在角色和功能上的不同。

搞清楚網路通訊中的代理和反向代理,大家只要弄明白兩件事:你在公司的電腦是怎麼訪問到外網的,你部署的網站或者API又是怎麼被外網訪問到的。

公司電腦上網

首先看公司電腦上網:公司裡的電腦一般不會直接連線到網際網路,它們通常在一個內網環境中,這既有成本的考慮,也有安全控制的需要。辦公電腦一般會先連線到交換機,交換機再連線到路由器,路由器再連線到網際網路。

在這些連線中,交換機只是一個小透明,辦公電腦可以看到路由器,路由器也可以看到辦公電腦,所以交換機不是我們這裡所說的代理。

這裡真正的代理是路由器,辦公電腦訪問網路時,請求先到達路由器,路由器做個請求來源的登記,記下這個請求是從哪臺電腦發出的,然後再發到網際網路上。請求出了路由器,網際網路上能夠看到的就是這個路由器,而看不到你的辦公電腦。資料從遠端伺服器返回時,也是先到達這個路由器,路由器再根據之前做的請求來源登記,將資料轉發到對應的辦公電腦上。

這種場景下,路由器就是一個正向代理,代理內網電腦訪問網際網路。

什麼是正向代理和反向代理?

除了使用路由器這種比較常見的代理方式,其實還有很多方式,比如在瀏覽器中配置HTTP代理,只允許透過瀏覽器訪問外網。

網站被外網訪問

再看網站或者API是怎麼被外網訪問到的:通常情況下,大家的伺服器也是放在內網中的,直接暴露在網際網路上會有安全風險,也不利於管理。所以,我們會在伺服器和網際網路之間設定一個代理伺服器,通常是Nginx或者LVS這種負載均衡器。當外網的使用者想要訪問你的網站或API時,他們的請求首先會傳送到這個代理伺服器上。

這個代理伺服器就是一個反向代理。

什麼是正向代理和反向代理?

反向代理伺服器接到請求後,它知道內網中哪臺伺服器能提供這個服務,於是它就把請求轉發給對應的伺服器。伺服器處理完這個請求後,再把結果傳送回反向代理伺服器,最後由反向代理伺服器返回給外網的使用者。

對比

以上就是計算機網路中正向代理和反向代理的基本原理和應用場景,我們再做一個對比,加深印象。

正向代理和反向代理的區別主要體現在它們服務的物件和用途上:

對比項

正向代理(Forward Proxy)

反向代理(Reverse Proxy)

服務物件

客戶端

伺服器

主要用途

- 幫助客戶端訪問無法直接訪問的資源

- 進行訪問控制和快取以提高速度和安全性

- 隱藏伺服器真實IP地址

- 提供負載均衡功能

- 提高伺服器訪問速度和安全性

工作方式

- 客戶端配置代理伺服器,請求先傳送至代理伺服器

- 代理伺服器代為訪問目標伺服器並返回資源給客戶端

- 客戶端請求傳送至反向代理伺服器

- 反向代理伺服器根據配置轉發請求到內部網路的特定伺服器

- 從伺服器獲取響應後返回給客戶端

舉例說明

- 使用瀏覽器設定代理伺服器,所有上網請求經由代理伺服器訪問網際網路資源

- 根據負載均衡策略將使用者請求分發到不同伺服器處理

簡單來說,正向代理是客戶端的代理,幫助客戶端訪問到無法直接獲取的資源;反向代理是伺服器的代理,幫助伺服器平滑處理來自各方的請求。

程式設計中的代理

在程式設計中,也有一個代理模式,雖然和網路中的正向代理或反向代理的概念不完全一樣,但本質上它們都是代理的概念,都是作為中介提供隔離、隱藏、控制訪問和功能增強等作用。

Just show me the code! 現在我們用Go來編寫一個代理的例項程式,假設我們有一個資源類,我們希望在訪問這個資源時,記錄訪問次數,並在資源不再被引用時自動釋放資源。

首先,定義一個資源介面Resource和實現這個介面的資源類MyResource:

package main

import (
    "fmt"
)

// Resource 介面定義了資源需要實現的方法
type Resource interface {
    Use()
    Release()
}

// MyResource 是實現了Resource介面的資源類
type MyResource struct{}

func (r *MyResource) Use() {
    fmt.Println("Using MyResource")
}

func (r *MyResource) Release() {
    fmt.Println("Releasing MyResource")
}

然後,定義一個代理的類 ResourceProxy,它包含了對資源的引用和引用計數,同時它也實現了Resource介面。

// ResourceProxy 是代理的結構體,包含資源和引用計數
type ResourceProxy struct {
    resource Resource
    refCount int
}

// NewResourceProxy 是ResourceProxy的建構函式
func NewResourceProxy(resource Resource) *ResourceProxy {
    return &ResourceProxy{resource: resource, refCount: 1} // 初始引用計數為1
}

// Use 方法增加引用計數並使用資源
func (sr *ResourceProxy) Use() {
    sr.refCount++
    fmt.Printf("Resource is used %d times\n", sr.refCount)
    sr.resource.Use()
}

// Release 方法減少引用計數,當計數為0時釋放資源
func (sr *ResourceProxy) Release() {
    sr.refCount--
    if sr.refCount == 0 {
        sr.resource.Release()
    } else {
        fmt.Printf("Resource is still used by %d references\n", sr.refCount)
    }
}

最後我們使用這個代理:

func main() {
    resource := &MyResource{}
    proxyRef := NewResourceProxy(resource)

    proxyRef.Use() // 使用資源,引用計數增加
    proxyRef.Release() // 釋放一次引用,引用計數減少到0,資源被釋放

    // Output:
    // Resource is used 1 times
    // Using MyResource
    // Releasing MyResource
}

這個簡單的例子演示了代理在資源管理中的應用,可以根據實際需要新增更多複雜的邏輯,比如錯誤處理、同步控制、日誌記錄等。

在程式設計中,代理模式是一種結構型設計模式,它讓我們能提供一個替代品來代表另一個物件,這個替代品控制著對原物件的訪問,可以在訪問原物件前後進行一些額外處理。

透過上邊的示例,我們可以發現代理模式的三個主要角色:

  • 抽象主題(Subject):定義了代理和真實主題的共用介面,這樣在任何使用真實主題的地方都可以使用代理。
  • 真實主題(Real Subject):實現了抽象主題的具體類,代表了實際的物件,是最終要使用的物件。
  • 代理(Proxy):包含對真實主題的引用,控制著對真實主題的訪問,並可能負責建立和刪除它。通常會做一些額外的事情來實現自己的價值。

在程式碼實際實現時,代理模式其實有多種不同的實現,包括:

  • 遠端代理(Remote Proxy):為一個物件在不同的地址空間(通常是不同計算機上的服務)提供區域性代表。常見的如RPC、gRPC等,透過本地代理物件,客戶端可以像呼叫本地介面一樣訪問遠端服務,而無需關心網路通訊的細節。
  • 虛擬代理(Virtual Proxy):透過它來存放例項化需要很長時間的真實物件。常見的就是懶載入,比如載入一個大檔案或者從資料庫中讀取大量資料,我們不希望在程式啟動時就立刻載入,而是希望在真正需要這些資料的時候才去載入它們。
  • 保護代理(Protection Proxy):控制對原始物件的訪問。用於物件應該有不同訪問許可權的時候。
  • 智慧引用(Smart Reference):當物件被引用時,提供一些額外的操作,比如計算物件被引用的次數。上邊提供的程式碼示例就是一個智慧引用的例子。

這裡就不展示更多的程式碼了,關鍵是在合適的時機使用恰當的代理模式來解決問題,這需要細細體會。

做個簡單的小結,代理模式就像程式中的一個“中間人”,在不需要直接訪問某個物件,或者直接訪問某個物件不太方便或者不符合需求時,代理模式提供了一個非常靈活的解決方案。


正如本文所探討的,代理模式在網路通訊和程式設計中都扮演著重要的角色。它透過提供一箇中間層,增強了系統的安全性、靈活性和可維護性。掌握代理,我們就擁有了在合適的場景下解決問題的一種強大能力。希望本文的討論能對你有一點用處。

相關文章