網(wǎng)站首頁 編程語言 正文
本篇文章旨在通過學(xué)習(xí)rpc包和github上的一個(gè)rpc小項(xiàng)目,熟悉和學(xué)習(xí)golang中各個(gè)包的使用
工作流程
通過閱讀官方文檔,了解了rpc的基本工作模式
- 第一步,建立一個(gè)用于遠(yuǎn)程調(diào)用的包,存放僅供遠(yuǎn)程調(diào)用使用的方法和類型-
- 第二步,實(shí)例化包的對(duì)象,并在rpc中注冊(cè)該包,以便之后的調(diào)用
- 第三步,建立一個(gè)服務(wù)端,接收客戶端的請(qǐng)求,使用編碼器解析請(qǐng)求后,根據(jù)請(qǐng)求中的方法和參數(shù),調(diào)用第二步注冊(cè)的實(shí)例的方法,然后使用編碼器把返回值加密后,返回給客戶端
- 第四步,建立一個(gè)客戶端,連接服務(wù)端,成功后,向連接發(fā)送使用編碼器加密后的數(shù)據(jù),然后等待服務(wù)端響應(yīng)(同步或異步)。響應(yīng)成功后,使用編碼器解析服務(wù)端返回的數(shù)據(jù)
第三步第四步中,多次用到的編碼器,是rpc包的關(guān)鍵。默認(rèn)情況下,rpc包使用的是go特有的encoding/gob包進(jìn)行數(shù)據(jù)的編碼和解碼。但是當(dāng)我們服務(wù)端和客戶端使用了不同的語言時(shí),若加密方法無法兼容,就會(huì)出現(xiàn)問題,所以rpc包支持自定義編碼器。
工作模式
go的rpc除了支持常規(guī)常規(guī)的tpc+端口的遠(yuǎn)程調(diào)用方式,也支持基于http的遠(yuǎn)程調(diào)用實(shí)現(xiàn)。但是,我都用rpc了,還用個(gè)毛的http形式。不過作為一種形式,我們出于禮貌的簡(jiǎn)單了解下。
http模式
官方文檔的例子,就是使用的http形式的rpc,如下
服務(wù)端
//實(shí)例化rpc遠(yuǎn)程調(diào)用的方法所屬對(duì)象 arith := new(Arith) //注冊(cè)對(duì)象 rpc.Register(arith) //把rpc監(jiān)聽 對(duì)應(yīng)到http處理器。即指定http請(qǐng)求addr+port時(shí),調(diào)用的方法 rpc.HandleHTTP() //獲取監(jiān)聽地址 l, e := net.Listen("tcp", ":1234") if e != nil { log.Fatal("listen error:", e) } //開啟一個(gè)go程,持續(xù)處理監(jiān)聽數(shù)據(jù) go http.Serve(l, nil)
客戶端
//連接 rpc的http服務(wù)端 client, err := rpc.DialHTTP("tcp", serverAddress + ":1234") if err != nil { log.Fatal("dialing:", err) } //同步調(diào)用 // 實(shí)例化rpc傳入?yún)?shù) args := &server.Args{7,8} //聲明rpc 回復(fù)參數(shù)。傳入和回復(fù)參數(shù),必須與調(diào)用方法中的參入類型一致 var reply int //調(diào)用rpc注冊(cè)的方法 err = client.Call("Arith.Multiply", args, &reply) if err != nil { log.Fatal("arith error:", err) } fmt.Printf("Arith: %d*%d=%d", args.A, args.B, reply) //或:異步調(diào)用 // Asynchronous call quotient := new(Quotient) divCall := client.Go("Arith.Divide", args, quotient, nil) replyCall := <-divCall.Done // will be equal to divCall
從上面的代碼可以看到,請(qǐng)求服務(wù)端時(shí),需要客戶端在服務(wù)器發(fā)起http請(qǐng)求,如果直接在瀏覽器或者其他工具發(fā)起http請(qǐng)求則報(bào)錯(cuò),因?yàn)榇藭r(shí)rpc.HandleHTTP方法指定的默認(rèn)方法,使用的是默認(rèn)gob編碼器,且只接收connect類型的請(qǐng)求。查看源代碼,如下
直接發(fā)起的http請(qǐng)求,無法使用go獨(dú)有的gob包編碼,rpc服務(wù)端也就無法使用默認(rèn)gob包解碼。
所以我們要寫一個(gè)新的方法代替rpc.HandleHTTp,把http請(qǐng)求綁定到一個(gè)使用其他解碼器的方法上,如下
//注冊(cè)路由和對(duì)應(yīng)的handler http.HandleFunc("/json", func(rw http.ResponseWriter, r *http.Request) { //聲明一個(gè)客戶端連接對(duì)象 var conn io.ReadWriteCloser = struct { io.Writer io.ReadCloser }{ ReadCloser: r.Body, Writer: rw, } //也可以使用如下方法獲取接管客戶端連接,http處理器不在管理該鏈接,使用完畢后需要自行關(guān)閉鏈接 // conn, _, err := rw.(http.Hijacker).Hijack() // if err != nil { // log.Print("rpc hijacking fail: ", err.Error()) // return // } io.WriteString(conn, "HTTP/1.0 rpc-ok\n\n") //server.ServeConn(conn) //rpc.ServeRequest,指定編碼器,以同步的方式處理請(qǐng)求一次,編碼器內(nèi)不關(guān)閉鏈接,由http服務(wù)處理。適用于http形式的請(qǐng)求,因?yàn)閔ttp是無狀態(tài)的,每次請(qǐng)求都是一個(gè)新的鏈接 //rpc.ServeCodec,指定編碼器,for循環(huán)接收客戶端鏈接的消息,每次消息處理開啟一個(gè)go程,相當(dāng)于異步處理,直到客戶端關(guān)閉或解碼錯(cuò)誤,跳出循環(huán),關(guān)閉連接。適用客戶端,一次連接多次發(fā)送數(shù)據(jù) rpc.ServeRequest(jsonrpc.NewServerCodec(conn))}) //監(jiān)聽http請(qǐng)求 http.ListenAndServe("127.0.0.1:1234", nil)
上面代碼中的==jsonrpc.NewServerCodec(conn)==是一個(gè)官方定義好的json格式的編碼器。我們?cè)趆ttp中發(fā)送數(shù)據(jù)時(shí),只要使用json格式,就能被服務(wù)端解析
執(zhí)行g(shù)o文件,在post模擬url請(qǐng)求,如下
服務(wù)器模式
服務(wù)端
//聲明和注冊(cè)rpc方法對(duì)象 //獲取監(jiān)聽信息 lis, err := net.Listen("tcp", ":8082") if err != nil { log.Fatal(err) } //循環(huán)讀取監(jiān)聽到的數(shù)據(jù) for { //獲取一個(gè)客戶端連接 conn, err := lis.Accept() if err != nil { continue } //開啟一個(gè)go程序,使用自定義編碼器處理當(dāng)前獲取連接 go s.Server.ServeCodec(jsonrpc.NewServerCodec(conn)) }
客戶端
//連接服務(wù)端 conn, err := net.Dial("tcp", ":8082")if err != nil { log.Fatal(err) } defer conn.Close() //使用json編碼器新建客戶端 client := &Client{rpc.NewClientWithCodec(jsonrpc.NewServerCodec(conn))} //聲明rpc方法中傳入和輸出的參數(shù) resq := message.ArithRequest{A: 20, B: 5} resp := message.ArithResponse{} //調(diào)用rpc方法 err = client.Call("ArithService.Add", &resq, &resp) log.Printf("Arith.Add(%v, %v): %v ,Error: %v", resq.A, resq.B, resp.C, err)
原文鏈接:https://blog.csdn.net/u012830303/article/details/126370703
相關(guān)推薦
- 2022-03-24 聊一聊redis奇葩數(shù)據(jù)類型與集群知識(shí)_Redis
- 2022-06-06 Kafka在Zookeeper中存儲(chǔ)的信息有哪些?
- 2022-12-04 在python?list中篩選包含字符的字段方式_python
- 2022-10-08 Golang?Web?框架Iris安裝部署_Golang
- 2022-09-04 docker部署可執(zhí)行jar包的思路與完整步驟_docker
- 2023-02-25 C++11如何引入的尾置返回類型_C 語言
- 2022-05-11 多線程編程(下):線程同步&通信
- 2022-03-14 springboot yml對(duì)于list列表配置方式
- 最近更新
-
- window11 系統(tǒng)安裝 yarn
- 超詳細(xì)win安裝深度學(xué)習(xí)環(huán)境2025年最新版(
- Linux 中運(yùn)行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲(chǔ)小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎(chǔ)操作-- 運(yùn)算符,流程控制 Flo
- 1. Int 和Integer 的區(qū)別,Jav
- spring @retryable不生效的一種
- Spring Security之認(rèn)證信息的處理
- Spring Security之認(rèn)證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權(quán)
- redisson分布式鎖中waittime的設(shè)
- maven:解決release錯(cuò)誤:Artif
- restTemplate使用總結(jié)
- Spring Security之安全異常處理
- MybatisPlus優(yōu)雅實(shí)現(xiàn)加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務(wù)發(fā)現(xiàn)-Nac
- Spring Security之基于HttpR
- Redis 底層數(shù)據(jù)結(jié)構(gòu)-簡(jiǎn)單動(dòng)態(tài)字符串(SD
- arthas操作spring被代理目標(biāo)對(duì)象命令
- Spring中的單例模式應(yīng)用詳解
- 聊聊消息隊(duì)列,發(fā)送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠(yuǎn)程分支