go語言中的RPC只能在go語言的程序中調(diào)用。如果使用其他語言編寫的客戶端,就要用到jsonRPC了。jsonRPC可以被跨語言調(diào)用。
這里我們采用一個計算乘法和除法求商取余數(shù)的例子來實踐。
首先,服務端server.go聲明算術運算結(jié)構(gòu)體和接收的參數(shù)結(jié)構(gòu)體及返回客戶端參數(shù)結(jié)構(gòu)體。
// 聲明算術運算結(jié)構(gòu)體
type Arith struct {
}
// 聲明接收的參數(shù)結(jié)構(gòu)體
type ArithRequest struct {
A,B int
}
// 聲明返回客戶端參數(shù)結(jié)構(gòu)體
type ArithResponse struct {
// 乘積
Pro int
// 商
Quo int
// 余數(shù)
Rem int
}
計算乘法的函數(shù)
// 乘法運算
func (a *Arith) Multiply(req ArithRequest, res *ArithResponse) error {
res.Pro = req.A * req.B
return nil
}
計算商和余數(shù)的時候,要注意一下需要考慮除數(shù)不能為0的情況。當除數(shù)為0的時候,就要用到返回結(jié)果error了。
// 商和余數(shù)運算
func (a *Arith) Divide(req ArithRequest, res *ArithResponse) error {
if req.B == 0 {
return errors.New("除數(shù)不能為0")
}
// 計算 商
res.Quo = req.A / req.B
// 計算 余數(shù)
res.Rem = req.A % req.B
return nil
}
在主函數(shù)中,首先要注冊服務
// 注冊服務
arith := new(Arith)
rpc.Register(arith)
然后要監(jiān)聽服務。這里我們采用tcp協(xié)議。需要確定服務采用的通訊端口,這里我們采用8081端口。
// 監(jiān)聽服務
lis, err := net.Listen("tcp", "127.0.0.1:8081")
if err != nil {
log.Fatal(err)
}
之后就是持續(xù)監(jiān)聽等待客戶端訪問,每當有訪問發(fā)生就產(chǎn)生一個goroutine。如果一個客戶端的連接發(fā)生錯誤,要忽略這次錯誤,進入下一個循環(huán)等待階段。這里使用for循環(huán)實現(xiàn)。
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的代碼。
首先聲明一下接收參數(shù)和返回客戶端參數(shù)的結(jié)構(gòu)體。他們和服務端是完全一樣的。
// 聲明接收的參數(shù)結(jié)構(gòu)體
type ArithRequest struct {
A,B int
}
// 聲明返回客戶端參數(shù)結(jié)構(gòu)體
type ArithResponse struct {
// 乘積
Pro int
// 商
Quo int
// 余數(shù)
Rem int
}
在主函數(shù)中,遠程連接rpc,連接的方式采用jsonrpc,按照tcp協(xié)議和服務端提供的地址和接口訪問。
// 連接遠程rpc
conn, err := jsonrpc.Dial("tcp", "127.0.0.1:8081")
if err != nil {
log.Println(err)
}
聲明要計算的參數(shù)和返回參數(shù)變量
req := ArithRequest{9, 2}
var res ArithResponse
調(diào)用服務端函數(shù),得到計算結(jié)果
// 調(diào)用乘積
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)
// 調(diào)用商
err3 := conn.Call("Arith.Divide", req, &res)
if err3 != nil {
log.Println(err3)
}
fmt.Printf("%d / %d,商 = %d, 余數(shù) = %d\n", req.A, req.B, res.Quo, res.Rem)
運行結(jié)果:
9 * 2 = 18
9 / 2,商 = 4, 余數(shù) = 1
server端完整代碼
/**
* Package: rpcServer2
* Description: This package is ...
* Author: Jian Junbo
* Email: junbojian@qq.com
* 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"
)
// 聲明算術運算結(jié)構(gòu)體
type Arith struct {
}
// 聲明接收的參數(shù)結(jié)構(gòu)體
type ArithRequest struct {
A,B int
}
// 聲明返回客戶端參數(shù)結(jié)構(gòu)體
type ArithResponse struct {
// 乘積
Pro int
// 商
Quo int
// 余數(shù)
Rem int
}
// 乘法運算
func (a *Arith) Multiply(req ArithRequest, res *ArithResponse) error {
res.Pro = req.A * req.B
return nil
}
// 商和余數(shù)運算
func (a *Arith) Divide(req ArithRequest, res *ArithResponse) error {
if req.B == 0 {
return errors.New("除數(shù)不能為0")
}
// 計算 商
res.Quo = req.A / req.B
// 計算 余數(shù)
res.Rem = req.A % req.B
return nil
}
func mainrpc() {
// 注冊服務
arith := new(Arith)
rpc.Register(arith)
// 綁定http協(xié)議
rpc.HandleHTTP()
err := http.ListenAndServe(":8081", nil)
if err != nil {
log.Fatal(err)
}
}
func main() {
// 注冊服務
arith := new(Arith)
rpc.Register(arith)
// 監(jiān)聽服務
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: junbojian@qq.com
* 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"
)
// 聲明接收的參數(shù)結(jié)構(gòu)體
type ArithRequest struct {
A,B int
}
// 聲明返回客戶端參數(shù)結(jié)構(gòu)體
type ArithResponse struct {
// 乘積
Pro int
// 商
Quo int
// 余數(shù)
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
// 調(diào)用乘積
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)
// 調(diào)用商
err3 := conn.Call("Arith.Divide", req, &res)
if err3 != nil {
log.Println(err3)
}
fmt.Printf("%d / %d,商 = %d, 余數(shù) = %d\n", req.A, req.B, res.Quo, res.Rem)
}