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.

FunTester
FunTester
FunTester
Why qrpc Beats gRPC: A Lightweight, High‑Performance RPC Framework

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

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

performanceMicroservicesRPCGoStreamingNetworkingpushqrpc
FunTester
Written by

FunTester

10k followers, 1k articles | completely useless

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.