Why qrpc Beats gRPC: A Lightweight, High‑Performance RPC Framework
qrpc is a lightweight, high‑performance RPC framework that adopts gRPC's streaming and bidirectional concepts without HTTP/2, offering a smaller binary, lower memory usage, up to three‑fold throughput gains, and flexible modes such as blocking, non‑blocking, streaming, push, and bidirectional calls, all demonstrated with Go code examples and real‑world use cases.
What is qrpc?
qrpc is a lightweight general‑purpose long‑connection RPC framework that adopts the streaming and bidirectional call concepts of Google gRPC but removes the HTTP/2 dependency. This yields a smaller binary, lower memory consumption and 2‑3× higher throughput.
Key Features
Blocking and non‑blocking modes
Streaming and non‑streaming modes
Active push
Bidirectional calls
Protocol Design
Both request and response use the same frame structure: an 8‑byte request ID, a 1‑byte flag, a 3‑byte command, and a payload of configurable length. The flag byte encodes mode information such as blocking, streaming, or push.
Basic Server (blocking mode)
package main
import "github.com/zhiqiangxu/qrpc"
const (
HelloCmd qrpc.Cmd = iota
HelloRespCmd
)
func main() {
handler := qrpc.NewServeMux()
handler.HandleFunc(HelloCmd, func(writer qrpc.FrameWriter, request *qrpc.RequestFrame) {
writer.StartWrite(request.RequestID, HelloRespCmd, 0)
writer.WriteBytes(append([]byte("hello world "), request.Payload...))
writer.EndWrite()
})
bindings := []qrpc.ServerBinding{{Addr: "0.0.0.0:8080", Handler: handler}}
server := qrpc.NewServer(bindings)
server.ListenAndServe()
}Basic Client
package main
import (
"fmt"
"github.com/zhiqiangxu/qrpc"
)
const HelloCmd qrpc.Cmd = iota
func main() {
conf := qrpc.ConnectionConfig{}
conn, _ := qrpc.NewConnection("0.0.0.0:8080", conf, nil)
_, resp, _ := conn.Request(HelloCmd, 0, []byte("xu"))
frame, _ := resp.GetFrame()
fmt.Println("resp is", string(frame.Payload))
}Mode Switching
Non‑blocking mode: use qrpc.NBFlag as the flag argument in Request.
_, resp, _ := conn.Request(HelloCmd, qrpc.NBFlag, []byte("xu"))Streaming mode: use StreamRequest to send multiple frames and mark the final frame with qrpc.StreamEndFlag. The server reads subsequent frames from request.FrameCh().
Push Mode
Server can push messages to all connected clients using qrpc.PushFlag. Example iterates over connections and sends a push frame in parallel.
package main
import (
"github.com/zhiqiangxu/qrpc"
"sync"
)
const (
HelloCmd qrpc.Cmd = iota
HelloRespCmd
)
func main() {
handler := qrpc.NewServeMux()
handler.HandleFunc(HelloCmd, func(writer qrpc.FrameWriter, request *qrpc.RequestFrame) {
var wg sync.WaitGroup
qserver := request.ConnectionInfo().SC.Server()
pushID := qserver.GetPushID()
qserver.WalkConn(0, func(writer qrpc.FrameWriter, ci *qrpc.ConnectionInfo) bool {
qrpc.GoFunc(&wg, func() {
writer.StartWrite(pushID, HelloCmd, qrpc.PushFlag)
writer.WriteBytes([]byte("pushed msg"))
writer.EndWrite()
})
return true
})
wg.Wait()
writer.StartWrite(request.RequestID, HelloRespCmd, 0)
writer.WriteBytes(append([]byte("push done"), request.Payload...))
writer.EndWrite()
})
bindings := []qrpc.ServerBinding{{Addr: "0.0.0.0:8080", Handler: handler}}
server := qrpc.NewServer(bindings)
server.ListenAndServe()
}Bidirectional Calls
Clients can register a callback to handle server‑initiated frames. See the test case TestClientHandler for an example.
Streaming Request / Response Example
// client side
writer, resp, _ := conn.StreamRequest(HelloCmd, 0, []byte("first frame"))
writer.StartWrite(HelloCmd)
writer.WriteBytes([]byte("last frame"))
writer.EndWrite(true) // attaches StreamEndFlag
frame, _ := resp.GetFrame()
fmt.Println("resp is", string(frame.Payload))
// server side (inside handler)
writer.StartWrite(request.RequestID, HelloRespCmd, qrpc.StreamFlag)
writer.WriteBytes(append([]byte("first frame "), request.Payload...))
writer.EndWrite()
for {
cont := <-request.FrameCh()
if cont == nil { break }
if cont.Flags.IsDone() {
writer.StartWrite(request.RequestID, HelloRespCmd, qrpc.StreamEndFlag)
} else {
writer.StartWrite(request.RequestID, HelloRespCmd, qrpc.StreamFlag)
}
writer.WriteBytes(append([]byte(" continue frame "), cont.Payload...))
writer.EndWrite()
}Performance
Benchmarks show qrpc achieves roughly four times the throughput of plain HTTP for comparable workloads.
Typical Use Cases
Push notifications and instant messaging
Micro‑service RPC
Middleware requiring multi‑step transactions
Repository
Source code: https://github.com/zhiqiangxu/qrpc
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
