《Go語言高級編程》玩轉RPC

《Go語言高級編程》玩轉RPC

一、客戶端 RPC 實現原理:異步調用機制

Go 的 RPC 客戶端支持同步和異步調用,核心在于 Client.Go 方法的實現:

1. 同步調用(Client.Call)的本質
func (client *Client) Call(serviceMethod string, args, reply interface{}) error {// 通過 Client.Go 發起異步調用,阻塞等待結果call := <-client.Go(serviceMethod, args, reply, make(chan *Call, 1)).Donereturn call.Error
}

同步調用本質是封裝了異步流程:創建調用請求后,通過通道阻塞等待結果返回。

2. 異步調用(Client.Go)的流程
func (client *Client) Go(serviceMethod string, args, reply interface{}, done chan *Call) *Call {call := &Call{ServiceMethod: serviceMethod,Args: args,Reply: reply,Done: make(chan *Call, 10), // 帶緩沖通道,避免阻塞}client.send(call) // 線程安全地發送調用請求return call
}

異步調用返回 Call 對象,調用完成后通過 call.Done 通道通知結果:

func (call *Call) done() {select {case call.Done <- call: // 結果寫入通道default: // 通道滿時不阻塞(由調用方保證緩沖區足夠)}
}
3. 異步調用示例
func doClientWork(client *rpc.Client) {// 發起異步調用,不阻塞當前 goroutinecall := client.Go("HelloService.Hello", "hello", new(string), nil)// 執行其他任務...// 等待調用結果call = <-call.Doneif call.Error != nil {log.Fatal(call.Error)}fmt.Println("參數:", call.Args.(string), "響應:", *call.Reply.(*string))
}

核心優勢:異步調用允許客戶端在等待 RPC 結果時處理其他任務,提升并發性能。

二、基于 RPC 實現 Watch 監控功能

通過 RPC 實現實時監控(類似訂閱-發布模式),以 KV 存儲為例:

1. 服務端設計(KVStoreService
type KVStoreService struct {m      map[string]string       // KV 數據存儲filter map[string]func(string) // 監控過濾器列表mu     sync.Mutex              // 互斥鎖保護共享資源
}// 獲取 KV 值
func (p *KVStoreService) Get(key string, value *string) error {p.mu.Lock(); defer p.mu.Unlock()if v, ok := p.m[key]; ok {*value = vreturn nil}return errors.New("not found")
}// 設置 KV 值,并觸發監控回調
func (p *KVStoreService) Set(kv [2]string, reply *struct{}) error {p.mu.Lock(); defer p.mu.Unlock()key, value := kv[0], kv[1]if oldVal := p.m[key]; oldVal != value {for _, fn := range p.filter {fn(key) // 調用所有監控過濾器}}p.m[key] = valuereturn nil
}// 監控方法:注冊過濾器,等待 key 變化或超時
func (p *KVStoreService) Watch(timeout int, keyChanged *string) error {id := "watch-" + time.Now().Format("150405") + "-" + strconv.Itoa(rand.Intn(1000))ch := make(chan string, 10)p.mu.Lock()p.filter[id] = func(key string) { ch <- key } // 注冊過濾器p.mu.Unlock()select {case <-time.After(time.Duration(timeout) * time.Second):return errors.New("timeout")case key := <-ch:*keyChanged = keyreturn nil}
}
2. 客戶端調用
func doClientWork(client *rpc.Client) {// 啟動獨立 goroutine 執行監控,阻塞等待 key 變化go func() {var key stringif err := client.Call("KVStoreService.Watch", 30, &key); err != nil {log.Fatal(err)}fmt.Println("監控到變化的 key:", key)}()// 修改 KV 值,觸發監控回調if err := client.Call("KVStoreService.Set", [2]string{"abc", "new-value"}, new(struct{})); err != nil {log.Fatal(err)}time.Sleep(3 * time.Second)
}

核心原理

  • 服務端為每個 Watch 調用生成唯一 ID,綁定過濾器函數到 filter 列表。
  • Set 方法修改數據時,遍歷調用所有過濾器,通過通道通知監控方。
  • 客戶端通過異步 goroutine 阻塞監聽,實現實時監控。
三、反向 RPC:內網服務主動連接外網

傳統 RPC 是客戶端連接服務端,反向 RPC 則相反,適用于內網服務無法被外網直接訪問的場景:

1. 內網服務端(主動連接外網)
func main() {rpc.Register(new(HelloService)) // 注冊服務for {// 主動連接外網服務器conn, err := net.Dial("tcp", "外網IP:1234")if err != nil {time.Sleep(1 * time.Second)continue}// 基于連接提供 RPC 服務rpc.ServeConn(conn)conn.Close()}
}
2. 外網客戶端(監聽連接)
func main() {listener, err := net.Listen("tcp", ":1234")if err != nil {log.Fatal(err)}clientChan := make(chan *rpc.Client)// 后臺 goroutine 接受連接并創建客戶端go func() {for {conn, err := listener.Accept()if err != nil {log.Fatal(err)}clientChan <- rpc.NewClient(conn) // 將客戶端放入通道}}()doClientWork(clientChan) // 從通道獲取客戶端并調用
}func doClientWork(clientChan <-chan *rpc.Client) {client := <-clientChandefer client.Close()var reply stringif err := client.Call("HelloService.Hello", "hello", &reply); err != nil {log.Fatal(err)}fmt.Println(reply)
}

核心邏輯

  • 內網服務主動撥號外網服務器,建立連接后提供 RPC 服務。
  • 外網客戶端監聽端口,接收連接并轉換為 RPC 客戶端,通過通道傳遞給業務邏輯。
  • 適用于內網服務需被外網訪問,但內網無法暴露端口的場景(如防火墻限制)。
四、上下文信息:基于連接的定制化服務

為每個 RPC 連接添加上下文(如認證狀態、客戶端信息),提升服務安全性和靈活性:

1. 服務端改造(包含連接和狀態)
type HelloService struct {conn    net.Conn    // 連接對象,可獲取客戶端地址等信息isLogin bool        // 登錄狀態
}// 登錄方法
func (p *HelloService) Login(request string, reply *string) error {if request != "user:password" {return errors.New("認證失敗")}log.Println("登錄成功")p.isLogin = true*reply = "登錄成功"return nil
}// 需要認證的 Hello 方法
func (p *HelloService) Hello(request string, reply *string) error {if !p.isLogin {return errors.New("請先登錄")}*reply = "hello:" + request + ", from " + p.conn.RemoteAddr().String()return nil
}
2. 服務端啟動邏輯(為每個連接創建獨立服務)
func main() {listener, err := net.Listen("tcp", ":1234")if err != nil {log.Fatal(err)}for {conn, err := listener.Accept()if err != nil {log.Fatal(err)}// 為每個連接啟動獨立 goroutine,綁定 HelloService 實例go func(c net.Conn) {defer c.Close()server := rpc.NewServer()server.Register(&HelloService{conn: c}) // 傳入連接對象server.ServeConn(c)}(conn)}
}
3. 客戶端調用流程
func main() {client, err := rpc.Dial("tcp", "localhost:1234")if err != nil {log.Fatal(err)}// 先登錄var loginReply stringif err := client.Call("HelloService.Login", "user:password", &loginReply); err != nil {log.Fatal("登錄失敗:", err)}// 再調用 Hello 方法var helloReply stringif err := client.Call("HelloService.Hello", "world", &helloReply); err != nil {log.Fatal("調用失敗:", err)}fmt.Println(helloReply) // 輸出包含客戶端地址的響應
}

核心優勢

  • 通過 net.Conn 獲取客戶端上下文(如 IP 地址、連接狀態)。
  • 基于連接狀態實現認證邏輯(如登錄驗證),確保服務安全性。
  • 每個連接獨立維護狀態,避免多客戶端數據混淆。
五、關鍵概念總結
  1. 異步調用:通過通道機制實現非阻塞 RPC 調用,提升客戶端并發能力。
  2. Watch 機制:利用函數回調和通道,實現服務端數據變化的實時通知。
  3. 反向 RPC:打破傳統 C/S 模式,適用于內網服務主動對外提供能力的場景。
  4. 上下文管理:基于連接綁定狀態(如認證信息),實現定制化服務邏輯。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/87178.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/87178.shtml
英文地址,請注明出處:http://en.pswp.cn/web/87178.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

四大核心要素驅動汽車智能化創新與相關芯片競爭格局

作者&#xff1a;北京華興萬邦管理咨詢有限公司 翔煜 商瑞 智能汽車時代的加速到來&#xff0c;使車載智能系統面臨前所未有的算力需求。隨著越來越多車型引入電子電氣架構轉向中心化、智能駕駛的多傳感器融合、智能座艙的多模態交互以及生成式AI驅動的虛擬助手等創新技術&a…

照明新基建:塔能科技如何用數字骨骼支撐智慧城市生長

一、能源管理困局&#xff1a;雙碳目標下的市政用電痛點 在雙碳背景下&#xff0c;城市照明用電量已引起市政部門的重點關注。據國家統計局統計&#xff1a;我國城市照明用電量已占據全市城市用電量的28%&#xff0c;部分城市的照明用電量已高達35%以上&#xff0c;高壓鈉燈傳統…

讓Claude Code像Cursor一樣好用

最近折騰AI工具&#xff0c;發現Claude Code真是個寶藏。但說實話&#xff0c;初學者一上手&#xff0c;十有八九會被命令行那一堆黑框框勸退。你以為你用熟了&#xff1f;其實你只解鎖了Claude Code不到20%的威力&#xff0c;剩下的80%都藏在命令行背后的“黑魔法”里。00后誰…

ROS 2 中更改從設備(如電機控制器)的運動模式

在 ROS 2 中更改從設備&#xff08;如電機控制器&#xff09;的運動模式&#xff08;例如從位置模式切換到速度模式&#xff09;&#xff0c;需要通過操作模式&#xff08;Mode of Operation&#xff0c;對應對象字典索引0x6060&#xff09; 進行設置。結合你的配置&#xff08…

樸素貝葉斯分類

一、樸素貝葉斯算法概述 樸素貝葉斯(Naive Bayes)是一種基于貝葉斯定理的簡單概率分類算法&#xff0c;它假設特征之間相互獨立&#xff08;"樸素"的含義&#xff09;。盡管這個假設在現實中很少成立&#xff0c;但該算法在許多實際應用中表現優異&#xff0c;特別是…

python協程:yield實現協程執行、生成器取值的三種方式

yield關鍵字執行流程 注意&#xff1a;yield關鍵字的調用次數如果超過了任務執行次數會報錯&#xff0c;提示stopiteration異常&#xff0c;例如 正常范圍內的任務執行 # 定義一個任務&#xff08;函數1&#xff09; def task1():for i in range(3):print(f----task1 i {i}-…

pdf刪除一頁 python實現(已驗證)

首先安裝庫 使用PyPDF2 首先&#xff0c;確保你已經安裝了PyPDF2。如果沒有安裝&#xff0c;可以通過pip安裝&#xff1a; pip install PyPDF2 然后運行 import PyPDF2def remove_page(input_pdf_path, output_pdf_path, page_number_to_remove):# 打開PDF文件with open(i…

2025.1版本PyCharam找不到已存在的conda虛擬環境

前言 創建Python項目指定conda虛擬環境是最常用的操作,我下載的2025.1版本PyCharam編譯器找不到我已經創建好的conda虛擬環境,解決方法如下 目錄 問題描述 問題解決 總結 問題描述 我使用2025.1版本PyCharam編譯器創建項目指定已經存在的虛擬環境出現如下情景 說是我沒有…

開機啟動項在哪里設置 實用步驟分享

電腦開機時&#xff0c;系統會自動運行一系列程序&#xff0c;其中包括必要的系統進程和用戶自行添加的啟動項。然而&#xff0c;過多的啟動項可能會導致開機速度變慢&#xff0c;影響系統性能。因此&#xff0c;合理管理開機啟動項&#xff0c;可以優化電腦的運行效率。電腦開…

LeetCode--39.組合總和

前引&#xff1a;明天就考最后一趟考試&#xff0c;最近考試周&#xff0c;我時時斷更&#xff0c;從明天開始&#xff0c;就會一直更新了&#xff0c;可以期待一下 解題思路&#xff1a; 1.獲取信息&#xff1a; 給定一個無重復的整數數組和一個目標值 從數組中選取任意數量的…

Visual Studio2022和C++opencv的配置保姆級教程

1.c桌面開發和windows平臺開發&#xff08;Visual Studio2022安裝時&#xff09; 2.下載OPenCV 3.系統屬性→添加環境變量→Path 4.VS2022配置opencv 5.項目→屬性→VC目錄中的包含目錄和庫目錄 5.項目→屬性→VC目錄中的包含目錄和庫目錄 包含 目錄添加&#xff1a; D:\…

使用Ansible的playbook安裝HTTP

實驗環境 安裝好ansible 一、準備測試服務&#xff08;192.168.10.41&#xff09; 1、安裝HTTP服務 dnf -y install httpd 2、啟動HTTP服務 systemctl start httpd 3、使用瀏覽器訪問 192.168.10.41 因為開啟了防火墻&#xff0c;所有無法訪問 4、開放防火墻的80端口 …

V少JS基礎班之第六彈

一、 前言 第六彈內容是閉包。 距離上次函數的發布已經過去了一個多月&#xff0c; 最近事情比較多&#xff0c;很少有時間去寫文章&#xff0c; 低質量還得保證所以本章放草稿箱一個月了&#xff0c;終于補齊了&#xff0c;其實還有很多細節要展開說明&#xff0c;想著拖太久…

【面板數據】全國高頻交易明細數據(2000-2024.7)

中國土地交易市場作為國家宏觀調控的重要組成部分&#xff0c;其通過市場機制&#xff0c;使土地使用權在不同主體間流轉&#xff0c;將土地資源配置給最具利用效率的部門或企業&#xff0c;提升土地利用率和經濟產出。中國土地高頻交易明細數據匯集了全國范圍內2000-2024年7月…

MongoDB 常用增刪改查方法及示例

MongoDB 的增刪改查&#xff08;CRUD&#xff09;操作是其核心功能&#xff0c;主要通過 mongo shell 或驅動&#xff08;如 Node.js、Python 等&#xff09;實現。以下是最常用操作的詳細說明及示例&#xff08;基于 mongo shell 語法&#xff09;。 ?一、插入操作&#xff…

moodle升級(4.5到5.0)

升級目標 由Moodle 4.5 (Build: 20241129) 升級到Moodle 5.0.1 (Build: 20250629) 參考教程&#xff1a;moodle升級&#xff08;詳細版&#xff09;-CSDN博客 操作平臺&#xff1a;寶塔 通過寶塔進行備份 備份文件 將/www/wwwroot/moodle 和/www/wwwroot/moodledata 復制…

基于Apache POI實現百度POI分類快速導入PostgreSQL數據庫實戰

## 引言:POI數據的價值與挑戰 POI(Point of Interest)數據作為地理信息系統的核心要素,在智慧城市、位置服務、商業分析等領域具有重要價值。百度POI數據包含了豐富的地點信息(如名稱、類別、坐標等),但如何高效處理這些數據并將其導入數據庫進行分析是開發者面臨的挑戰…

linux LAMP 3

[rootcode apache2]# bin/apachectl AH00558: httpd: Could not reliably determine the server’s fully qualified domain name, using fe80::20c:29ff:fe2a:708a. Set the ‘ServerName’ directive globally to suppress this message root192.168.235.5s password:┌─…

UI自動化-Selenium WebDriver

前言 Selenium WebDriver 是 Selenium 項目中最核心、最強大的組件&#xff0c;它是一個用于自動化控制網頁瀏覽器的開源 API&#xff08;應用程序編程接口&#xff09;。 簡單來說&#xff0c;Selenium WebDriver 就是一個允許你用編程語言&#xff08;如 Java、Python、C#、…

具身多模態大模型在感知與交互方面的綜述

引言在本學期方老師的《機器人與大模型》課上&#xff0c;我首次接觸到了關于具身智能的前沿知識&#xff0c;尤其作為課上交互組的成員&#xff0c;從表情識別到語音交互到機械狗的開發實踐進行了一些有意思的探索&#xff0c;使我在其中感受到了具身智能的巨大魅力和無限潛力…