Home > Backend Development > Golang > RPC Action EPImplement a simple RPC interface in Go

RPC Action EPImplement a simple RPC interface in Go

PHPz
Release: 2024-09-09 18:30:11
Original
998 people have browsed it

RPC Action EPImplement a simple RPC interface in Go

RPC(Remote Procedure Call)is a widely used communication method between different nodes in distributed systems and a foundational technology of the Internet era. Go's standard library provides a simple implementation of RPC under the net/rpc package. This article aims to help you understand RPC by walking you through implementing a simple RPC interface using the net/rpc package.

This article was first published in the Medium MPP plan. If you are a Medium user, please follow me on Medium. Thank you very much.

To enable a function to be remotely called in net/rpc, it must meet the following five conditions:

  • The method’s type is exported.
  • The method is exported.
  • The method has two arguments, both of which are exported (or built-in) types.
  • The method’s second argument is a pointer.
  • The method has a return type of error.

In other words, the function signature must be:

func (t *T) MethodName(argType T1, replyType *T2) error
Copy after login

Creating a Simple RPC Request

Based on these five conditions, we can construct a simple RPC interface:

type HelloService struct{}  
func (p *HelloService) Hello(request string, reply *string) error {  
    log.Println("HelloService Hello")  
    *reply = "hello:" + request  
    return nil  
}
Copy after login

Next, you can register an object of the 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)  
    }  
    for {  
        conn, err := listener.Accept()  
        if err != nil {  
           log.Fatal("Accept error:", err)  
        }  
        go rpc.ServeConn(conn)  
    }
}
Copy after login

The client-side implementation is as follows:

func main() {
    conn, err := net.Dial("tcp", ":1234")
    if err != nil {
        log.Fatal("net.Dial:", err)
    }
    client := rpc.NewClient(conn)
    var reply string
    err = client.Call("HelloService.Hello", "hello", &reply)
    if err != nil {
        log.Fatal(err)
    }
    fmt.Println(reply)
}
Copy after login

First, the client dials the RPC service using rpc.Dial, then invokes a specific RPC method via client.Call(). The first parameter is the RPC service name and method name combined with a dot, the second is the input, and the third is the return value, which is a pointer. This example demonstrates how easy it is to use RPC.

In both the server and client code, we need to remember the RPC service name HelloService and the method name Hello. This can easily lead to errors during development, so we can wrap the code slightly by abstracting the common parts. The complete code is as follows:

// server.go
const ServerName = "HelloService"  

type HelloServiceInterface = interface {  
    Hello(request string, reply *string) error  
}  

func RegisterHelloService(srv HelloServiceInterface) error {  
    return rpc.RegisterName(ServerName, srv)  
}  

type HelloService struct{}  

func (p *HelloService) Hello(request string, reply *string) error {  
    log.Println("HelloService Hello")  
    *reply = "hello:" + request  
    return nil  
}

func main() {  
    _ = RegisterHelloService(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.ServeConn(conn)  
    }  
}
Copy after login
// client.go

type HelloServiceClient struct {  
    *rpc.Client  
}  

var _ HelloServiceInterface = (*HelloServiceClient)(nil)  

const ServerName = "HelloService" 

func DialHelloService(network, address string) (*HelloServiceClient, error) {  
    conn, err := net.Dial(network, address)  
    client := rpc.NewClient(conn)  
    if err != nil {  
       return nil, err  
    }  
    return &HelloServiceClient{Client: client}, nil  
}

func (p *HelloServiceClient) Hello(request string, reply *string) error {  
    return p.Client.Call(ServerName+".Hello", request, reply)  
}
func main() {
    client, err := DialHelloService("tcp", "localhost:1234")  
    if err != nil {  
        log.Fatal("net.Dial:", err)  
    }  
    var reply string  
    err = client.Hello("hello", &reply)  
    if err != nil {  
        log.Fatal(err)  
    }  
    fmt.Println(reply)
}
Copy after login

Does it look familiar?

Implementing JSON Codec with Go's net/rpc Package

By default, Go's standard RPC library uses Go's proprietary Gob encoding. However, it's straightforward to implement other encodings, such as Protobuf or JSON, on top of it. The standard library already supports jsonrpc encoding, and we can implement JSON encoding by making minor changes to the server and client code.

// server.go
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))  
       //go rpc.ServeConn(conn)  
    }  
}

//client.go
func DialHelloService(network, address string) (*HelloServiceClient, error) {  
    conn, err := net.Dial(network, address)  
    //client := rpc.NewClient(conn)  
    client := rpc.NewClientWithCodec(jsonrpc.NewClientCodec(conn))  
    if err != nil {  
       return nil, err  
    }  
    return &HelloServiceClient{Client: client}, nil  
}
Copy after login

The JSON request data object internally corresponds to two structures: on the client side, it's clientRequest, and on the server side, it's serverRequest. The content of clientRequest and serverRequest structures is essentially the same:

type clientRequest struct {  
    Method string `json:"method"`  
    Params [1]any `json:"params"`  
    Id     uint64 `json:"id"`  
}
type serverRequest struct {  
    Method string           `json:"method"`  
    Params *json.RawMessage `json:"params"`  
    Id     *json.RawMessage `json:"id"`  
}
Copy after login

Here, Method represents the service name composed of serviceName and Method. The first element of Params is the parameter, and Id is a unique call number maintained by the caller, used to distinguish requests in concurrent scenarios.

We can use nc to simulate the server and then run the client code to see what information the JSON-encoded client sends to the server:

 nc -l 1234
Copy after login

The nc command receives the following data:

 {"method":"HelloService.Hello","params":["hello"],"id":0}
Copy after login

This is consistent with serverRequest.

We can also run the server code and use nc to send a request:

echo -e '{"method":"HelloService.Hello","params":["Hello"],"Id":1}' | nc localhost 1234 
--- 
{"id":1,"result":"hello:Hello","error":null}
Copy after login

Conclusion

This article introduced the rpc package from Go's standard library, highlighting its simplicity and powerful performance. Many third-party rpc libraries are built on top of the rpc package. This article serves as the first installment in a series on RPC research. In the next article, we will combine protobuf with RPC and eventually implement our own RPC framework.

The above is the detailed content of RPC Action EPImplement a simple RPC interface in Go. For more information, please follow other related articles on the PHP Chinese website!

source:dev.to
Statement of this Website
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn
Popular Tutorials
More>
Latest Downloads
More>
Web Effects
Website Source Code
Website Materials
Front End Template