eRPC is an efficient, extensible and easy-to-use RPC framework.
Suitable for RPC, Microservice, Peer-to-Peer, IM, Game and other fields.
Install
-
go vesion ≥ 1.11
-
install
GO111MODULE=on go get -u -v -insecure github.com/henrylee2cn/erpc/v6
- import
import "github.com/henrylee2cn/erpc/v6"
Feature
HeaderBodyHeaderBodyrawprotojsonprotopbprotothriftprotohttprototcptcp4tcp6unixunixpacketkcpquic
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
| Environment | Throughputs | Mean Latency | P99 Latency |
|---|---|---|---|
- Profile torch of erpc/socket
- Heap torch of erpc/socket
Example
server.go
package main
import (
"fmt"
"time"
"github.com/henrylee2cn/erpc/v6"
)
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/henrylee2cn/erpc/v6"
)
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", "henrylee2cn"),
).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
}
Usage
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
erpc.SetServiceMethodMapper(erpc.HTTPServiceMethodMapper)
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
func SetMessageSizeLimit(maxMessageSize uint32)
func SetSocketKeepAlive(keepalive bool)
func SetSocketKeepAlivePeriod(d time.Duration)
func SetSocketNoDelay(_noDelay bool)
func SetSocketReadBuffer(bytes int)
func SetSocketWriteBuffer(bytes int)
Extensions
Codec
"github.com/henrylee2cn/erpc/v6/codec""github.com/henrylee2cn/erpc/v6/codec""github.com/henrylee2cn/erpc/v6/codec""github.com/henrylee2cn/erpc/v6/codec""github.com/henrylee2cn/erpc/v6/codec""github.com/henrylee2cn/erpc/v6/codec"
Plugin
"github.com/henrylee2cn/erpc/v6/plugin/auth""github.com/henrylee2cn/erpc/v6/plugin/binder""github.com/henrylee2cn/erpc/v6/plugin/heartbeat""github.com/henrylee2cn/erpc/v6/plugin/proxy""github.com/henrylee2cn/erpc/v6/plugin/secure""github.com/henrylee2cn/erpc/v6/plugin/overloader"
Protocol
"github.com/henrylee2cn/erpc/v6/proto/rawproto"github.com/henrylee2cn/erpc/v6/proto/jsonproto""github.com/henrylee2cn/erpc/v6/proto/pbproto""github.com/henrylee2cn/erpc/v6/proto/thriftproto""github.com/henrylee2cn/erpc/v6/proto/httproto"
Transfer-Filter
"github.com/henrylee2cn/erpc/v6/xfer/gzip""github.com/henrylee2cn/erpc/v6/xfer/md5"
Mixer
"github.com/henrylee2cn/erpc/v6/mixer/multiclient""github.com/henrylee2cn/erpc/v6/mixer/websocket""github.com/henrylee2cn/erpc/v6/mixer/evio"html "github.com/xiaoenai/tp-micro/helper/mod-html"
Projects based on eRPC
| project | description |
|---|---|
| TP-Micro is a simple, powerful micro service framework based on eRPC | |
| Pholcus is a distributed, high concurrency and powerful web crawler software |
Business Users
License
eRPC is under Apache v2 License. See the LICENSE file for the full license text