MCPcopy Index your code
hub / github.com/andeya/erpc

github.com/andeya/erpc @v7.2.2 sqlite

repository ↗ · DeepWiki ↗ · release v7.2.2 ↗
2,024 symbols 6,122 edges 159 files 1,253 documented · 62%
README

eRPC

tag Go Version GoDoc Build Status Go report License

eRPC is an efficient, extensible and easy-to-use RPC framework.

Suitable for RPC, Microservice, Peer-to-Peer, IM, Game and other fields.

简体中文

eRPC-Framework

Install

  • go vesion ≥ 1.18

  • install

GO111MODULE=on go get -u -v -insecure github.com/andeya/erpc/v7
  • import
import "github.com/andeya/erpc/v7"

Feature

  • Use peer to provide the same API package for the server and client
  • Provide multi-layout abstractions such as:
  • peer
  • session/socket
  • router
  • handle/context
  • message
  • protocol
  • codec
  • transfer filter
  • plugin
  • Support reboot and shutdown gracefully
  • HTTP-compatible message format:
  • Composed of two parts, the Header and the Body
  • Header contains metadata in the same format as HTTP header
  • Body supports for custom codec of Content Type-Like, already implemented:
    • Protobuf
    • Thrift
    • JSON
    • XML
    • Form
    • Plain
  • Support push, call-reply and more message types
  • Support custom message protocol, and provide some common implementations:
  • rawproto - Default high performance binary protocol
  • jsonproto - JSON message protocol
  • pbproto - Ptotobuf message protocol
  • thriftproto - Thrift message protocol
  • httproto - HTTP message protocol
  • Optimized high performance transport layer
  • Use Non-block socket and I/O multiplexing technology
  • Support setting the size of socket I/O buffer
  • Support setting the size of the reading message (if exceed disconnect it)
  • Support controling the connection file descriptor
  • Support a variety of network types:
  • tcp
  • tcp4
  • tcp6
  • unix
  • unixpacket
  • kcp
  • quic
  • other
    • websocket
    • evio
  • Provide a rich plug-in point, and already implemented:
  • auth
  • binder
  • heartbeat
  • ignorecase(service method)
  • overloader
  • proxy(for unknown service method)
  • secure
  • Powerful and flexible logging system:
  • Detailed log information, support print input and output details
  • Support setting slow operation alarm threshold
  • Support for custom implementation log component
  • Client session support automatically redials after disconnection

Benchmark

Self Test

  • A server and a client process, running on the same machine
  • CPU: Intel Xeon E312xx (Sandy Bridge) 16 cores 2.53GHz
  • Memory: 16G
  • OS: Linux 2.6.32-696.16.1.el6.centos.plus.x86_64, CentOS 6.4
  • Go: 1.9.2
  • Message size: 581 bytes
  • Message codec: protobuf
  • Sent total 1000000 messages

  • erpc

client concurrency mean(ms) median(ms) max(ms) min(ms) throughput(TPS)
100 1 0 16 0 75505
500 9 11 97 0 52192
1000 19 24 187 0 50040
2000 39 54 409 0 42551
5000 96 128 1148 0 46367
  • erpc/socket
client concurrency mean(ms) median(ms) max(ms) min(ms) throughput(TPS)
100 0 0 14 0 225682
500 2 1 24 0 212630
1000 4 3 51 0 180733
2000 8 6 64 0 183351
5000 21 18 651 0 133886

Comparison Test

EnvironmentThroughputsMean LatencyP99 Latency

More Detail

  • Profile torch of erpc/socket

erpc_socket_profile_torch

svg file

  • Heap torch of erpc/socket

erpc_socket_heap_torch

svg file

Example

server.go

package main

import (
    "fmt"
    "time"

    "github.com/andeya/erpc/v7"
)

