gorpc: simple and fast rpc library for highload projects

892 views
Skip to first unread message

Aliaksandr Valialkin

unread,
Feb 28, 2015, 9:49:07 PM2/28/15
to golan...@googlegroups.com
Hi all,

If you need simple and fast RPC library optimized for high load, then check it out - https://github.com/valyala/gorpc .

The library allowed reducing network bandwidth from 300Mbit/s to 24Mbit/s in production project with 40K qps load.

Gorpc has the following benefits comparing to standard library at http://golang.org/pkg/net/rpc/ :

- It provides simple API.
- It minimizes OS overhead associated with RPC:
  * the amount of data required for transferring over the network by using gob encoding and deflate compression;
  * the number of TCP connections in TIME_WAIT and CLOSE_WAIT states by multiplexing RPC messages over a single connection;
  * the number of send() and recv() syscalls by buffering and batching RPC data before passing it to the OS.
  * the number of network packets required for passing RPC messages.
- It provides counters on the number of bytes transferred over the network and the number of syscalls performed. These counters can be useful for client / server configuration options' tuning.

--
Best Regards,

Aliaksandr

Sokolov Yura

unread,
Mar 2, 2015, 2:23:14 AM3/2/15
to golan...@googlegroups.com
Deflate compression is awful, imo. Why don't u use lz4 or something similar (may be custom)?

Aliaksandr Valialkin

unread,
Mar 3, 2015, 6:32:41 PM3/3/15
to golan...@googlegroups.com


On Monday, March 2, 2015 at 9:23:14 AM UTC+2, Sokolov Yura wrote:
Deflate compression is awful, imo. Why don't u use lz4 or something similar (may be custom)?

I tried using lz4, but gave up, since available libs don't support Flush() functionality, which is required for efficient RPC implementation.
So I chose deflate compression from standard library - it supports Flush() out of the box.
Probably in the future deflate in gorpc will be substituted with something less CPU-hungry :)

Aliaksandr Valialkin

unread,
Mar 3, 2015, 6:36:05 PM3/3/15
to golan...@googlegroups.com
Forgot to say that deflate routines don't show up in the top of CPU profile on our production servers generating 40K qps and 24Mbit/s RPC traffic.
 

rlai...@gmail.com

unread,
Mar 4, 2015, 12:43:37 AM3/4/15
to golan...@googlegroups.com
Great looking lib Aliaksandr!

Is there any way to allow your library to pass in a user supplied net.Listener or tls.Listener? I'm working on building an AGENT -> MASTER automation system where the AGENT acts as a rpc server and the MASTER acts as a rpc client to the agent's rpc server.

Any advise if your library can support this scenario? So far the std golang rpc library is not fitting my bill.

Thanks!




rlai...@gmail.com

unread,
Mar 4, 2015, 1:19:43 AM3/4/15
to golan...@googlegroups.com, rlai...@gmail.com
 My apologies I was not exactly clear:

Looking at your client.go & server.go it appears they both generate and store a var net.Conn privately within the clientHandler and serverHandler methods. It is the net.Conn object I need to supply to gorpc as my program needs to startup an rpc server/client on an already established TLS socket by passing into the rpc client/server the existing net.Conn objects my TLS socket has already generated.

Thanks again.




pierre...@gmail.com

unread,
Mar 4, 2015, 8:19:58 AM3/4/15
to golan...@googlegroups.com
Hello,


I am in the process of finalizing an LZ4 library supporting the LZ4 frame format written 100% in go with builtin concurrency (compress and decompress using multiple go routines).

I do have a Flush method although it is only required if block dependency is enabled and less than 64Kb of data has been written (as this is the only case where the Writer has to buffer).
NB. I find block dependency not adding any value as the compression ratio is almost the same with or without and especially since decompression cannot be done concurrently.
So basically, Flush() would not be required in most cases.

Anyway, the final API I came up with looks like this:
    Constants
    Variables
    func CompressBlock(src, dst []byte, soffset int) (int, error)
    func CompressBlockBound(n int) int
    func CompressBlockHC(src, dst []byte, soffset int) (int, error)
    func UncompressBlock(src, dst []byte, di int) (int, error)
    type Header
    type Reader
        func NewReader(src io.Reader) *Reader
        func (z *Reader) Read(buf []byte) (n int, err error)
        func (z *Reader) Reset(r io.Reader)
        func (z *Reader) WriteTo(w io.Writer) (n int64, err error)
    type Writer
        func NewWriter(dst io.Writer) *Writer
        func (z *Writer) Close() error
        func (z *Writer) Flush() error
        func (z *Writer) ReadFrom(r io.Reader) (n int64, err error)
        func (z *Writer) Reset(w io.Writer)
        func (z *Writer) Write(buf []byte) (n int, err error)


