Golang Beginner's Guide to RPC Server
Framework Design
Our framework will include the following core components:
Server
Object: This object encapsulates the underlying RPC server instance and provides a simple and easy-to-use API to manage the server.NewServer(addr string)
: A factory function to create a newServer
instance and specify the listening address.Register(serviceName string, service interface{})
: A method of theServer
object to register your business service with the RPC server. You can specify a custom name for the service.Start()
: A method of theServer
object to start the RPC server and begin listening for client connections.
Framework Implementation (quickrpc/server.go
)
First, we create a Go module for the framework and implement Server
.
// ... (code unchanged, see original for details)
How to Use the Framework
Now, let's see how to use this quickrpc
framework to quickly build an RPC server.
1. Create Your Project
First, create a new Go project and import our quickrpc
framework and rpcx
.
2. Implement Your Business Service
Define your business service and related data structures. Here we use a simple math service Arith
as an example.
// ... (code unchanged, see original for details)
3. Start the RPC Server (main.go
)
Use our quickrpc
framework to start the server and register the Arith
service.
// ... (code unchanged, see original for details)
Client Example (client/main.go
)
To test our RPC server, here is a client implementation:
// ... (code unchanged, see original for details)
With this simple framework, you can see that building a new RPC server only takes three steps:
- Create a
Server
. - Register your business service.
- Start the
Server
.
This design greatly simplifies the process of building an RPC server, allowing you to focus more on business logic development without worrying too much about low-level implementation details. You can extend this framework, for example, by adding service discovery, load balancing, authentication, etc.
A basic RPC server can work, but a production-level server must have high robustness and fault tolerance.
To improve our previously designed quickrpc
framework, we can add logic in the following key areas:
- Graceful Shutdown: When the server needs to shut down or restart (e.g., deploying a new version), it should not immediately cut off all connections, but stop accepting new requests and wait for current requests to finish before shutting down. This prevents data inconsistency or client request failures.
- Exception Recovery (Panic Recovery): If an RPC method panics due to code bugs (such as nil pointer dereference), the entire server process should not crash. We should catch this panic, convert it to an error returned to the client, log it, and ensure the server continues to serve other clients.
- Unified Logging: We need to record key events, such as server start/shutdown, request entry and exit, processing time, and any errors. Using structured logs (like JSON) greatly helps with monitoring and troubleshooting.
- Middleware/Plugin Support: Implement cross-cutting concerns like logging and panic recovery as middleware, keeping business code clean and making the framework more extensible.
Below, we will gradually modify the quickrpc
framework and example code to implement these features.
1. Upgrade the Framework quickrpc/server.go
We will refactor Server
to support plugins and provide graceful shutdown. rpcx
itself provides a convenient plugin mechanism for us to implement middleware.
// ... (code unchanged, see original for details)
2. Implement Logging and Panic Recovery Plugins
Now let's create two core plugins.
Logging Plugin (quickrpc/logging_plugin.go
)
This plugin logs before and after request processing and records processing time.
// ... (code unchanged, see original for details)
Panic Recovery Plugin (quickrpc/recovery_plugin.go
)
This plugin uses defer
and recover
to catch any panics in service methods.
// ... (code unchanged, see original for details)
Note: The rpcx
plugin system is complex for directly modifying response errors. The officially recommended way for panic recovery is to enable the built-in recovery option when creating the server. For teaching purposes, we still use the plugin here, but for production, use the official option for simplicity and robustness.
3. Update main.go
to Use New Features
Now update main.go
to use our new plugins and add a method that triggers a panic to test recovery.
Business Service (main.go
)
// ... (code unchanged, see original for details)
Server Startup Logic (main.go
)
// ... (code unchanged, see original for details)
Summary
Feature | Implementation Logic | Benefits |
---|---|---|
Graceful Shutdown | Listens for SIGINT , SIGTERM signals, calls rpcxServer.Close() with timeout. | Ensures ongoing requests complete, prevents data loss and client errors, makes deployment and maintenance smoother. |
Exception Recovery | Uses plugin (or rpcx built-in option) with defer-recover to catch panics in service methods. | A single request's panic won't crash the whole server process, improving stability and availability. |
Unified Logging | Logging plugin records key info (service name, method, duration, errors) before and after requests. | Greatly improves system observability; logs are essential for locating and analyzing issues. |
Plugin Architecture | Use() method allows easy addition of any plugin. | Makes it easy to add new features (like auth, rate limiting, distributed tracing), keeps business logic clean, and code clearer. |
Now, when you run this server and call the PanicTest
method from the client, the server won't crash but will print the panic info and stack trace, and the client will receive an error. All requests are logged by the logging plugin. When you press Ctrl+C
, the server prints "Shutting down..." and waits for ongoing requests to finish before exiting.