基础使用
序列化使用自带gob协议
server
package main
import (
"net"
"net/rpc"
)
// 定义一个handler结构体
type HelloService struct {
}
// 定义handler方法,大小写,参数,返回值都是固定的,否则无法注册
func (receiver *HelloService) Hello(req string, rep *string) error {
*rep = "hello " + req
return nil
}
func main() {
//1. 实例化一个server
listen, _ := net.Listen("tcp", ":8001")
//2. 注册handler
_ = rpc.RegisterName("HelloService", &HelloService{})
// 3. 启动服务
for {
conn, _ := listen.Accept()
go rpc.ServeConn(conn) //避免阻塞
}
}
}
client
package main
import (
"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 main
import (
"net"
"net/rpc"
"net/rpc/jsonrpc"
)
// 定义一个handler结构体
type HelloService struct {
}
// 定义handler方法,大小写,参数,返回值都是固定的,否则无法注册
func (receiver *HelloService) Hello(req string, rep *string) error {
*rep = "hello " + req
return nil
}
func main() {
jsonserver()
}
/*
*
go 默认的序列化反序列化协议是gob
*/
func gobserver() {
//1. 实例化一个server
listen, _ := net.Listen("tcp", ":8001")
//2. 注册handler
_ = rpc.RegisterName("HelloService", &HelloService{})
// 3. 启动服务
for {
conn, _ := listen.Accept()
go rpc.ServeConn(conn) //避免阻塞
}
}
/*
*
使用json来序列化和反序列化
*/
func jsonserver() {
//1. 实例化一个server
listen, _ := net.Listen("tcp", ":8001")
//2. 注册handler
_ = rpc.RegisterName("HelloService", &HelloService{})
// 3. 启动服务
for {
conn, _ := listen.Accept()
go rpc.ServeCodec(jsonrpc.NewServerCodec(conn)) //避免阻塞
}
}
client
package main
import (
"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 main
import (
"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_proxy
import (
"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 " + req
return nil
}
server.go
package main
import (
"fmt"
"learngo/chw01/nicerpc/handler"
"learngo/chw01/nicerpc/server_proxy_stub"
"net"
"net/rpc"
)
func main() {
var address string = ":9001"
//实例化一个server
listen, 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_stub
import (
"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)
}