rpc/session.go
package rpc
import (
"encoding/binary"
"io"
"net"
)// 編寫資料會話中讀寫
// 會話連線的結構體
type session struct
// 建立新連線
func newsession(conn net.conn) *session
}// 向連線中寫資料
func (s session) write(data byte) error
return nil
}// 從連線中讀資料
func (s session) read() (byte, error)
// 讀取資料長度
datalen := binary.bigendian.uint32(header)
// 按照資料長度去讀取資料
data := make(byte, datalen)
_, err = io.readfull(s.conn, data)
if err != nil
return data, nil
}
rpc/session_test.go
package rpc
import (
"fmt"
"net"
"sync"
"testing"
)func testsession_readwrite(t *testing.t)
// 協程,乙個讀,乙個寫
wg.add(2)
// 寫資料協程
go func()
conn,_ := lis.accept()
s := session
// 寫資料
err = s.write(byte(my_data))
if err != nil
}()// 讀資料協程
go func()
s := session
// 讀資料
data, err := s.read()
if err != nil
if string(data) != my_data
fmt.println(string(data))
}()wg.wait()
}
rpc/codec.go
package rpc
import (
"bytes"
"encoding/gob"
)// 定義資料格式和編譯碼
type rpcdata struct
}// 編碼
func encode(data rpcdata) (byte, error)
return buf.bytes(), nil
}// 解碼
func decode(b byte) (rpcdata, error)
return data, nil
}
rpc/server.go
package rpc
import (
"fmt"
"net"
"reflect"
)// 宣告服務端
type server struct
// 建立服務端物件
func newserver(addr string) *server
}// 服務端繫結註冊方法
// 將函式名與函式真正實現對應起來
// 第乙個引數為函式名, 第二個傳入真正的函式
func (s *server) register(rpcname string, f inte***ce{})
// map中沒有值,則將對映新增進map,便於呼叫
fval := reflect.valueof(f)
s.funcs[rpcname] = fval
}// 服務端等待呼叫
func (s *server) run()
for
// 建立會話
srvsession := newsession(conn)
// rpc 讀取資料
b, err := srvsession.read()
if err != nil
// 對資料解碼
rpcdata, err := decode(b)
if err != nil
// 根據讀取到的資料的name,得到呼叫的函式名
f, ok := s.funcs[rpcdata.name]
if !ok
// 解析遍歷客戶端出來的引數, 放到乙個陣列中
inargs := make(reflect.value, 0, len(rpcdata.args))
for _, arg := range rpcdata.args
// 反射呼叫方法,傳入引數
out := f.call(inargs)
// 解析遍歷執行結果,放到乙個陣列中
outargs := make(inte***ce{}, 0, len(out))
for _, o := range out
// 包裝資料返回給客戶端
resprpcdata := rpcdata
// 編碼
respbytes, err := encode(resprpcdata)
if err != nil
// 使用rpc寫出資料
err = srvsession.write(respbytes)
if err != nil
}}
rpc/client.go
package rpc
import (
"net"
"reflect"
)// 宣告客戶端
type client struct
// 建立客戶端物件
func newclient(conn net.conn) *client
}// 實現通用的rpc客戶端
// 繫結rpc使用的方法
// 傳入訪問的函式名
// 函式具體實現在server端, client只有函式原型
// 使用makefunc() 完成原型到函式的呼叫
// fptr指向函式原型
func (c *client) callrpc(rpcname string, fptr inte***ce{}) , 0, len(args))
for _, arg := range args
// 建立連線
clisession := newsession(c.conn)
// 編碼資料
reqrpc := rpcdata
b, err := encode(reqrpc)
if err != nil
// 寫出資料
err = clisession.write(b)
if err != nil
// 讀響應資料
respbytes, err := clisession.read()
if err != nil
// 解碼資料
resprpc, err := decode(respbytes)
if err != nil
// 處理服務端返回的資料
outargs := make(reflect.value, 0, len(resprpc.args))
for i, arg := range resprpc.args
} return outargs
} v := reflect.makefunc(fn.type(), f)
// 為函式fptr賦值
fn.set(v)
}
rpc/******_tpc_test.go
package rpc
import (
"encoding/gob"
"fmt"
"net"
"testing"
)// 使用者查詢
// 用於測試的結構體
type user struct
// 用於測試查詢使用者的方法
func queryuser(uid int) (user, error)
user[1] = user
user[2] = user
// 模擬查詢使用者
if u, ok := user[uid]; ok
return user{}, fmt.errorf("id %d not in user db", uid)
}func testrpc(t *testing.t) )
addr := "127.0.0.1:8080"
// 建立服務端
srv := newserver(addr)
// 將方法註冊到服務端
srv.register("queryuser", queryuser)
// 服務端等待呼叫
go srv.run()
// 客戶端獲取連線
conn , err := net.dial("tcp", addr)
if err != nil
// 建立客戶端
cli := newclient(conn)
// 宣告函式原型
var query func(int) (user error)
cli.callrpc("queryuser", &query)
// 得到查詢結果
u, err := query(1)
if err != nil
fmt.println(u)
}
golang 實現rpc遠端呼叫,開箱即用
rpc 遠端方法呼叫 優點 提公升系統可擴充套件性,提公升可維護性,和吃持續交付能力 實現系統的高可用等 缺點 rpc受限於網路 實現乙個rcp遠端呼叫關鍵在於帶裡層的實現 還是貼 吧 將client 位址賦值 func new addr string client jrp實現 func c cli...
乙個自己實現的rpc框架
整合spring配置 檔案頭部加入rpc的namespace 服務端 服務端標籤 其中,protocol標籤屬性分析 port指定服務端繫結的埠 service標籤屬性分析 inte ce指定介面的完整類名,ref指定實現類的id,且該實現類必須放入spring容器中service標籤也可 以用註解...
golang官方rpc包的使用
rpc remote rrocedure call,遠端過程呼叫 是乙個計算機通訊協議。rpc協議假定某些傳輸協議的存在,如tcp和udp,為通訊程式之間攜帶資訊資料。在osi網路通訊模型中,rpc跨越了傳輸層和應用層。rpc採用c s模式,請求程式就是乙個客戶機,而服務提供程式就是乙個伺服器。首先...