RPC

Here is the explanation of RPC from Wikipedia, which can be compared with HTTP protocol, RPC is more suitable for distributed call scenarios in large and medium-sized projects in companies.

In distributed computing, Remote Procedure Call (RPC for short) is a computer communication protocol. The protocol allows a program running on one computer to call a subroutine in another address space (usually a computer on an open network) without the programmer having to additionally program this interaction (without attention to detail), just as if it were a local program. RPC is a server-client (Client/Server) pattern, classically implemented as a system that interacts with information by sending requests and receiving responses.

Calling process

  1. The client side calls the client stub (client stub). This call is made locally and pushes the call parameters onto the stack.
  2. The client stub wraps these arguments and sends them to the server machine via a system call. The process of packaging is called marshalling. (Common methods: XML, JSON, binary encoding).
  3. the client local operating system sends information to the server. (can be transmitted via custom TCP protocol or HTTP).
  4. the server system transmits the information to the server stub (server stub).
  5. the server stub parses the information. This process is called unmarshalling.
  6. the server stub calls the program and returns it to the client in a similar way.

Call flow of C/S architecture

RPC and HTTP Differences

RPC calls are implemented in a similar way to HTTP, but there are differences between RPC and HTTP in the request/response:

  1. HTTP and RPC protocols are different in implementation, we all understand that the principle of HTTP is that the client requests the server, the server responds and returns the results, but the RPC protocol is designed in such a way that the server provides TCP long connection services to the client, and the Client calls the interface provided by the Server to achieve specific functions;
  2. RPC can provide both synchronous and asynchronous invocation, while HTTP provides a way to synchronous invocation, the client will wait and accept the results of the server’s request processing.
  3. the RPC service design can improve the decoupling operation in the code writing process and improve the portability of the code, each service can be designed to provide a specific function of the small service, the client to call the remote service, without caring how the remote is implemented.

RPC application areas

  • internal subsystem design for large websites.
  • providing downgrading functionality for systems.
  • concurrent design scenarios.

Of course, RPC also has disadvantages, each RPC service needs to be built separately, once the service is wrong or more serious does not provide support, as the client service will be unavailable, which requires high system stability and sustainable support, of course, in the design process, this also increases the difficulty of debugging the system, that is, this design requires the stability and correctness of RPC services The requirements are relatively large.

Practices

Client

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package main

import (
    "demo/common"
    "fmt"
    "net/rpc"
)

func main() {
    var args = common.Args{A: 32, B: 14}
    var result = common.Result{}

    var client, err = rpc.DialHTTP("tcp", "127.0.0.1:9090")
    if err != nil {
        fmt.Printf("connect rpc server failed, err:%v", err)
    }

    err = client.Call("MathService.Divide", args, &result)
    if err != nil {
        fmt.Printf("call math service failed, err:%v", err)
    }
    fmt.Printf("call RPC server success, result:%f", result.Value)
}

Server-side

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
package main

import (
    "demo/common"
    "fmt"
    "net/http"
    "net/rpc"
)

func main() {
    var ms = new(common.MathService)
    // Registering RPC Services
    err := rpc.Register(ms)
    if err != nil {
        fmt.Printf("rpc server register faild, err:%s", err)
    }
    // Bind the RPC service to the HTTP service
    rpc.HandleHTTP()

    fmt.Printf("server start ....")
    err = http.ListenAndServe(":9090", nil)

    if err != nil {
        fmt.Printf("listen and server is failed, err:%v\n", err)
    }

    fmt.Printf("server stop ....")
}

Function Implementation

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
package common

import "errors"

type Args struct {
    A, B float32
}

type Result struct {
    Value float32
}

type MathService struct {}


func (s *MathService) Add (args *Args, result *Result) error{
    result.Value = args.A + args.B
    return nil
}


func (s *MathService) Divide(args *Args, result *Result) error{
    if args.B == 0 {
        return errors.New("arge.B is 0")
    }

    result.Value = args.A / args.B
    return nil
}