微信搜索superit|邀请体验:大数据, 数据管理、OLAP分析与可视化平台 | 赞助作者:赞助作者

go-rpc的tcp调用

go aide_941 8℃

了解完了RPC技术的组成结构我们来看一下具体是如何实现客户端到服务端的调用的。实际上,如果我们想要在网络中的任意两台计算机上实现远程调用过程,要解决很多问题,比如:

  • 两台物理机器在网络中要建立稳定可靠的通信连接。
  • 两台服务器的通信协议的定义问题,即两台服务器上的程序如何识别对方的请求和返回结果。也就是说两台计算机必须都能够识别对方发来的信息,并且能够识别出其中的请求含义和返回含义,然后才能进行处理。这其实就是通信协议所要完成的工作。

让我们来看看RPC具体是如何解决这些问题的,RPC具体的调用步骤图如下:

在上述图中,通过1-10的步骤图解的形式,说明了RPC每一步的调用过程。具体描述为:

  • 1、客户端想要发起一个远程过程调用,首先通过调用本地客户端Stub程序的方式调用想要使用的功能方法名;
  • 2、客户端Stub程序接收到了客户端的功能调用请求,将客户端请求调用的方法名,携带的参数等信息做序列化操作,并打包成数据包。
  • 3、客户端Stub查找到远程服务器程序的IP地址,调用Socket通信协议,通过网络发送给服务端。
  • 4、服务端Stub程序接收到客户端发送的数据包信息,并通过约定好的协议将数据进行反序列化,得到请求的方法名和请求参数等信息。
  • 5、服务端Stub程序准备相关数据,调用本地Server对应的功能方法进行,并传入相应的参数,进行业务处理。
  • 6、服务端程序根据已有业务逻辑执行调用过程,待业务执行结束,将执行结果返回给服务端Stub程序。
  • 7、服务端Stub程序将程序调用结果按照约定的协议进行序列化,并通过网络发送回客户端Stub程序。
  • 8、客户端Stub程序接收到服务端Stub发送的返回数据,对数据进行反序列化操作,并将调用返回的数据传递给客户端请求发起者。
  • 9、客户端请求发起者得到调用结果,整个RPC调用过程结束。

 

定义RPC结构体和方法

// RPC方法必须要有两个参数和返回值error, // 第一个参数为请求结构体变量,指用于获取客户端提交的参数 // 第二个参数为响应结构体指针变量,用于返回,返回值error用于告知客户端错误信息
5|2Rpcserver
tree rpcserver 
rpcserver
├── data
│   └── data.go
├── go.mod
├── main.go
└── service
    └── rpc_client.go

go mod init rpcserver

 

rpcserver/data/data.go

package data

type CalculatorRequest struct {
    Left int
    Right int
}
// 定义算法服务
type CalculatorResponse struct {
    Result int
}

 

rpcserver/service/rpc_server

package service

import (
    "log"
    "rpcserver/data"
)

// 定义算法服务
type Calculator struct {

}

// 定义+方法
func (c *Calculator) Add(request *data.CalculatorRequest,response *data.CalculatorResponse) error {
    log.Printf("[+] call add method\n")
    response.Result = request.Left + request.Right
    return nil
}

 

rpcserver/main.go

package main

import (
    "log"
    "net"
    "net/rpc"
    "net/rpc/jsonrpc"
    "rpcserver/service"
)

func main()  {
    addr := ":8080"
    // 注册服务,未指定服务名称,默认结构体名
    //rpc.Register(&service.Calculator{})

    // 注册服务,指定服务名称
    rpc.RegisterName("Calc",&service.Calculator{})

    lister,err := net.Listen("tcp",addr)
    if err != nil{
        log.Fatal(err)
    }
    defer lister.Close()
    log.Printf("[+] listen on: %s",addr)
    for {
        conn,err := lister.Accept()
        if err != nil{
            log.Printf("[-]error client: %s",err.Error())
            continue
        }
        log.Printf("[+] client connected: %s",conn.RemoteAddr())

        // 使用例程启动jsonrpc处理客户端请求
        go jsonrpc.ServeConn(conn)
    }
}

 

5|3Rpcclient
tree rpcclient 
rpcclient
├── data
│   └── data.go
├── go.mod
└── main.go

1 directory, 3 files

 

rpcclient/data/data.go

package data

type CalculatorRequest struct {
    Left int
    Right int
}
// 定义算法服务
type CalculatorResponse struct {
    Result int
}

 

rpcclient/main.go

package main

import (
    "fmt"
    "log"
    "net/rpc/jsonrpc"
    "rpcclient/data"
)

func main()  {
    addr := "127.0.0.1:8080"
    conn,err := jsonrpc.Dial("tcp",addr)
    if err != nil{
        log.Fatal(err)
    }
    defer conn.Close()

    // 定义请求对象
    request := &data.CalculatorRequest{2,3}

    // 定义响应对象
    response := &data.CalculatorResponse{}

    // 调用远程方法
    //err = conn.Call("Calculator.Add",request,response)
    err = conn.Call("Calc.Add",request,response)

    // 获取结果
    fmt.Println(err,response.Result)
}

 

转载请注明:SuperIT » go-rpc的tcp调用

喜欢 (0)or分享 (0)