golang 实现RPC的几种方式

golang 实现RPC的几种方式



什么是RPC


远程过程调用(Remote Procedure Call,缩写为 RPC)是一个计算机通信协议。 该协议允许运行于一台计算机的程序调用另一台计算机的子程序,而程序员无需额外地为这个交互作用编程。 如果涉及的软件采用面向对象编程,那么远程过程调用亦可称作远程调用或远程方法调用。

用通俗易懂的语言描述就是:RPC允许跨机器、跨语言调用计算机程序方法。打个比方,我用go语言写了个获取用户信息的方法getUserInfo,并把go程序部署在阿里云服务器上面,现在我有一个部署在腾讯云上面的php项目,需要调用golang的getUserInfo方法获取用户信息,php跨机器调用go方法的过程就是RPC调用。


golang中使用RPC


golang官方的net/rpc库使用encoding/gob进行编解码,支持tcp或http数据传输方式,不支持跨语言调用。
官方还提供了net/rpc/jsonrpc库实现RPC,支持跨语言调用,但是不支持http。


RPC格式的函数


Go RPC的函数只有符合下面的条件才能被远程访问,不然会被忽略,详细的要求如下:

  • 函数必须是导出的(首字母大写)
  • 必须有两个导出类型的参数
  • 第一个参数是接收的参数,第二个参数是返回给客- 户端的参数,第二个参数必须是指针类型的
  • 函数还要有一个返回值error

比如:

func (t *T) MethodName(argType T1, replyType *T2) error

net/rpc库


下面的例子演示一下如何使用golang官方的net/rpc库实现RPC方法,使用http作为RPC的载体,通过net/http包监听客户端连接请求。


server.go

package main

import (
	"errors"
	"fmt"
	"log"
	"net"
	"net/http"
	"net/rpc"
)

// Arith 算数运算结构体
type Arith struct {
}

// ArithRequest 算数运算请求结构体
type ArithRequest struct {
	A int
	B int
}

// ArithResponse 算数运算响应结构体
type ArithResponse struct {
	// 乘积
	Pro int
	// 商
	Quo int
	// 余数
	Rem int
}

// Multiply 乘法运算方法
func (a *Arith) Multiply(req ArithRequest, res *ArithResponse) error {
	res.Pro = req.A * req.B
	return nil
}

// Divide 除法运算方法
func (a *Arith) Divide(req ArithRequest, res *ArithResponse) error {
	if req.B == 0 {
		return errors.New("divide by zero")
	}
	res.Quo = req.A / req.B
	res.Rem = req.A % req.B
	return nil
}

func main() {
	// 注册rpc服务
	rpc.Register(new(Arith))
	// 采用http协议作为rpc载体
	rpc.HandleHTTP()

	// 服务器创建监听端口
	listen, err := net.Listen("tcp", "127.0.0.1:8095")
	if err != nil {
		log.Fatalln("fatal error: ", err)
	}

	fmt.Println("start connection")
	// 处理请求
	http.Serve(listen, nil)
}

client.go

package main

import (
	"fmt"
	"log"
	"net/rpc"
)

// ArithRequest 算数运算请求结构体
type ArithRequest struct {
	A int
	B int
}

// ArithResponse 算数运算响应结构体
type ArithResponse struct {
	// 乘积
	Pro int
	// 商
	Quo int
	// 余数
	Rem int
}

func main() {
	// 请求服务器连接
	conn, err := rpc.DialHTTP("tcp", "127.0.0.1:8095")
	if err != nil {
		log.Fatalln("dailing error: ", err)
	}

	req := ArithRequest{9, 2}
	var res ArithResponse

	// Call函数远程调用服务器的函数
	err = conn.Call("Arith.Multiply", req, &res) // 乘法运算
	if err != nil {
		log.Fatalln("arith error: ", err)
	}
	fmt.Printf("%d * %d = %d\n", req.A, req.B, res.Pro)

	err = conn.Call("Arith.Divide", req, &res)
	if err != nil {
		log.Fatalln("arith error: ", err)
	}
	fmt.Printf("%d / %d, quo is %d, rem is %d\n", req.A, req.B, res.Quo, res.Rem)
}

net/rpc/jsonrpc


下面的例子我们演示一下使用net/rpc/jsonrpc库实现RPC方法,此方式实现的RPC方法支持跨语言调用。


server.go

package main

import (
	"errors"
	"fmt"
	"log"
	"net"
	"net/rpc"
	"net/rpc/jsonrpc"
)

// Arith 算数运算结构体
type Arith struct {
}

// ArithRequest 算数运算请求结构体
type ArithRequest struct {
	A int
	B int
}

// ArithResponse 算数运算响应结构体
type ArithResponse struct {
	Pro int // 乘积
	Quo int // 商
	Rem int // 余数
}

// Multiply 乘法运算方法
func (a *Arith) Multiply(req ArithRequest, res *ArithResponse) error {
	res.Pro = req.A * req.B
	return nil
}

// Divide 除法运算方法
func (a *Arith) Divide(req ArithRequest, res *ArithResponse) error {
	if req.B == 0 {
		return errors.New("divide by zero")
	}
	res.Quo = req.A / req.B
	res.Rem = req.A % req.B
	return nil
}

func main() {
	rpc.Register(new(Arith)) // 注册rpc服务

	lis, err := net.Listen("tcp", "127.0.0.1:8096")
	if err != nil {
		log.Fatalln("fatal error: ", err)
	}

	fmt.Println("start connection")

	for {
		conn, err := lis.Accept() // 接收客户端连接请求
		if err != nil {
			continue
		}

		go func(conn net.Conn) { // 并发处理客户端请求
			fmt.Println("new client in coming")
			jsonrpc.ServeConn(conn)
		}(conn)
	}
}

client.go

package main

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

// ArithRequest 算数运算请求结构体
type ArithRequest struct {
	A int
	B int
}

// ArithResponse 算数运算响应结构体
type ArithResponse struct {
	Pro int // 乘积
	Quo int // 商
	Rem int // 余数
}

func main() {
	conn, err := jsonrpc.Dial("tcp", "127.0.0.1:8096")
	if err != nil {
		log.Fatalln("dailing error: ", err)
	}

	req := ArithRequest{9, 2}
	var res ArithResponse

	err = conn.Call("Arith.Multiply", req, &res) // 乘法运算
	if err != nil {
		log.Fatalln("arith error: ", err)
	}
	fmt.Printf("%d * %d = %d\n", req.A, req.B, res.Pro)

	err = conn.Call("Arith.Divide", req, &res)
	if err != nil {
		log.Fatalln("arith error: ", err)
	}
	fmt.Printf("%d / %d, quo is %d, rem is %d\n", req.A, req.B, res.Quo, res.Rem)
}

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值