88.go微服務(wù)之RPC實(shí)踐

微服務(wù)是現(xiàn)在大多數(shù)新項(xiàng)目會(huì)采用的架構(gòu)。服務(wù)端提供服務(wù),客戶端調(diào)用服務(wù)端的功能,就像調(diào)用一個(gè)函數(shù)一樣。微服務(wù)的更新升級(jí),只要接口沒有變化,客戶端完全可以無影響地升級(jí)到新功能。這就是微服務(wù)的第一個(gè)體驗(yàn)。
隨之而來的,就是每個(gè)微服務(wù)都可以用任意計(jì)算機(jī)語言實(shí)現(xiàn),方便了不同技術(shù)團(tuán)隊(duì)的協(xié)作。
而整個(gè)軟件系統(tǒng)的構(gòu)成,也不同于以往的實(shí)現(xiàn)方式了。這樣的構(gòu)成,更靈活和強(qiáng)大。當(dāng)然隨著服務(wù)數(shù)量的增加,也增加了服務(wù)的管理難度。
go語言的RPC服務(wù)實(shí)現(xiàn)起來非常簡單??梢岳胻cp或http協(xié)議來傳遞數(shù)據(jù)。
我們通過一個(gè)計(jì)算矩形面積和周長的微服務(wù)實(shí)例來學(xué)習(xí)rpc服務(wù)的編寫方法。
首先,實(shí)現(xiàn)server端(server.go),定義一個(gè)矩形

//  聲明矩形結(jié)構(gòu)體
type Rect struct {

}

再定義矩形的參數(shù)

//  聲明參數(shù)結(jié)構(gòu)體
type Params struct {
    //  寬,高
    Width, Height int
}

因?yàn)橛?jì)算面積和周長的RPC服務(wù)是遠(yuǎn)程調(diào)用,那么在服務(wù)端計(jì)算,通過獲取和改變內(nèi)存變量的值實(shí)現(xiàn)調(diào)用遠(yuǎn)程微服務(wù)像本地函數(shù)一樣的效果。所以,傳入的參數(shù)除了寬和高以外,還有一個(gè)供承載計(jì)算結(jié)果的整形指針。如果發(fā)生錯(cuò)誤,就返回error,無錯(cuò)誤的時(shí)候,return nil 就可以了。

//  計(jì)算矩形面積
func (r *Rect) Area(p Params, ret *int) error {
    *ret = p.Width * p.Height
    return nil
}
//  計(jì)算周長
func (r *Rect) Perimeter(p Params, ret *int) error {
    *ret = (p.Width + p.Height) * 2
    return nil
}

在主函數(shù)中,首先要注冊(cè)服務(wù)

    //  注冊(cè)服務(wù)
    rect := new(Rect)
    rpc.Register(rect)

然后綁定服務(wù)到http協(xié)議

// 服務(wù)綁定http協(xié)議
    rpc.HandleHTTP()

最后服務(wù)器開始監(jiān)聽服務(wù),等待客戶端來調(diào)用。這個(gè)服務(wù)我們使用8080端口。

//  監(jiān)聽服務(wù),等待客戶端調(diào)用(求面積和周長的方法)
    err := http.ListenAndServe(":8080", nil)
    if err != nil {
        log.Fatal(err)
    }

至此,微服務(wù)server端代碼已經(jīng)寫完。
下面開始編寫微服務(wù)client端代碼(client.go)。
client端要和server端擁有相同的參數(shù)結(jié)構(gòu)體。即

//  參數(shù)
type Params struct {
    Width, Height int
}

client遠(yuǎn)程連接server,這里采用tcp協(xié)議,還需要提前知道server的地址和通訊端口。以下代碼建立遠(yuǎn)程連接,并得到一個(gè)實(shí)例rp。

//  連接遠(yuǎn)程的RPC服務(wù)
    rp, err := rpc.DialHTTP("tcp", "127.0.0.1:8080")
    if err != nil {
        log.Println(err)
    }

