go语言中的RPC只能在go语言的程序中调用。如果使用其他语言编写的客户端,就要用到jsonRPC了。jsonRPC可以被跨语言调用。
这里我们采用一个计算乘法和除法求商取余数的例子来实践。
首先,服务端server.go声明算术运算结构体和接收的参数结构体及返回客户端参数结构体。
// 声明算术运算结构体
type Arith struct {
}
// 声明接收的参数结构体
type ArithRequest struct {
A,B int
}
// 声明返回客户端参数结构体
type ArithResponse struct {
// 乘积
Pro int
// 商
Quo int
// 余数
Rem int
}
计算乘法的函数
// 乘法运算
func (a *Arith) Multiply(req ArithRequest, res *ArithResponse) error {
res.Pro = req.A * req.B
return nil
}
计算商和余数的时候,要注意一下需要考虑除数不能为0的情况。当除数为0的时候,就要用到返回结果error了。
// 商和余数运算
func (a *Arith) Divide(req ArithRequest, res *ArithResponse) error {
if req.B == 0 {
return errors.New("除数不能为0")
}
// 计算 商
res.Quo = req.A / req.B
// 计算 余数
res.Rem = req.A % req.B
return nil
}
在主函数中,首先要注册服务
// 注册服务
arith := new(Arith)
rpc.Register(arith)
然后要监听服务。这里我们采用tcp协议。需要确定服务采用的通讯端口,这里我们采用8081端口。
// 监听服务
lis, err := net.Listen("tcp", "127.0.0.1:8081")
if err != nil {
log.Fatal(err)
}
之后就是持续监听等待客户端访问,每当有访问发生就产生一个goroutine。如果一个客户端的连接发生错误,要忽略这次错误,进入下一个循环等待阶段。这里使用for循环实现。
for true {
conn, err := lis.Accept()
if err != nil {
continue
}
go func(conn net.Conn) {
fmt.Println("new Client")
jsonrpc.ServeConn(conn)
}(conn)
}
也许你对携程(goroutine)这里的代码有些困惑。那么对比一下下面的代码。
go xxx(conn)
...
func xxx(conn net.Conn) {
fmt.Println("new Client")
jsonrpc.ServeConn(conn)
}
服务端的代码就是这些了。
下面是客户端client.go的代码。
首先声明一下接收参数和返回客户端参数的结构体。他们和服务端是完全一样的。
// 声明接收的参数结构体
type ArithRequest struct {
A,B int
}
// 声明返回客户端参数结构体
type ArithResponse struct {
// 乘积
Pro int
// 商
Quo int
// 余数
Rem int
}
在主函数中,远程连接rpc,连接的方式采用jsonrpc,按照tcp协议和服务端提供的地址和接口访问。
// 连接远程rpc
conn, err := jsonrpc.Dial("tcp", "127.0.0.1:8081")
if err != nil {
log.Println(err)
}
声明要计算的参数和返回参数变量
req := ArithRequest{9, 2}
var res ArithResponse
调用服务端函数,得到计算结果
// 调用乘积
err2 := conn.Call("Arith.Multiply", req, &res)
if err2 != nil {
log.Println(err2)
}
fmt.Printf("%d * %d = %d\n", req.A, req.B, res.Pro)
// 调用商
err3 := conn.Call("Arith.Divide", req, &res)
if err3 != nil {
log.Println(err3)
}
fmt.Printf("%d / %d,商 = %d, 余数 = %d\n", req.A, req.B, res.Quo, res.Rem)
运行结果:
9 * 2 = 18
9 / 2,商 = 4, 余数 = 1
server端完整代码
/**
* Package: rpcServer2
* Description: This package is ...
* Author: Jian Junbo
* Email: [email protected]
* Date: 2021/3/21 20:48
* Copyright ©2021 Jian Junbo & Shanxi Xiyue Mancang Technology Co., Ltd. All rights reserved.
**/
package main
import (
"errors"
"fmt"
"log"
"net"
"net/http"
"net/rpc"
"net/rpc/jsonrpc"
)
// 声明算术运算结构体
type Arith struct {
}
// 声明接收的参数结构体
type ArithRequest struct {
A,B int
}
// 声明返回客户端参数结构体
type ArithResponse struct {
// 乘积
Pro int
// 商
Quo int
// 余数
Rem int
}
// 乘法运算
func (a *Arith) Multiply(req ArithRequest, res *ArithResponse) error {
res.Pro = req.A * req.B
return nil
}
// 商和余数运算
func (a *Arith) Divide(req ArithRequest, res *ArithResponse) error {
if req.B == 0 {
return errors.New("除数不能为0")
}
// 计算 商
res.Quo = req.A / req.B
// 计算 余数
res.Rem = req.A % req.B
return nil
}
func mainrpc() {
// 注册服务
arith := new(Arith)
rpc.Register(arith)
// 绑定http协议
rpc.HandleHTTP()
err := http.ListenAndServe(":8081", nil)
if err != nil {
log.Fatal(err)
}
}
func main() {
// 注册服务
arith := new(Arith)
rpc.Register(arith)
// 监听服务
lis, err := net.Listen("tcp", "127.0.0.1:8081")
if err != nil {
log.Fatal(err)
}
for true {
conn, err := lis.Accept()
if err != nil {
continue
}
go func(conn net.Conn) {
fmt.Println("new Client")
jsonrpc.ServeConn(conn)
}(conn)
}
}
client端完整代码
/**
* Package: rpcClient2
* Description: This package is ...
* Author: Jian Junbo
* Email: [email protected]
* Date: 2021/3/21 20:57
* Copyright ©2021 Jian Junbo & Shanxi Xiyue Mancang Technology Co., Ltd. All rights reserved.
**/
package main
import (
"fmt"
"log"
"net/rpc/jsonrpc"
)
// 声明接收的参数结构体
type ArithRequest struct {
A,B int
}
// 声明返回客户端参数结构体
type ArithResponse struct {
// 乘积
Pro int
// 商
Quo int
// 余数
Rem int
}
func main() {
// 连接远程rpc
conn, err := jsonrpc.Dial("tcp", "127.0.0.1:8081")
if err != nil {
log.Println(err)
}
req := ArithRequest{9, 2}
var res ArithResponse
// 调用乘积
err2 := conn.Call("Arith.Multiply", req, &res)
if err2 != nil {
log.Println(err2)
}
fmt.Printf("%d * %d = %d\n", req.A, req.B, res.Pro)
// 调用商
err3 := conn.Call("Arith.Divide", req, &res)
if err3 != nil {
log.Println(err3)
}
fmt.Printf("%d / %d,商 = %d, 余数 = %d\n", req.A, req.B, res.Quo, res.Rem)
}