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
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
}
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)
}
}
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)
}
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)
}
}
// 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)
}
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
}
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"`
}
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
The nc command receives the following data:
{"method":"HelloService.Hello","params":["hello"],"id":0}
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}
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!
Hot AI Tools
Undresser.AI Undress
AI-powered app for creating realistic nude photos
AI Clothes Remover
Online AI tool for removing clothes from photos.
Undress AI Tool
Undress images for free
Clothoff.io
AI clothes remover
Video Face Swap
Swap faces in any video effortlessly with our completely free AI face swap tool!
Hot Article
Hot Tools
Notepad++7.3.1
Easy-to-use and free code editor
SublimeText3 Chinese version
Chinese version, very easy to use
Zend Studio 13.0.1
Powerful PHP integrated development environment
Dreamweaver CS6
Visual web development tools
SublimeText3 Mac version
God-level code editing software (SublimeText3)
Hot Topics
1389
52
What are the vulnerabilities of Debian OpenSSL
Apr 02, 2025 am 07:30 AM
OpenSSL, as an open source library widely used in secure communications, provides encryption algorithms, keys and certificate management functions. However, there are some known security vulnerabilities in its historical version, some of which are extremely harmful. This article will focus on common vulnerabilities and response measures for OpenSSL in Debian systems. DebianOpenSSL known vulnerabilities: OpenSSL has experienced several serious vulnerabilities, such as: Heart Bleeding Vulnerability (CVE-2014-0160): This vulnerability affects OpenSSL 1.0.1 to 1.0.1f and 1.0.2 to 1.0.2 beta versions. An attacker can use this vulnerability to unauthorized read sensitive information on the server, including encryption keys, etc.
How do you write unit tests in Go?
Mar 21, 2025 pm 06:34 PM
The article discusses writing unit tests in Go, covering best practices, mocking techniques, and tools for efficient test management.
How do you use the pprof tool to analyze Go performance?
Mar 21, 2025 pm 06:37 PM
The article explains how to use the pprof tool for analyzing Go performance, including enabling profiling, collecting data, and identifying common bottlenecks like CPU and memory issues.Character count: 159
What libraries are used for floating point number operations in Go?
Apr 02, 2025 pm 02:06 PM
The library used for floating-point number operation in Go language introduces how to ensure the accuracy is...
What is the problem with Queue thread in Go's crawler Colly?
Apr 02, 2025 pm 02:09 PM
Queue threading problem in Go crawler Colly explores the problem of using the Colly crawler library in Go language, developers often encounter problems with threads and request queues. �...
PostgreSQL monitoring method under Debian
Apr 02, 2025 am 07:27 AM
This article introduces a variety of methods and tools to monitor PostgreSQL databases under the Debian system, helping you to fully grasp database performance monitoring. 1. Use PostgreSQL to build-in monitoring view PostgreSQL itself provides multiple views for monitoring database activities: pg_stat_activity: displays database activities in real time, including connections, queries, transactions and other information. pg_stat_replication: Monitors replication status, especially suitable for stream replication clusters. pg_stat_database: Provides database statistics, such as database size, transaction commit/rollback times and other key indicators. 2. Use log analysis tool pgBadg
Transforming from front-end to back-end development, is it more promising to learn Java or Golang?
Apr 02, 2025 am 09:12 AM
Backend learning path: The exploration journey from front-end to back-end As a back-end beginner who transforms from front-end development, you already have the foundation of nodejs,...
How to solve the user_id type conversion problem when using Redis Stream to implement message queues in Go language?
Apr 02, 2025 pm 04:54 PM
The problem of using RedisStream to implement message queues in Go language is using Go language and Redis...


