AI prompts
base on A hyper application layer networking framework with messaging, rpc, bidirectional-rpc, multiplexer and client-server consistency. <p align=center>
<img src="./docs/geminio.png" width="40%" height="40%">
</p>
<div align="center">
[![Go Reference](https://pkg.go.dev/badge/badge/github.com/singchia/geminio.svg)](https://pkg.go.dev/github.com/singchia/geminio)
[![Go Report Card](https://goreportcard.com/badge/github.com/singchia/geminio)](https://goreportcard.com/report/github.com/singchia/geminio)
[![License](https://img.shields.io/badge/License-Apache_2.0-blue.svg)](https://opensource.org/licenses/Apache-2.0)
![Platform](https://img.shields.io/badge/platform-linux-brightgreen.svg)
![Platform](https://img.shields.io/badge/platform-mac-brightgreen.svg)
![Platform](https://img.shields.io/badge/platform-windows-brightgreen.svg)
English | [简体中文](./README_cn.md)
</div>
## Introduction
Geminio is a library that provides **application-layer** network programming functions, named after [Doubling_Charm](https://harrypotter.fandom.com/wiki/Doubling_Charm).
This library can make network development much easier with comprehensive capabilities like _**RPC**_, _**bidirectional-RPC**_, _**messager**_, _**multi-sessions**_, _**multiplexing**_ and still _**raw-connections**_.
## Architecture
<img src="./docs/biz-arch.png" width="100%" height="100%">
### Interfaces
Most of the library's abstractions are defined in the `geminio.go` file. You can understand the library's concepts by starting from `End` and combining it with the architecture diagram above. Alternatively, you can jump to the usage section below and directly look at the examples.
```golang
type RPCer interface {
NewRequest(data []byte, opts ...*options.NewRequestOptions) Request
Call(ctx context.Context, method string, req Request, opts ...*options.CallOptions) (Response, error)
CallAsync(ctx context.Context, method string, req Request, ch chan *Call, opts ...*options.CallOptions) (*Call, error)
Register(ctx context.Context, method string, rpc RPC) error
}
type Messager interface {
NewMessage(data []byte, opts ...*options.NewMessageOptions) Message
Publish(ctx context.Context, msg Message, opts ...*options.PublishOptions) error
PublishAsync(ctx context.Context, msg Message, ch chan *Publish, opts ...*options.PublishOptions) (*Publish, error)
Receive(ctx context.Context) (Message, error)
}
type Raw net.Conn
type RawRPCMessager interface {
// raw
Raw
// rpc
RPCer
// message
Messager
}
type Stream interface {
// a stream is a geminio
RawRPCMessager
// meta info for a stream
StreamID() uint64
ClientID() uint64
Meta() []byte
}
// Stream multiplexer
type Multiplexer interface {
OpenStream(opts ...*options.OpenStreamOptions) (Stream, error)
AcceptStream() (Stream, error)
ListStreams() []Stream
}
type End interface {
// End is the entry for everything, and it's also a default stream with streamID 1
Stream
// End is a stream multiplexer
Multiplexer
// Close will close all from the End
Close()
}
```
## Features
* **Basic RPC** (registration and invocation)
* **Bidirectional RPC** (registration and invocation on both ends)
* **Message send and acknowledgment** (message consistency guarantee)
* **Synchronous/Asynchronous messaging** (waiting for return, asynchronous waiting)
* **Connection multiplexing** (abstract countless TCP/UDP connections on a single connection)
* **Connection identification** (unique ClientID and StreamID)
* **Native net package compatibility** (support for abstracting net.Conn and net.Listener)
* **High availability** (client's continuous reconnection mechanism)
* **Extensive testing** (stress testing, chaos testing, runtime PProf analysis, etc.)
* ...
## Usage
All usage examples can be found [here](./examples/usage).
### Message
**server:**
```golang
package main
import (
"context"
"github.com/jumboframes/armorigo/log"
"github.com/singchia/geminio/server"
)
func main() {
ln, err := server.Listen("tcp", "127.0.0.1:8080")
if err != nil {
log.Errorf("server listen err: %s", err)
return
}
for {
end, err := ln.AcceptEnd()
if err != nil {
log.Errorf("accept err: %s", err)
break
}
go func() {
msg, err := end.Receive(context.TODO())
if err != nil {
return
}
log.Infof("end receive: %s", string(msg.Data()))
msg.Done()
}()
}
}
```
**client:**
```golang
package main
import (
"context"
"github.com/jumboframes/armorigo/log"
"github.com/singchia/geminio/client"
)
func main() {
end, err := client.NewEnd("tcp", "127.0.0.1:8080")
if err != nil {
log.Errorf("client dial err: %s", err)
return
}
msg := end.NewMessage([]byte("hello"))
err = end.Publish(context.TODO(), msg)
if err != nil {
log.Errorf("end publish err: %s", err)
return
}
end.Close()
}
```
### RPC
**server:**
```golang
package main
import (
"context"
"github.com/jumboframes/armorigo/log"
"github.com/singchia/geminio"
"github.com/singchia/geminio/server"
)
func main() {
ln, err := server.Listen("tcp", "127.0.0.1:8080")
if err != nil {
log.Errorf("server listen err: %s", err)
return
}
for {
end, err := ln.AcceptEnd()
if err != nil {
log.Errorf("accept err: %s", err)
break
}
go func() {
err := end.Register(context.TODO(), "echo", echo)
if err != nil {
return
}
}()
}
}
func echo(_ context.Context, req geminio.Request, rsp geminio.Response) {
rsp.SetData(req.Data())
log.Info("echo:", string(req.Data()))
}
```
**client:**
```golang
package main
import (
"context"
"github.com/jumboframes/armorigo/log"
"github.com/singchia/geminio/client"
)
func main() {
opt := client.NewEndOptions()
opt.SetWaitRemoteRPCs("echo")
end, err := client.NewEnd("tcp", "127.0.0.1:8080", opt)
if err != nil {
log.Errorf("client dial err: %s", err)
return
}
rsp, err := end.Call(context.TODO(), "echo", end.NewRequest([]byte("hello")))
if err != nil {
log.Errorf("end call err: %s", err)
return
}
if string(rsp.Data()) != "hello" {
log.Fatal("wrong echo", string(rsp.Data()))
}
log.Info("echo:", string(rsp.Data()))
end.Close()
}
```
### Bidirectional RPC
**server:**
```golang
package main
import (
"context"
"github.com/jumboframes/armorigo/log"
"github.com/singchia/geminio"
"github.com/singchia/geminio/server"
)
func main() {
opt := server.NewEndOptions()
// the option means all End from server will wait for the rpc registration
opt.SetWaitRemoteRPCs("client-echo")
// pre-register server side method
opt.SetRegisterLocalRPCs(&geminio.MethodRPC{"server-echo", echo})
ln, err := server.Listen("tcp", "127.0.0.1:8080", opt)
if err != nil {
log.Errorf("server listen err: %s", err)
return
}
for {
end, err := ln.AcceptEnd()
if err != nil {
log.Errorf("accept err: %s", err)
break
}
go func() {
// call client side method
rsp, err := end.Call(context.TODO(), "client-echo", end.NewRequest([]byte("foo")))
if err != nil {
log.Errorf("end call err: %s", err)
return
}
if string(rsp.Data()) != "foo" {
log.Fatal("wrong echo", string(rsp.Data()))
}
log.Info("client echo:", string(rsp.Data()))
}()
}
}
func echo(_ context.Context, req geminio.Request, rsp geminio.Response) {
rsp.SetData(req.Data())
log.Info("server echo:", string(req.Data()))
}
```
**clent:**
```golang
package main
import (
"context"
"github.com/jumboframes/armorigo/log"
"github.com/singchia/geminio"
"github.com/singchia/geminio/client"
)
func main() {
opt := client.NewEndOptions()
// the option means all End from server will wait for the rpc registration
opt.SetWaitRemoteRPCs("server-echo")
// pre-register client side method
opt.SetRegisterLocalRPCs(&geminio.MethodRPC{"client-echo", echo})
end, err := client.NewEnd("tcp", "127.0.0.1:8080", opt)
if err != nil {
log.Errorf("client dial err: %s", err)
return
}
// call server side method
rsp, err := end.Call(context.TODO(), "server-echo", end.NewRequest([]byte("bar")))
if err != nil {
log.Errorf("end call err: %s", err)
return
}
if string(rsp.Data()) != "bar" {
log.Fatal("wrong echo", string(rsp.Data()))
}
log.Info("server echo:", string(rsp.Data()))
end.Close()
}
func echo(_ context.Context, req geminio.Request, rsp geminio.Response) {
rsp.SetData(req.Data())
log.Info("client echo:", string(req.Data()))
}
```
### Multiplexer
**server:**
```golang
package main
import (
"github.com/jumboframes/armorigo/log"
"github.com/singchia/geminio/server"
)
func main() {
ln, err := server.Listen("tcp", "127.0.0.1:8080")
if err != nil {
log.Errorf("server listen err: %s", err)
return
}
for {
end, err := ln.AcceptEnd()
if err != nil {
log.Errorf("accept err: %s", err)
break
}
// stream #1, and it's also a net.Conn
sm1, err := end.OpenStream()
if err != nil {
log.Errorf("end open stream err: %s", err)
break
}
sm1.Write([]byte("hello#1"))
sm1.Close()
// stream #2 and it's also a net.Conn
sm2, err := end.OpenStream()
if err != nil {
log.Errorf("end open stream err: %s", err)
break
}
sm2.Write([]byte("hello#2"))
sm2.Close()
}
}
```
**client:**
```golang
package main
import (
"net"
"github.com/jumboframes/armorigo/log"
"github.com/singchia/geminio/client"
)
func main() {
end, err := client.NewEnd("tcp", "127.0.0.1:8080")
if err != nil {
log.Errorf("client dial err: %s", err)
return
}
// the end is also a net.Listener
ln := net.Listener(end)
for {
conn, err := ln.Accept()
if err != nil {
log.Errorf("end accept err: %s", err)
break
}
go func(conn net.Conn) {
buf := make([]byte, 128)
_, err := conn.Read(buf)
if err != nil {
return
}
log.Info("read:", string(buf))
}(conn)
}
end.Close()
}
```
## Examples
* **Message and Acknowledgment** [messager](./examples/messager)
* **Message Queue** [mq](./examples/mq)
* **Chatroom** [chatroom](./examples/chatroom)
* **Relay** [relay](./examples/relay)
* **Intranet Penetration** [traversal](./examples/traversal)
## Test
### Benchmarks
```
goos: darwin
goarch: amd64
pkg: github.com/singchia/geminio/test/bench
cpu: Intel(R) Core(TM) i5-6267U CPU @ 2.90GHz
BenchmarkMessage-4 10117 112584 ns/op 1164.21 MB/s 5764 B/op 181 allocs/op
BenchmarkEnd-4 11644 98586 ns/op 1329.52 MB/s 550534 B/op 73 allocs/op
BenchmarkStream-4 12301 96955 ns/op 1351.88 MB/s 550605 B/op 82 allocs/op
BenchmarkRPC-4 6960 165384 ns/op 792.53 MB/s 38381 B/op 187 allocs/op
PASS
```
## Design
This library is implemented based on the following architecture
<p align=center>
<img src="./docs/design.png" width="80%" height="80%">
</p>
## Contributing
If you find any bugs, please submit the issue, we will respond in a short time.
If you want to contribute new features or help solve project problems, please feel free to submit a PR:
* Maintain consistent code style
* Submit one feature at a time
* Include unit tests with the code you submit
<!-- Copy-paste in your Readme.md file -->
<a href="https://next.ossinsight.io/widgets/official/compose-activity-trends?repo_id=412119706" target="_blank" style="display: block" align="center">
<picture>
<source media="(prefers-color-scheme: dark)" srcset="https://next.ossinsight.io/widgets/official/compose-activity-trends/thumbnail.png?repo_id=412119706&image_size=auto&color_scheme=dark" width="815" height="auto">
<img alt="Activity Trends of singchia/geminio - Last 28 days" src="https://next.ossinsight.io/widgets/official/compose-activity-trends/thumbnail.png?repo_id=412119706&image_size=auto&color_scheme=light" width="815" height="auto">
</picture>
</a>
<!-- Made with [OSS Insight](https://ossinsight.io/) -->
## License
© Austin Zhai, 2023-2030
Released under the [Apache License 2.0](https://github.com/singchia/geminio/blob/main/LICENSE)
", Assign "at most 3 tags" to the expected json: {"id":"7160","tags":[]} "only from the tags list I provide: [{"id":77,"name":"3d"},{"id":89,"name":"agent"},{"id":17,"name":"ai"},{"id":54,"name":"algorithm"},{"id":24,"name":"api"},{"id":44,"name":"authentication"},{"id":3,"name":"aws"},{"id":27,"name":"backend"},{"id":60,"name":"benchmark"},{"id":72,"name":"best-practices"},{"id":39,"name":"bitcoin"},{"id":37,"name":"blockchain"},{"id":1,"name":"blog"},{"id":45,"name":"bundler"},{"id":58,"name":"cache"},{"id":21,"name":"chat"},{"id":49,"name":"cicd"},{"id":4,"name":"cli"},{"id":64,"name":"cloud-native"},{"id":48,"name":"cms"},{"id":61,"name":"compiler"},{"id":68,"name":"containerization"},{"id":92,"name":"crm"},{"id":34,"name":"data"},{"id":47,"name":"database"},{"id":8,"name":"declarative-gui "},{"id":9,"name":"deploy-tool"},{"id":53,"name":"desktop-app"},{"id":6,"name":"dev-exp-lib"},{"id":59,"name":"dev-tool"},{"id":13,"name":"ecommerce"},{"id":26,"name":"editor"},{"id":66,"name":"emulator"},{"id":62,"name":"filesystem"},{"id":80,"name":"finance"},{"id":15,"name":"firmware"},{"id":73,"name":"for-fun"},{"id":2,"name":"framework"},{"id":11,"name":"frontend"},{"id":22,"name":"game"},{"id":81,"name":"game-engine "},{"id":23,"name":"graphql"},{"id":84,"name":"gui"},{"id":91,"name":"http"},{"id":5,"name":"http-client"},{"id":51,"name":"iac"},{"id":30,"name":"ide"},{"id":78,"name":"iot"},{"id":40,"name":"json"},{"id":83,"name":"julian"},{"id":38,"name":"k8s"},{"id":31,"name":"language"},{"id":10,"name":"learning-resource"},{"id":33,"name":"lib"},{"id":41,"name":"linter"},{"id":28,"name":"lms"},{"id":16,"name":"logging"},{"id":76,"name":"low-code"},{"id":90,"name":"message-queue"},{"id":42,"name":"mobile-app"},{"id":18,"name":"monitoring"},{"id":36,"name":"networking"},{"id":7,"name":"node-version"},{"id":55,"name":"nosql"},{"id":57,"name":"observability"},{"id":46,"name":"orm"},{"id":52,"name":"os"},{"id":14,"name":"parser"},{"id":74,"name":"react"},{"id":82,"name":"real-time"},{"id":56,"name":"robot"},{"id":65,"name":"runtime"},{"id":32,"name":"sdk"},{"id":71,"name":"search"},{"id":63,"name":"secrets"},{"id":25,"name":"security"},{"id":85,"name":"server"},{"id":86,"name":"serverless"},{"id":70,"name":"storage"},{"id":75,"name":"system-design"},{"id":79,"name":"terminal"},{"id":29,"name":"testing"},{"id":12,"name":"ui"},{"id":50,"name":"ux"},{"id":88,"name":"video"},{"id":20,"name":"web-app"},{"id":35,"name":"web-server"},{"id":43,"name":"webassembly"},{"id":69,"name":"workflow"},{"id":87,"name":"yaml"}]" returns me the "expected json"