func main() {
    defer erpc.FlushLogger()
    // graceful
    go erpc.GraceSignal()

    // server peer
    srv := erpc.NewPeer(erpc.PeerConfig{
        CountTime:   true,
        ListenPort:  9090,
        PrintDetail: true,
    })
    // srv.SetTLSConfig(erpc.GenerateTLSConfigForServer())

    // router
    srv.RouteCall(new(Math))

    // broadcast per 5s
    go func() {
        for {
            time.Sleep(time.Second * 5)
            srv.RangeSession(func(sess erpc.Session) bool {
                sess.Push(
                    "/push/status",
                    fmt.Sprintf("this is a broadcast, server time: %v", time.Now()),
                )
                return true
            })
        }
    }()

    // listen and serve
    srv.ListenAndServe()
}

// Math handler
type Math struct {
    erpc.CallCtx
}

// Add handles addition request
func (m *Math) Add(arg *[]int) (int, *erpc.Status) {
    // test meta
    erpc.Infof("author: %s", m.PeekMeta("author"))
    // add
    var r int
    for _, a := range *arg {
        r += a
    }
    // response
    return r, nil
}

client.go

package main

import (
  "time"

  "github.com/andeya/erpc/v7"
)

func main() {
  defer erpc.SetLoggerLevel("ERROR")()

  cli := erpc.NewPeer(erpc.PeerConfig{})
  defer cli.Close()
  // cli.SetTLSConfig(&tls.Config{InsecureSkipVerify: true})

  cli.RoutePush(new(Push))

  sess, stat := cli.Dial(":9090")
  if !stat.OK() {
    erpc.Fatalf("%v", stat)
  }

  var result int
  stat = sess.Call("/math/add",
    []int{1, 2, 3, 4, 5},
    &result,
    erpc.WithAddMeta("author", "andeya"),
  ).Status()
  if !stat.OK() {
    erpc.Fatalf("%v", stat)
  }
  erpc.Printf("result: %d", result)
  erpc.Printf("Wait 10 seconds to receive the push...")
  time.Sleep(time.Second * 10)
}

// Push push handler
type Push struct {
  erpc.PushCtx
}

// Push handles '/push/status' message
func (p *Push) Status(arg *string) *erpc.Status {
  erpc.Printf("%s", *arg)
  return nil
}

More Examples

Usage

NOTE:

  • It is best to set the packet size when reading: SetReadLimit
  • The default packet size limit when reading is 1 GB

Peer(server or client) Demo

// Start a server
var peer1 = erpc.NewPeer(erpc.PeerConfig{
ListenPort: 9090, // for server role
})
peer1.Listen()

...

// Start a client
var peer2 = erpc.NewPeer(erpc.PeerConfig{})
var sess, err = peer2.Dial("127.0.0.1:8080")

Call-Struct API template

type Aaa struct {
    erpc.CallCtx
}
func (x *Aaa) XxZz(arg *<T>) (<T>, *erpc.Status) {
    ...
    return r, nil
}
  • register it to root router:
// register the call route
// HTTP mapping: /aaa/xx_zz
// RPC mapping: Aaa.XxZz
peer.RouteCall(new(Aaa))

// or register the call route
// HTTP mapping: /xx_zz
// RPC mapping: XxZz
peer.RouteCallFunc((*Aaa).XxZz)

Service method mapping

  • The default mapping(HTTPServiceMethodMapper) of struct(func) name to service methods:

    • AaBb -> /aa_bb
    • ABcXYz -> /abc_xyz
    • Aa__Bb -> /aa_bb
    • aa__bb -> /aa_bb
    • ABC__XYZ -> /abc_xyz
    • Aa_Bb -> /aa/bb
    • aa_bb -> /aa/bb
    • ABC_XYZ -> /abc/xyz go erpc.SetServiceMethodMapper(erpc.HTTPServiceMethodMapper)
  • The mapping(RPCServiceMethodMapper) of struct(func) name to service methods:

    • AaBb -> AaBb
    • ABcXYz -> ABcXYz
    • Aa__Bb -> Aa_Bb
    • aa__bb -> aa_bb
    • ABC__XYZ -> ABC_XYZ
    • Aa_Bb -> Aa.Bb
    • aa_bb -> aa.bb
    • ABC_XYZ -> ABC.XYZ go erpc.SetServiceMethodMapper(erpc.RPCServiceMethodMapper)

