Ecosyste.ms: Awesome

An open API service indexing awesome lists of open source software.

Awesome Lists | Featured Topics | Projects

https://github.com/lesismal/arpc

More effective network communication, two-way calling, notify and broadcast supported.
https://github.com/lesismal/arpc

Last synced: 6 days ago
JSON representation

More effective network communication, two-way calling, notify and broadcast supported.

Awesome Lists containing this project

README

        

# ARPC - More Effective Network Communication

[![Slack][1]][2]

[![Mentioned in Awesome Go][3]][4] [![MIT licensed][5]][6] [![Build Status][7]][8] [![Go Report Card][9]][10] [![Coverage Statusd][11]][12]

[1]: https://img.shields.io/badge/join-us%20on%20slack-gray.svg?longCache=true&logo=slack&colorB=green
[2]: https://join.slack.com/t/arpcnbio/shared_invite/zt-1esuz619h-SbKacrYHNdbkNmO9SV~X1A
[3]: https://awesome.re/mentioned-badge-flat.svg
[4]: https://github.com/avelino/awesome-go#distributed-systems
[5]: https://img.shields.io/badge/license-MIT-blue.svg
[6]: LICENSE
[7]: https://img.shields.io/github/actions/workflow/status/lesismal/arpc/build_linux.yml?branch=master&style=flat-square&logo=github-actions
[8]: https://github.com/lesismal/arpc/actions?query=workflow%3build-linux
[9]: https://goreportcard.com/badge/github.com/lesismal/arpc
[10]: https://goreportcard.com/report/github.com/lesismal/arpc
[11]: https://codecov.io/gh/lesismal/arpc/branch/master/graph/badge.svg
[12]: https://codecov.io/gh/lesismal/arpc
[13]: https://godoc.org/github.com/lesismal/arpc?status.svg
[14]: https://godoc.org/github.com/lesismal/arpc

## Contents

