go语言rpc初体验
package main
import (
"net"
"net/rpc"
)
// 注册一个接口进来
type HelloService struct {
}
func (s *HelloService) Hello(request string, replay *string) error {
//返回值是通过修改replay的值
*replay = "hello " + request
return nil
}
// go语言内置rpc包
func main() {
//注册名字 实例化一个sever
listener, _ := net.Listen("tcp", ":1234") //监听
//注册处理handle
_ = rpc.RegisterName("HelloService", &HelloService{})
//启动服务
conn, _ := listener.Accept() //当一个新的连接进来的时候,生成套接字
rpc.ServeConn(conn) //调用一次连接就结束
}
package main
import (
"fmt"
"net/rpc"
)
func main() {
//建立连接
client, err := rpc.Dial("tcp", "localhost:1234")
if err != nil {
panic("连接失败")
}
//var replay *string //reading body gob:DecodeValue of unassignable value 传递的nil 没有地址
//var replay *string = new(string) //new 是分配一个空间 第一种方法
var replay string //string有默认值 第二种方法
err = client.Call("HelloService.Hello", "bobby", &replay)
if err != nil {
panic("调用失败")
}
fmt.Println(replay)
}
一连串的代码大部分都是net的包好像和rpc没有关系?
答:不行,rpc调用中有几个问题需要解决:1:call id,2序列化和反序列化
可以跨语言调用呢? 1. go语言的rpc的序列化协议是什么(Gob) 2. 能否替换成常见的序列化
package main
import (
"net"
"net/rpc"
"net/rpc/jsonrpc"
)
// 注册一个接口进来
type HelloService struct {
}
func (s *HelloService) Hello(request string, replay *string) error {
//返回值是通过修改replay的值
*replay = "hello " + request
return nil
}
// go语言内置rpc包
func main() {
//注册名字 实例化一个sever
listener, _ := net.Listen("tcp", ":1234") //监听
//注册处理handle
_ = rpc.RegisterName("HelloService", &HelloService{})
for { //如果使用死循环,有一个弊端如果同时多个客户端处理,需要一个一个处理,所以需要加协程
//启动服务
conn, _ := listener.Accept() //当一个新的连接进来的时候,生成套接字
//但使用指定的编解码器,以编码请求主体和解码回复主体。
go rpc.ServeCodec(jsonrpc.NewServerCodec(conn)) //后面传来的东西,然后支持的编码都可以
}
}
如果使用死循环,有一个弊端如果同时多个客户端处理,需要一个一个处理,所以需要加协程
package main
import (
"fmt"
"net"
"net/rpc"
"net/rpc/jsonrpc"
)
func main() {
//建立连接
conn, err := net.Dial("tcp", "localhost:1234")
if err != nil {
panic("连接失败")
}
//jsonrpc.NewClientCodec(conn) 包装连接变成一个新的conn
client := rpc.NewClientWithCodec(jsonrpc.NewClientCodec(conn)) //因为他是json的格式
var replay string //string有默认值 第二种方法
err = client.Call("HelloService.Hello", "bobby", &replay) //发送数据格式为json
if err != nil {
panic("调用失败")
}
fmt.Println(replay)
}
client.go
package main
import (
"awesomeProject123/new_helloworld/client_proxy"
"fmt"
)
func main() {
//建立连接
//1 只想写业务逻辑,不想关注每个函数的名称
//客户端部分
client := client_proxy.NewHelloServiceClient("tcp", "localhost:1234")
//jsonrpc.NewClientCodec(conn) 包装连接变成一个新的conn
var replay string //string有默认值 第二种方法
err := client.Hello("bobby", &replay) //发送数据格式为json
if err != nil {
panic("调用失败")
}
fmt.Println(replay)
}
client_proxy.go
package client_proxy
import (
"awesomeProject123/new_helloworld/handler"
"net/rpc"
)
type HelloServerStub struct {
*rpc.Client
}
// NewHelloServiceClient 在go语言中没有类、对象,就意味着没有初始化方法
func NewHelloServiceClient(protol, address string) HelloServerStub {
conn, err := rpc.Dial(protol, address)
if err != nil {
panic("connect error!")
}
return HelloServerStub{conn}
}
func (c *HelloServerStub) Hello(request string, replay *string) error {
err := c.Call(handler.HelloServiceName+".Hello", request, replay)
if err != nil {
return err
}
return nil
}
server.go
package main
import (
"awesomeProject123/new_helloworld/handler"
"awesomeProject123/new_helloworld/server_proxy"
"net"
"net/rpc"
)
// go语言内置rpc包
func main() {
//注册名字 实例化一个sever
listener, _ := net.Listen("tcp", ":1234") //监听
//注册处理handler
_ = server_proxy.RegisterHelloService(&handler.NewHelloService{})
//_ = rpc.RegisterName(handler.HelloServiceName, &handler.HelloService{})
for {
//启动服务
conn, _ := listener.Accept() //当一个新的连接进来的时候,生成套接字
rpc.ServeConn(conn) //调用一次连接就结束
}
}
server_proxy.go
package server_proxy
import (
"awesomeProject123/new_helloworld/handler"
"net/rpc"
)
type HelloServicer interface {
Hello(request string, replay *string) error
}
// RegisterHelloService 解耦 --我们关心的是函数,鸭子类型
func RegisterHelloService(srv HelloServicer) error {
return rpc.RegisterName(handler.HelloServiceName, srv)
}
https://github.com/protocolbuffers/protobuf/releases