Call-Function API template

func XxZz(ctx erpc.CallCtx, arg *<T>) (<T>, *erpc.Status) {
    ...
    return r, nil
}
  • register it to root router:
// register the call route
// HTTP mapping: /xx_zz
// RPC mapping: XxZz
peer.RouteCallFunc(XxZz)

Push-Struct API template

type Bbb struct {
    erpc.PushCtx
}
func (b *Bbb) YyZz(arg *<T>) *erpc.Status {
    ...
    return nil
}
  • register it to root router:
// register the push handler
// HTTP mapping: /bbb/yy_zz
// RPC mapping: Bbb.YyZz
peer.RoutePush(new(Bbb))

// or register the push handler
// HTTP mapping: /yy_zz
// RPC mapping: YyZz
peer.RoutePushFunc((*Bbb).YyZz)

Push-Function API template

// YyZz register the handler
func YyZz(ctx erpc.PushCtx, arg *<T>) *erpc.Status {
    ...
    return nil
}
  • register it to root router:
// register the push handler
// HTTP mapping: /yy_zz
// RPC mapping: YyZz
peer.RoutePushFunc(YyZz)

Unknown-Call-Function API template

func XxxUnknownCall (ctx erpc.UnknownCallCtx) (interface{}, *erpc.Status) {
    ...
    return r, nil
}
  • register it to root router:
// register the unknown call route: /*
peer.SetUnknownCall(XxxUnknownCall)

Unknown-Push-Function API template

func XxxUnknownPush(ctx erpc.UnknownPushCtx) *erpc.Status {
    ...
    return nil
}
  • register it to root router:
// register the unknown push route: /*
peer.SetUnknownPush(XxxUnknownPush)

Plugin Demo

// NewIgnoreCase Returns a ignoreCase plugin.
func NewIgnoreCase() *ignoreCase {
    return &ignoreCase{}
}

type ignoreCase struct{}

var (
    _ erpc.PostReadCallHeaderPlugin = new(ignoreCase)
    _ erpc.PostReadPushHeaderPlugin = new(ignoreCase)
)

func (i *ignoreCase) Name() string {
    return "ignoreCase"
}

func (i *ignoreCase) PostReadCallHeader(ctx erpc.ReadCtx) *erpc.Status {
    // Dynamic transformation path is lowercase
    ctx.UriObject().Path = strings.ToLower(ctx.UriObject().Path)
    return nil
}

func (i *ignoreCase) PostReadPushHeader(ctx erpc.ReadCtx) *erpc.Status {
    // Dynamic transformation path is lowercase
    ctx.UriObject().Path = strings.ToLower(ctx.UriObject().Path)
    return nil
}

Register above handler and plugin

// add router group
group := peer.SubRoute("test")
// register to test group
group.RouteCall(new(Aaa), NewIgnoreCase())
peer.RouteCallFunc(XxZz, NewIgnoreCase())
group.RoutePush(new(Bbb))
peer.RoutePushFunc(YyZz)
peer.SetUnknownCall(XxxUnknownCall)
peer.SetUnknownPush(XxxUnknownPush)

Config

