eRPC is an efficient, extensible and easy-to-use RPC framework.
Suitable for RPC, Microservice, Peer-to-Peer, IM, Game and other fields.
go vesion ≥ 1.18
install
GO111MODULE=on go get -u -v -insecure github.com/andeya/erpc/v7
import "github.com/andeya/erpc/v7"
Header
and the Body
Header
contains metadata in the same format as HTTP headerBody
supports for custom codec of Content Type-Like, already implemented:
rawproto
- Default high performance binary protocoljsonproto
- JSON message protocolpbproto
- Ptotobuf message protocolthriftproto
- Thrift message protocolhttproto
- HTTP message protocoltcp
tcp4
tcp6
unix
unixpacket
kcp
quic
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 |
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
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
}
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
}
NOTE:
SetReadLimit
// 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")
type Aaa struct {
erpc.CallCtx
}
func (x *Aaa) XxZz(arg *<T>) (<T>, *erpc.Status) {
...
return r, nil
}
// 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)
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
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
erpc.SetServiceMethodMapper(erpc.RPCServiceMethodMapper)
func XxZz(ctx erpc.CallCtx, arg *<T>) (<T>, *erpc.Status) {
...
return r, nil
}
// register the call route
// HTTP mapping: /xx_zz
// RPC mapping: XxZz
peer.RouteCallFunc(XxZz)
type Bbb struct {
erpc.PushCtx
}
func (b *Bbb) YyZz(arg *<T>) *erpc.Status {
...
return nil
}
// 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)
// YyZz register the handler
func YyZz(ctx erpc.PushCtx, arg *<T>) *erpc.Status {
...
return nil
}
// register the push handler
// HTTP mapping: /yy_zz
// RPC mapping: YyZz
peer.RoutePushFunc(YyZz)
func XxxUnknownCall (ctx erpc.UnknownCallCtx) (interface{}, *erpc.Status) {
...
return r, nil
}
// register the unknown call route: /*
peer.SetUnknownCall(XxxUnknownCall)
func XxxUnknownPush(ctx erpc.UnknownPushCtx) *erpc.Status {
...
return nil
}
// register the unknown push route: /*
peer.SetUnknownPush(XxxUnknownPush)
// 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
}
// 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)
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"`
}
SetMessageSizeLimit sets max message size. If maxSize<=0, set it to max uint32.
func SetMessageSizeLimit(maxMessageSize uint32)
SetSocketKeepAlive sets whether the operating system should send keepalive messages on the connection.
func SetSocketKeepAlive(keepalive bool)
SetSocketKeepAlivePeriod sets period between keep alives.
func SetSocketKeepAlivePeriod(d time.Duration)
SetSocketNoDelay controls whether the operating system should delay message transmission in hopes of sending fewer messages (Nagle's algorithm). The default is true (no delay), meaning that data is sent as soon as possible after a Write.
func SetSocketNoDelay(_noDelay bool)
SetSocketReadBuffer sets the size of the operating system's receive buffer associated with the connection.
func SetSocketReadBuffer(bytes int)
SetSocketWriteBuffer sets the size of the operating system's transmit buffer associated with the connection.
func SetSocketWriteBuffer(bytes int)
package | import | description |
---|---|---|
json | "github.com/andeya/erpc/v7/codec" |
JSON codec(erpc own) |
protobuf | "github.com/andeya/erpc/v7/codec" |
Protobuf codec(erpc own) |
thrift | "github.com/andeya/erpc/v7/codec" |
Form(url encode) codec(erpc own) |
xml | "github.com/andeya/erpc/v7/codec" |
Form(url encode) codec(erpc own) |
plain | "github.com/andeya/erpc/v7/codec" |
Plain text codec(erpc own) |
form | "github.com/andeya/erpc/v7/codec" |
Form(url encode) codec(erpc own) |
package | import | description |
---|---|---|
auth | "github.com/andeya/erpc/v7/plugin/auth" |
An auth plugin for verifying peer at the first time |
binder | "github.com/andeya/erpc/v7/plugin/binder" |
Parameter Binding Verification for Struct Handler |
heartbeat | "github.com/andeya/erpc/v7/plugin/heartbeat" |
A generic timing heartbeat plugin |
proxy | "github.com/andeya/erpc/v7/plugin/proxy" |
A proxy plugin for handling unknown calling or pushing |
secure | "github.com/andeya/erpc/v7/plugin/secure" |
Encrypting/decrypting the message body |
overloader | "github.com/andeya/erpc/v7/plugin/overloader" |
A plugin to protect erpc from overload |
package | import | description |
---|---|---|
rawproto | "github.com/andeya/erpc/v7/proto/rawproto |
A fast socket communication protocol(erpc default protocol) |
jsonproto | "github.com/andeya/erpc/v7/proto/jsonproto" |
A JSON socket communication protocol |
pbproto | "github.com/andeya/erpc/v7/proto/pbproto" |
A Protobuf socket communication protocol |
thriftproto | "github.com/andeya/erpc/v7/proto/thriftproto" |
A Thrift communication protocol |
httproto | "github.com/andeya/erpc/v7/proto/httproto" |
A HTTP style socket communication protocol |
package | import | description |
---|---|---|
gzip | "github.com/andeya/erpc/v7/xfer/gzip" |
Gzip(erpc own) |
md5 | "github.com/andeya/erpc/v7/xfer/md5" |
Provides a integrity check transfer filter |
package | import | description |
---|---|---|
multiclient | "github.com/andeya/erpc/v7/mixer/multiclient" |
Higher throughput client connection pool when transferring large messages (such as downloading files) |
websocket | "github.com/andeya/erpc/v7/mixer/websocket" |
Makes the eRPC framework compatible with websocket protocol as specified in RFC 6455 |
evio | "github.com/andeya/erpc/v7/mixer/evio" |
A fast event-loop networking framework that uses the erpc API layer |
html | html "github.com/xiaoenai/tp-micro/helper/mod-html" |
HTML render for http client |
project | description |
---|---|
TP-Micro | TP-Micro is a simple, powerful micro service framework based on eRPC |
Pholcus | Pholcus is a distributed, high concurrency and powerful web crawler software |
eRPC is under Apache v2 License. See the LICENSE file for the full license text