基礎使用
序列化使用自帶gob協議
server
package mainimport ("net""net/rpc"
)// 定義一個handler結構體
type HelloService struct {
}// 定義handler方法,大小寫,參數,返回值都是固定的,否則無法注冊
func (receiver *HelloService) Hello(req string, rep *string) error {*rep = "hello " + reqreturn nil
}func main() {
//1. 實例化一個serverlisten, _ := net.Listen("tcp", ":8001")//2. 注冊handler_ = rpc.RegisterName("HelloService", &HelloService{})// 3. 啟動服務for {conn, _ := listen.Accept()go rpc.ServeConn(conn) //避免阻塞}}}
client
package mainimport ("fmt""net/rpc"
)func main() {//1. 建立連接client, _ := rpc.Dial("tcp", "localhost:8001")var data *string = new(string)err := client.Call("HelloService.Hello", "matthew", data)if err != nil {fmt.Println("調用失敗")}fmt.Println("success: ", *data)
}
注:兩個文件需要在不同包下面
使用json序列化
server
package mainimport ("net""net/rpc""net/rpc/jsonrpc"
)// 定義一個handler結構體
type HelloService struct {
}// 定義handler方法,大小寫,參數,返回值都是固定的,否則無法注冊
func (receiver *HelloService) Hello(req string, rep *string) error {*rep = "hello " + reqreturn nil
}func main() {jsonserver()
}/*
*
go 默認的序列化反序列化協議是gob
*/
func gobserver() {//1. 實例化一個serverlisten, _ := net.Listen("tcp", ":8001")//2. 注冊handler_ = rpc.RegisterName("HelloService", &HelloService{})// 3. 啟動服務for {conn, _ := listen.Accept()go rpc.ServeConn(conn) //避免阻塞}
}/*
*
使用json來序列化和反序列化
*/
func jsonserver() {//1. 實例化一個serverlisten, _ := net.Listen("tcp", ":8001")//2. 注冊handler_ = rpc.RegisterName("HelloService", &HelloService{})// 3. 啟動服務for {conn, _ := listen.Accept()go rpc.ServeCodec(jsonrpc.NewServerCodec(conn)) //避免阻塞}}
client
package mainimport ("fmt""net""net/rpc""net/rpc/jsonrpc"
)func main() {jsonRpcClient()
}func jsonRpcClient() {//1. 建立連接client, _ := net.Dial("tcp", "localhost:8001")var data *string = new(string)codeclient := rpc.NewClientWithCodec(jsonrpc.NewClientCodec(client))err := codeclient.Call("HelloService.Hello", "matthew", data)if err != nil {fmt.Println("調用失敗")}fmt.Println("success: ", *data)
}
構建一個規范的rpc項目框架
如果想構建生產環境使用的rpc服務,不但要能實現業務,還要有良好的架構設計
- client 客戶端
- client_proxy/stub 客戶端代理封裝
- handler 句柄封裝
- server 服務端
- server_proxy/stub 服務端函數封裝
- handler 句柄封裝
句柄相當于服務端和客戶端互相通信的通道,告訴雙方可以提供的方法
client.go
package mainimport ("fmt""learngo/chw01/nicerpc/client_stub_proxy"
)func main() {stub := client_stub_proxy.NewHelloServiceStub("tcp", "localhost:9001")res := new(string)stub.Hello("matthew", res)fmt.Println(*res)
}
client_stub_proxy
package client_stub_proxyimport ("fmt""learngo/chw01/nicerpc/handler""net/rpc"
)/**
存放客戶端proxy或者stub,封裝已經注冊的服務和方法列表,用于客戶端快速調用專注業務
*/type HelloServiceStub struct {*rpc.Client
}// 構造函數返回一個,服務代理并攜帶客戶端
func NewHelloServiceStub(protcol, address string) HelloServiceStub {client, err := rpc.Dial(protcol, address)if err != nil {fmt.Println(err)panic("rpc client start panic")}return HelloServiceStub{client}
}/*
*
封裝了HelloService服務的Hello方法
*/
func (cs *HelloServiceStub) Hello(req string, reply *string) error {err := cs.Call(handler.HelloServiceName+".Hello", req, reply)return err
}
hander.go
package handler/*
*
定義所有的handler名稱,可以被client,server兩端引入對齊
*/
const HelloServiceName = "hander/HelloService"// 定義一個handler結構體
type HelloService struct {
}// 定義handler方法,大小寫,參數,返回值都是固定的,否則無法注冊
func (receiver *HelloService) Hello(req string, rep *string) error {*rep = "hello " + reqreturn nil
}
server.go
package mainimport ("fmt""learngo/chw01/nicerpc/handler""learngo/chw01/nicerpc/server_proxy_stub""net""net/rpc"
)func main() {var address string = ":9001"//實例化一個serverlisten, err := net.Listen("tcp", address)if err != nil {panic("server start error " + address)}fmt.Println("server start success on address: ", address)//注冊方法server_proxy_stub.RegisterHelloServicer(&handler.HelloService{})//啟動服務for {conn, err := listen.Accept()if err != nil {fmt.Println("connect error", err)}rpc.ServeConn(conn)}
}
server_proxy_stub
package server_proxy_stubimport ("learngo/chw01/nicerpc/handler""net/rpc"
)/**
封裝所有server的方法(業務邏輯)
*//*
*
多態:頂一個HelloServie的接口,凡是實現了該接口方法的struct都繼承了接口
*/
type HelloServicer interface {Hello(req string, reply *string) error
}// 當前的方法只能用來HelloService一種結構體,我們關注的不是結構體而是結構體的方法
func RegisterHelloService(srv *handler.HelloService) error {return rpc.RegisterName(handler.HelloServiceName, srv)
}// 對比上面的注冊,通過接口來注冊更加靈活。所有實現了HelloServicer接口的struct都可以直接使用該方法
func RegisterHelloServicer(srv *handler.HelloService) error {return rpc.RegisterName(handler.HelloServiceName, srv)
}