type PeerConfig struct {
    Network            string        `yaml:"network"              ini:"network"              comment:"Network; tcp, tcp4, tcp6, unix, unixpacket, kcp or quic"`
    LocalIP            string        `yaml:"local_ip"             ini:"local_ip"             comment:"Local IP"`
    ListenPort         uint16        `yaml:"listen_port"          ini:"listen_port"          comment:"Listen port; for server role"`
    DialTimeout time.Duration `yaml:"dial_timeout" ini:"dial_timeout" comment:"Default maximum duration for dialing; for client role; ns,µs,ms,s,m,h"`
    RedialTimes        int32         `yaml:"redial_times"         ini:"redial_times"         comment:"The maximum times of attempts to redial, after the connection has been unexpectedly broken; Unlimited when <0; for client role"`
    RedialInterval     time.Duration `yaml:"redial_interval"      ini:"redial_interval"      comment:"Interval of redialing each time, default 100ms; for client role; ns,µs,ms,s,m,h"`
    DefaultBodyCodec   string        `yaml:"default_body_codec"   ini:"default_body_codec"   comment:"Default body codec type id"`
    DefaultSessionAge  time.Duration `yaml:"default_session_age"  ini:"default_session_age"  comment:"Default session max age, if less than or equal to 0, no time limit; ns,µs,ms,s,m,h"`
    DefaultContextAge  time.Duration `yaml:"default_context_age"  ini:"default_context_age"  comment:"Default CALL or PUSH context max age, if less than or equal to 0, no time limit; ns,µs,ms,s,m,h"`
    SlowCometDuration  time.Duration `yaml:"slow_comet_duration"  ini:"slow_comet_duration"  comment:"Slow operation alarm threshold; ns,µs,ms,s ..."`
    PrintDetail        bool          `yaml:"print_detail"         ini:"print_detail"         comment:"Is print body and metadata or not"`
    CountTime          bool          `yaml:"count_time"           ini:"count_time"           comment:"Is count cost time or not"`
}

Optimize

  • SetMessageSizeLimit sets max message size. If maxSize<=0, set it to max uint32.

    go func SetMessageSizeLimit(maxMessageSize uint32)

  • SetSocketKeepAlive sets whether the operating system should send keepalive messages on the connection.

    go func SetSocketKeepAlive(keepalive bool)

  • SetSocketKeepAlivePerio

Extension points exported contracts — how you extend this code

Plugin (Interface)
Plugin plugin background [28 implementers]
plugin.go
Codec (Interface)
Codec makes the body's Encoder and Decoder [6 implementers]
codec/codec.go
PostWebsocketAcceptPlugin (Interface)
PreHandshake executes the PostWebsocketAcceptPlugin after websocket accepting connection [6 implementers]
mixer/websocket/server.go
Proto (Interface)
Proto pack/unpack protocol scheme of socket message. NOTE: Implementation specifications for Message interface should be [8 …
socket/protocol.go
Logger (Interface)
Logger logger interface [4 implementers]
log.go
WriteCtx (Interface)
WriteCtx context method set for writing message. [3 implementers]
context.go
Pong (Interface)
Pong receive heartbeat. [3 implementers]
plugin/heartbeat/pong.go
CallForwarder (Interface)
CallForwarder the object used to call [2 implementers]
plugin/proxy/proxy.go

Core symbols most depended-on inside this repo

Errorf
called by 283
log.go
String
called by 157
socket/message.go
Write
called by 79
socket/socket.go
Close
called by 77
peer.go
Printf
called by 73
log.go
Fatalf
called by 68
log.go
Status
called by 66
context.go
Call
called by 63
session.go

Shape

Method 1,218
Function 524
Struct 182
Interface 72
FuncType 14
TypeAlias 14

Languages

Go100%

Modules by API surface

log.go141 symbols
context.go118 symbols
session.go95 symbols
plugin.go89 symbols
socket/message.go76 symbols
utils/color/color.go58 symbols
examples/bench/msg/benchmark.pb.go56 symbols
socket/socket.go55 symbols
utils/args.go54 symbols
peer.go48 symbols
router.go47 symbols
mixer/websocket/websocket/websocket.go42 symbols

Dependencies from manifests, versioned

github.com/andeya/cfgov0.0.0-2022062615294 · 1×
github.com/andeya/goutilv1.0.0 · 1×
github.com/apache/thriftv0.17.0 · 1×
github.com/go-task/slim-sprigv0.0.0-2023031518552 · 1×
github.com/golang/mockv1.6.0 · 1×
github.com/google/pprofv0.0.0-2022120304183 · 1×
github.com/henrylee2cn/amedav1.5.1 · 1×
github.com/kavu/go_reuseportv1.5.0 · 1×
github.com/klauspost/cpuid/v2v2.2.2 · 1×

For agents

$ claude mcp add erpc \
  -- python -m otcore.mcp_server <graph>

⬇ download graph artifact