go Language Advanced Programming RPC

RPC is the abbreviation of remote procedure call. It is a popular communication mode between different nodes in distributed system. In the Internet era, RPC has become an indispensable basic component like IPC. Therefore, the standard library of Go language also provides a simple RPC implementation.

1. RPC based on gob coding

The path of the RPC package of Go language is net/rpc, which is placed under the net package directory. RPC package is based on net package.

Let's first construct a HelloService type, in which the Hello method is used to realize the printing function:

type HelloService struct {}

type Request struct {
A string
}

type Response struct {
    B string
}
func (p *HelloService) Hello(request Request, reply * Response) error {
    reply.B = "hello:" + request.A
    return nil
}

The Hello method must meet the RPC rules of Go language: the method can only have two serializable parameters, the second parameter is the pointer type, and returns an error type. At the same time, it must be an exposed method.

Then you can register an object of HelloService type as an RPC service:

func main() {
    rpc.RegisterName("HelloService", new(HelloService))

    listener, err := net.Listen("tcp", ":1234")
    if err != nil {
        log.Fatal("ListenTCP error:", err)
    }

    conn, err := listener.Accept()
    if err != nil {
        log.Fatal("Accept error:", err)
    }

    rpc.ServeConn(conn)
}

Including RPC The register function call will register all object methods that meet RPC rules in the object type as RPC functions, and all registered methods will be placed under the "HelloService" service space. Then we establish a unique TCP link through RPC The serverconn function provides RPC services for the other party on the TCP link.

The following is the code of the client requesting HelloService service:

func main() {
    client, err := rpc.Dial("tcp", "localhost:1234")
    if err != nil {
        log.Fatal("dialing:", err)
    }

    var reply Response
    err = client.Call("HelloService.Hello", Request{A: "hello"}, &reply)
    if err != nil {
        log.Fatal(err)
    }
    fmt.Println(reply.B)
}

The first is through RPC Dial up the RPC service, and then use the client Call calls the specific RPC method. When calling client When calling, the first parameter is the RPC service name and method name linked with a dot. The second and third parameters define the two parameters of the RPC method respectively.

2. Cross language jsonrpc

The RPC of the standard library adopts the gob code unique to Go language by default, so it will be difficult to call the RPC service implemented by Go language from other languages. In the era of Internet microservices, each RPC and service users may use different programming languages. Therefore, cross language is a primary condition of RPC in the era of Internet. Thanks to the framework design of RPC, the RPC of Go language is actually easy to realize cross language support.

The RPC framework of Go language has two distinctive designs: one is that when RPC data is packaged, user-defined encoding and decoding can be realized through plug-ins; The other is that RPC is based on abstract io Based on the readwritecloser interface, we can set up RPC on different communication protocols. Here we will try to implement a cross language RPC through the official net/rpc/jsonrpc extension.

The first is to re implement the RPC service based on json coding:

func main() {
    rpc.RegisterName("HelloService", new(HelloService))

    listener, err := net.Listen("tcp", ":1234")
    if err != nil {
        log.Fatal("ListenTCP error:", err)
    }

    for {
        conn, err := listener.Accept()
        if err != nil {
            log.Fatal("Accept error:", err)
        }

        go rpc.ServeCodec(jsonrpc.NewServerCodec(conn))
    }
}

The biggest change in the code is to use RPC The servercodec function replaces RPC Serverconn function. The parameters passed in are for the json codec of the server.

Then there is the client that implements the json version:

func main() {
    conn, err := net.Dial("tcp", "localhost:1234")
    if err != nil {
        log.Fatal("net.Dial:", err)
    }

    client := rpc.NewClientWithCodec(jsonrpc.NewClientCodec(conn))

    var reply Response
    err = client.Call("HelloService.Hello", Request{A: "hello"}, &reply)
    if err != nil {
        log.Fatal(err)
    }

    fmt.Println(reply.B)
}

Call net first The dial function establishes a TCP link, and then establishes a json codec for the client based on the link.

No matter what language is used, you can communicate with the RPC service written in Go language as long as you follow the same json structure and the same process. In this way, we have realized cross language RPC.

3. RPC over Http

The internal RPC framework of Go language has supported the provision of RPC services on HTTP protocol. However, the HTTP service of the framework also adopts the built-in gob protocol, and does not provide interfaces using other protocols, so it is still inaccessible from other languages. In the previous example, the jsonrpc service running on the TCP protocol has been realized, and the RPC method call has been successfully realized through the nc command line tool. Now we provide JSON RPC service over HTTP protocol.

The new RPC service is actually an interface similar to the REST specification. It receives requests and adopts the corresponding processing flow:

func main() {
    rpc.RegisterName("HelloService", new(HelloService))

    http.HandleFunc("/jsonrpc", func(w http.ResponseWriter, r *http.Request) {
        var conn io.ReadWriteCloser = struct {
            io.Writer
            io.ReadCloser
        }{
            ReadCloser: r.Body,
            Writer:     w,
        }

        rpc.ServeRequest(jsonrpc.NewServerCodec(conn))
    })

    http.ListenAndServe(":1234", nil)
}

The RPC service is set up in the "/ jsonrpc" path and is based on HTTP in the processing function Responsewriter and HTTP Request type parameters construct an IO conn channel of type readwritecloser. Then, a json codec for the server is built based on conn. Finally, through RPC The serverequest function processes an RPC method call for each request. This makes it easy to access RPC services from different languages.

 

 

 

 

 

Tags: Go

Posted by dips_007 on Sat, 07 May 2022 06:06:40 +0300