89.go微服务之jsonRPC实践

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)

}

你可能感兴趣的:(89.go微服务之jsonRPC实践)