基础使用
序列化使用自带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)
}