- [ARPC - More Effective Network Communication](#arpc---more-effective-network-communication)
- [Contents](#contents)
- [Features](#features)
- [Performance](#performance)
- [Header Layout](#header-layout)
- [Installation](#installation)
- [Quick Start](#quick-start)
- [API Examples](#api-examples)
- [Register Routers](#register-routers)
- [Router Middleware](#router-middleware)
- [Coder Middleware](#coder-middleware)
- [Client Call, CallAsync, Notify](#client-call-callasync-notify)
- [Server Call, CallAsync, Notify](#server-call-callasync-notify)
- [Broadcast - Notify](#broadcast---notify)
- [Async Response](#async-response)
- [Handle New Connection](#handle-new-connection)
- [Handle Disconnected](#handle-disconnected)
- [Handle Client's send queue overstock](#handle-clients-send-queue-overstock)
- [Custom Net Protocol](#custom-net-protocol)
- [Custom Codec](#custom-codec)
- [Custom Logger](#custom-logger)
- [Custom operations before conn's recv and send](#custom-operations-before-conns-recv-and-send)
- [Custom arpc.Client's Reader by wrapping net.Conn](#custom-arpcclients-reader-by-wrapping-netconn)
- [Custom arpc.Client's send queue capacity](#custom-arpcclients-send-queue-capacity)
- [JS Client](#js-client)
- [Web Chat Examples](#web-chat-examples)
- [Pub/Sub Examples](#pubsub-examples)
- [More Examples](#more-examples)

## Features
- [x] Two-Way Calling
- [x] Two-Way Notify
- [x] Sync and Async Calling
- [x] Sync and Async Response
- [x] Batch Write | Writev | net.Buffers
- [x] Broadcast
- [x] Middleware
- [x] Pub/Sub
- [x] [Opentracing](https://github.com/opentracing/opentracing-go)

| Pattern | Interactive Directions | Description |
| ------- | ---------------------------- | ------------------------ |
| call | two-way:
c -> s
s -> c | request and response |
| notify | two-way:
c -> s
s -> c | request without response |

## Performance

Here are some thirdparty benchmark including arpc, **although these repos have provide the performance report, but I suggest you run the code yourself and get the real result, other than just believe other people's doc**:
- [go-rpc-framework-benchmark](https://github.com/micro-svc/go-rpc-framework-benchmark)
- [rpcx-benchmark](https://github.com/rpcxio/rpcx-benchmark)
- [kitex-benchmark](https://github.com/cloudwego/kitex-benchmark)

## Header Layout

- LittleEndian

| bodyLen | reserved | cmd | flag | methodLen | sequence | method | body |
| ------- | -------- | ------ | ------- | --------- | -------- | --------------- | ----------------------- |
| 4 bytes | 1 byte | 1 byte | 1 bytes | 1 bytes | 8 bytes | methodLen bytes | bodyLen-methodLen bytes |

## Installation

1. Get and install arpc

```sh
$ go get -u github.com/lesismal/arpc
```

2. Import in your code:

```go
import "github.com/lesismal/arpc"
```

## Quick Start

- start a [server](https://github.com/lesismal/arpc/blob/master/examples/rpc/server/server.go)

```go
package main

import "github.com/lesismal/arpc"

func main() {
server := arpc.NewServer()

// register router
server.Handler.Handle("/echo", func(ctx *arpc.Context) {
str := ""
if err := ctx.Bind(&str); err == nil {
ctx.Write(str)
}
})

server.Run("localhost:8888")
}
```

- start a [client](https://github.com/lesismal/arpc/blob/master/examples/rpc/client/client.go)

```go
package main

import (
"log"
"net"
"time"

"github.com/lesismal/arpc"
)

func main() {
client, err := arpc.NewClient(func() (net.Conn, error) {
return net.DialTimeout("tcp", "localhost:8888", time.Second*3)
})
if err != nil {
panic(err)
}
defer client.Stop()

req := "hello"
rsp := ""
err = client.Call("/echo", &req, &rsp, time.Second*5)
if err != nil {
log.Fatalf("Call failed: %v", err)
} else {
log.Printf("Call Response: \"%v\"", rsp)
}
}
```

## API Examples

### Register Routers

```golang
var handler arpc.Handler

// package
handler = arpc.DefaultHandler
// server
handler = server.Handler
// client
handler = client.Handler

// message would be default handled one by one in the same conn reader goroutine
handler.Handle("/route", func(ctx *arpc.Context) { ... })
handler.Handle("/route2", func(ctx *arpc.Context) { ... })

// this make message handled by a new goroutine
async := true
handler.Handle("/asyncResponse", func(ctx *arpc.Context) { ... }, async)
```

### Router Middleware

See [router middleware](https://github.com/lesismal/arpc/tree/master/extension/middleware/router), it's easy to implement middlewares yourself

```golang
import "github.com/lesismal/arpc/extension/middleware/router"

var handler arpc.Handler

// package
handler = arpc.DefaultHandler
// server
handler = server.Handler
// client
handler = client.Handler

handler.Use(router.Recover())
handler.Use(router.Logger())
handler.Use(func(ctx *arpc.Context) { ... })
handler.Handle("/echo", func(ctx *arpc.Context) { ... })
handler.Use(func(ctx *arpc.Context) { ... })
```

### Coder Middleware

- Coder Middleware is used for converting a message data to your designed format, e.g encrypt/decrypt and compress/uncompress

```golang
import "github.com/lesismal/arpc/extension/middleware/coder/gzip"

var handler arpc.Handler

// package
handler = arpc.DefaultHandler
// server
handler = server.Handler
// client
handler = client.Handler

handler.UseCoder(gzip.New())
handler.Handle("/echo", func(ctx *arpc.Context) { ... })
```

### Client Call, CallAsync, Notify

1. Call (Block, with timeout/context)

```golang
request := &Echo{...}
response := &Echo{}
timeout := time.Second*5
err := client.Call("/call/echo", request, response, timeout)
// ctx, cancel := context.WithTimeout(context.Background(), time.Second)
// defer cancel()
// err := client.CallWith(ctx, "/call/echo", request, response)
```

2. CallAsync (Nonblock, with callback and timeout/context)

```golang
request := &Echo{...}

timeout := time.Second*5
err := client.CallAsync("/call/echo", request, func(ctx *arpc.Context) {
response := &Echo{}
ctx.Bind(response)
...
}, timeout)
```

3. Notify (same as CallAsync with timeout/context, without callback)

```golang
data := &Notify{...}
client.Notify("/notify", data, time.Second)
// ctx, cancel := context.WithTimeout(context.Background(), time.Second)
// defer cancel()
// client.NotifyWith(ctx, "/notify", data)
```

### Server Call, CallAsync, Notify

1. Get client and keep it in your application

```golang
var client *arpc.Client
server.Handler.Handle("/route", func(ctx *arpc.Context) {
client = ctx.Client
// release client
client.OnDisconnected(func(c *arpc.Client){
client = nil
})
})

go func() {
for {
time.Sleep(time.Second)
if client != nil {
client.Call(...)
client.CallAsync(...)
client.Notify(...)
}
}
}()
```

2. Then Call/CallAsync/Notify

- [See Previous](#client-call-callasync-notify)

### Broadcast - Notify

- for more details: [**server**](https://github.com/lesismal/arpc/blob/master/examples/broadcast/server/server.go) [**client**](https://github.com/lesismal/arpc/blob/master/examples/broadcast/client/client.go)

```golang
var mux = sync.RWMutex{}
var clientMap = make(map[*arpc.Client]struct{})

func broadcast() {
var svr *arpc.Server = ...
msg := svr.NewMessage(arpc.CmdNotify, "/broadcast", fmt.Sprintf("broadcast msg %d", i))
mux.RLock()
for client := range clientMap {
client.PushMsg(msg, arpc.TimeZero)
}
mux.RUnlock()
}
```

### Async Response

```golang
var handler arpc.Handler

// package
handler = arpc.DefaultHandler
// server
handler = server.Handler
// client
handler = client.Handler

asyncResponse := true // default is true, or set false
handler.Handle("/echo", func(ctx *arpc.Context) {
req := ...
err := ctx.Bind(req)
if err == nil {
ctx.Write(data)
}
}, asyncResponse)
```

### Handle New Connection

```golang
// package
arpc.DefaultHandler.HandleConnected(func(c *arpc.Client) {
...
})

// server
svr := arpc.NewServer()
svr.Handler.HandleConnected(func(c *arpc.Client) {
...
})

// client
client, err := arpc.NewClient(...)
client.Handler.HandleConnected(func(c *arpc.Client) {
...
})
```

### Handle Disconnected

```golang
// package
arpc.DefaultHandler.HandleDisconnected(func(c *arpc.Client) {
...
})

// server
svr := arpc.NewServer()
svr.Handler.HandleDisconnected(func(c *arpc.Client) {
...
})

// client
client, err := arpc.NewClient(...)
client.Handler.HandleDisconnected(func(c *arpc.Client) {
...
})
```

### Handle Client's send queue overstock

```golang
// package
arpc.DefaultHandler.HandleOverstock(func(c *arpc.Client) {
...
})

// server
svr := arpc.NewServer()
svr.Handler.HandleOverstock(func(c *arpc.Client) {
...
})

// client
client, err := arpc.NewClient(...)
client.Handler.HandleOverstock(func(c *arpc.Client) {
...
})
```

### Custom Net Protocol

```golang
// server
var ln net.Listener = ...
svr := arpc.NewServer()
svr.Serve(ln)

// client
dialer := func() (net.Conn, error) {
return ...
}
client, err := arpc.NewClient(dialer)
```

### Custom Codec

```golang
import "github.com/lesismal/arpc/codec"

var codec arpc.Codec = ...

// package
codec.Defaultcodec = codec

// server
svr := arpc.NewServer()
svr.Codec = codec

// client
client, err := arpc.NewClient(...)
client.Codec = codec
```

### Custom Logger

```golang
import "github.com/lesismal/arpc/log"

var logger arpc.Logger = ...
log.SetLogger(logger) // log.DefaultLogger = logger
```

### Custom operations before conn's recv and send

```golang
arpc.DefaultHandler.BeforeRecv(func(conn net.Conn) error) {
// ...
})

arpc.DefaultHandler.BeforeSend(func(conn net.Conn) error) {
// ...
})
```

### Custom arpc.Client's Reader by wrapping net.Conn

```golang
arpc.DefaultHandler.SetReaderWrapper(func(conn net.Conn) io.Reader) {
// ...
})
```

### Custom arpc.Client's send queue capacity

```golang
arpc.DefaultHandler.SetSendQueueSize(4096)
```

## JS Client

- See [arpc.js](https://github.com/lesismal/arpc/blob/master/extension/jsclient/arpc.js)

## Web Chat Examples

- See [webchat](https://github.com/lesismal/arpc/tree/master/examples/webchat)

## Pub/Sub Examples

- start a server
```golang
import "github.com/lesismal/arpc/extension/pubsub"

var (
address = "localhost:8888"

password = "123qwe"

topicName = "Broadcast"
)

func main() {
s := pubsub.NewServer()
s.Password = password

// server publish to all clients
go func() {
for i := 0; true; i++ {
time.Sleep(time.Second)
s.Publish(topicName, fmt.Sprintf("message from server %v", i))
}
}()

s.Run(address)
}
```

- start a subscribe client
```golang
import "github.com/lesismal/arpc/log"
import "github.com/lesismal/arpc/extension/pubsub"

var (
address = "localhost:8888"

password = "123qwe"

topicName = "Broadcast"
)

func onTopic(topic *pubsub.Topic) {
log.Info("[OnTopic] [%v] \"%v\", [%v]",
topic.Name,
string(topic.Data),
time.Unix(topic.Timestamp/1000000000, topic.Timestamp%1000000000).Format("2006-01-02 15:04:05.000"))
}

func main() {
client, err := pubsub.NewClient(func() (net.Conn, error) {
return net.DialTimeout("tcp", address, time.Second*3)
})
if err != nil {
panic(err)
}
client.Password = password

// authentication
err = client.Authenticate()
if err != nil {
panic(err)
}

// subscribe topic
if err := client.Subscribe(topicName, onTopic, time.Second); err != nil {
panic(err)
}

<-make(chan int)
}
```

- start a publish client
```golang
import "github.com/lesismal/arpc/extension/pubsub"

var (
address = "localhost:8888"

password = "123qwe"

topicName = "Broadcast"
)

func main() {
client, err := pubsub.NewClient(func() (net.Conn, error) {
return net.DialTimeout("tcp", address, time.Second*3)
})
if err != nil {
panic(err)
}
client.Password = password

// authentication
err = client.Authenticate()
if err != nil {
panic(err)
}

for i := 0; true; i++ {
if i%5 == 0 {
// publish msg to all clients
client.Publish(topicName, fmt.Sprintf("message from client %d", i), time.Second)
} else {
// publish msg to only one client
client.PublishToOne(topicName, fmt.Sprintf("message from client %d", i), time.Second)
}
time.Sleep(time.Second)
}
}
```

## More Examples

- See [examples](https://github.com/lesismal/arpc/tree/master/examples)