89.go微服務之jsonRPC實踐

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)

}

?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容