為了獲取計(jì)算結(jié)果和提供計(jì)算參數(shù),聲明2個(gè)變量。一個(gè)保存結(jié)果,一個(gè)保存參數(shù)。

    //  結(jié)果
    ret := 0
    //  參數(shù)
    p := Params{50 ,100}

調(diào)用遠(yuǎn)程服務(wù)(面積)

//  1. 求面積
    err2 := rp.Call("Rect.Area", p, &ret)
    if err2 != nil {
        log.Println(err2)
    }else{
        fmt.Println("面積:", ret)
    }

調(diào)用遠(yuǎn)程服務(wù)(周長)

//  2. 求周長
    err3 := rp.Call("Rect.Perimeter", p, &ret)
    if err3 != nil {
        log.Println(err3)
    }else {
        fmt.Println("周長:", ret)
    }

運(yùn)行客戶端client.go,由于此時(shí)server未啟動(dòng),所以客戶端會(huì)得到一個(gè)連接被拒絕的結(jié)果。

2021/03/21 20:23:48 dial tcp 127.0.0.1:8080: connectex: 由于目標(biāo)計(jì)算機(jī)積極拒絕,無法連接。
panic: runtime error: invalid memory address or nil pointer dereference
[signal 0xc0000005 code=0x1 addr=0x10 pc=0x109e944]
...

啟動(dòng)server端,再次運(yùn)行client端,得到運(yùn)行結(jié)果

面積: 5000
周長: 300

server.go 完整代碼

/**
* Package: rpcServer
* Description: This package is rpcServer example
* Author: Jian Junbo
* Email: junbojian@qq.com
* Date:  2021/3/21 17:31
* Copyright ?2021 Jian Junbo & Shanxi Xiyue Mancang Technology Co., Ltd. All rights reserved.
**/
package main

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

//  聲明矩形結(jié)構(gòu)體
type Rect struct {

}
//  聲明參數(shù)結(jié)構(gòu)體
type Params struct {
    //  寬,高
    Width, Height int
}
//  計(jì)算矩形面積
func (r *Rect) Area(p Params, ret *int) error {
    *ret = p.Width * p.Height
    return nil
}
//  計(jì)算周長
func (r *Rect) Perimeter(p Params, ret *int) error {
    *ret = (p.Width + p.Height) * 2
    return nil
}

func main() {
    //  注冊(cè)服務(wù)
    rect := new(Rect)
    rpc.Register(rect)
    // 服務(wù)綁定http協(xié)議
    rpc.HandleHTTP()
    //  監(jiān)聽服務(wù),等待客戶端調(diào)用(求面積和周長的方法)
    err := http.ListenAndServe(":8080", nil)
    if err != nil {
        log.Fatal(err)
    }
}

client.go 完整代碼

/**
* Package: rpcClient
* Description: This package is rpcClient example.
* Author: Jian Junbo
* Email: junbojian@qq.com
* Date:  2021/3/21 18:31
* Copyright ?2021 Jian Junbo & Shanxi Xiyue Mancang Technology Co., Ltd. All rights reserved.
**/
package main

import (
    "fmt"
    "log"
    "net/rpc"
)
//  參數(shù)
type Params struct {
    Width, Height int
}

//  主函數(shù)調(diào)用服務(wù)
func main() {
    //  連接遠(yuǎn)程的RPC服務(wù)
    rp, err := rpc.DialHTTP("tcp", "127.0.0.1:8080")
    if err != nil {
        log.Println(err)
    }
    //  * 調(diào)用服務(wù)方法
    //  結(jié)果
    ret := 0
    //  參數(shù)
    p := Params{50 ,100}
    //  1. 求面積
    err2 := rp.Call("Rect.Area", p, &ret)
    if err2 != nil {
        log.Println(err2)
    }else{
        fmt.Println("面積:", ret)
    }

    //  2. 求周長
    err3 := rp.Call("Rect.Perimeter", p, &ret)
    if err3 != nil {
        log.Println(err3)
    }else {
        fmt.Println("周長:", ret)
    }
}

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

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

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