Hopefully you will try it out when I release it (should be very soon...).


Rgds,

Pierre

Aliaksandr Valialkin

unread,
Mar 4, 2015, 10:18:36 AM3/4/15
to golan...@googlegroups.com, rlai...@gmail.com

On Wednesday, March 4, 2015 at 8:19:43 AM UTC+2, rlai...@gmail.com wrote:
 My apologies I was not exactly clear:

Looking at your client.go & server.go it appears they both generate and store a var net.Conn privately within the clientHandler and serverHandler methods. It is the net.Conn object I need to supply to gorpc as my program needs to startup an rpc server/client on an already established TLS socket by passing into the rpc client/server the existing net.Conn objects my TLS socket has already generated.

There are plans for adding user-supplied Dial callback into Client and Accept callback into Server. This will allow using arbitrary underlying transport for RPC. For example:

c := &gorpc.Client{
   
Addr: "rpc.server.addr:12345",
   
Dial: func(c *Client) (conn net.Conn, err error) {
       
if conn, err = net.Dial("tcp", c.Addr); err != nil {
           
return nil, err
       
}
       
return setupTLSAndOtherStuffYouWantForClient(conn)
   
},
}

// setup listener before server start
ln
, err := net.Listen("tcp", ":12345")

s
:= &gorpc.Server{
   
Accept: func(s *gorpc.Server) (conn net.Conn, err error) {
       
if conn, err = ln.Accept(); err != nil {
           
return nil, err
       
}
       
return setupTLSAndOtherStuffYouWantForServer(conn)
   
},
}



Aliaksandr Valialkin

unread,
Mar 4, 2015, 10:20:24 AM3/4/15
to golan...@googlegroups.com, pierre...@gmail.com


On Wednesday, March 4, 2015 at 3:19:58 PM UTC+2, pierre...@gmail.com wrote:
Anyway, the final API I came up with looks like this:
    Constants
    Variables
    func CompressBlock(src, dst []byte, soffset int) (int, error)
    func CompressBlockBound(n int) int
    func CompressBlockHC(src, dst []byte, soffset int) (int, error)
    func UncompressBlock(src, dst []byte, di int) (int, error)
    type Header
    type Reader
        func NewReader(src io.Reader) *Reader
        func (z *Reader) Read(buf []byte) (n int, err error)
        func (z *Reader) Reset(r io.Reader)
        func (z *Reader) WriteTo(w io.Writer) (n int64, err error)
    type Writer
        func NewWriter(dst io.Writer) *Writer
        func (z *Writer) Close() error
        func (z *Writer) Flush() error
        func (z *Writer) ReadFrom(r io.Reader) (n int64, err error)
        func (z *Writer) Reset(w io.Writer)
        func (z *Writer) Write(buf []byte) (n int, err error)


Hopefully you will try it out when I release it (should be very soon...).

Looks great! Will be glad using it instead of deflate.

Aliaksandr Valialkin

unread,
Mar 4, 2015, 12:21:52 PM3/4/15
to golan...@googlegroups.com, rlai...@gmail.com

These plans became reality in master repository - now the Client provides Dial callback and the server provides Listener interface. Arbitrary custom transport can be easily implemented with these additions to gorpc.
 

Aliaksandr Valialkin

unread,
Mar 4, 2015, 1:49:54 PM3/4/15
to golan...@googlegroups.com, rlai...@gmail.com


On Wednesday, March 4, 2015 at 7:21:52 PM UTC+2, Aliaksandr Valialkin wrote:

These plans became reality in master repository - now the Client provides Dial callback and the server provides Listener interface. Arbitrary custom transport can be easily implemented with these additions to gorpc.
 

I just added NewTLSDial() and NewTLSListener() helpers for easier usage of TLS transport. See docs for more info.

rlai...@gmail.com

unread,
Mar 4, 2015, 4:33:45 PM3/4/15
to golan...@googlegroups.com, rlai...@gmail.com
Simply Brilliant :)
Reply all
Reply to author
Forward
0 new messages