rony

package module
v0.0.41 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Dec 16, 2020 License: BSD-3-Clause Imports: 13 Imported by: 23

README

RONY (Fast and Scalable RPC Framework)


Rony lets you create a clustered aware service easily. Basically when your write a service with Rony framework, you develop two interfaces one is for the client side which clients of your service connect and communicate. We call it 'edge'. The other interface communicates with other instances in the cluster.

In Rony we support two types of cluster mode at the same time. Replicated instances or sharded instances. In replicated cluster, the nodes in the cluster are replicas of each other. There is only one leader at a time. This is done by implementing Raft protocol under the hood. In sharded cluster nodes are responsible for different sets of data. This could achieve by using different replicaSet (i.e. any number between [1..2^31]). The logic that how data distributed between different replica sets depends on your own service.

When we want to write a service to support sharding and replication, developers need to write a lot of code to manage their communication and how to write the code to handle these interactions between services and clients. Rony makes it easy. Rony internally uses Raft for consensuses
However, to develop your service using Rony framework, there are more works to initialize your code comparing to other RESTful frameworks. To help you setup the skeleton even faster there is an executable to makes the life easier for you.

Installation

Use go get to install the Go client-and-server generator:

go get -u github.com/ronaksoft/rony/cmd/rony
go get -u github.com/ronaksoft/rony/cmd/protoc-gen-rony

You will also need:

  • protoc, the protobuf compiler. You need version 3+.
  • github.com/golang/protobuf/protoc-gen-go, the Go protobuf generator plugin. Get this with go get
Getting Started

After we have installed two executable files protoc-gen-rony and rony you are ready to create your project.

mkdir sample-project
cd ./sample-project
rony project create --project.name github.com/ronaksoft/sample --project.name sample-project

After running the 'project create' command, you should go to the service directory and open service.proto file and write the appropriate protobuf file. You can also go to model directory and edit the model.proto file, but it is an advanced topic. we just delete it. After finishing our edit job we run the following command from the root of our project.

rony project gen-proto

The above command generates the boiler plate codes for our service.

Features
  1. Gossip Protocol
  2. Raft
  3. ProtocolBuffer friendly rpc
  4. Multiple gateways: Websocket, Http, Quic

Performance

Rony is very fast and with very low overhead. In a non-raft environment it adds < 25us latency, and in raft-enabled scenario around 1ms latency.

Rony has negligible memory footprint by reusing buffers and pooling.

BenchmarkEdgeServerMessageSerial
BenchmarkEdgeServerMessageSerial-16                       901370              1195 ns/op              87 B/op          2 allocs/op
BenchmarkEdgeServerMessageParallel
BenchmarkEdgeServerMessageParallel-16                    4521645               272 ns/op              83 B/op          2 allocs/op
BenchmarkEdgeServerWithRaftMessageSerial
BenchmarkEdgeServerWithRaftMessageSerial-16                 9541            132065 ns/op            5034 B/op        116 allocs/op
BenchmarkEdgeServerWithRaftMessageParallel
BenchmarkEdgeServerWithRaftMessageParallel-16             124658              8438 ns/op            4462 B/op         51 allocs/op

BenchmarkServerWithWebsocket-16            46514             25138 ns/op             691 B/op         19 allocs/op

Easy Setup for advanced scenarios
package main 
import "github.com/ronaksoft/rony"

func main() {
    server := rony.NewEdgeServer(serverID, &dispatcher{},
    	rony.WithTcpGateway(tcpGateway.Config{
    		NewConnectionWorkers: 10,
    		MaxConcurrency:       1000,
    		MaxIdleTime:          0,
    		ListenAddress:        "0.0.0.0:0",
    	}),
    	rony.WithDataPath(filepath.Join("./_hdd", serverID)),
    	rony.WithReplicaSet(100, port*10, bootstrap),
    	rony.WithGossipPort(port),
    )
 
    server.AddHandler(msg.C_EchoRequest, EchoHandler)
    
    server.Run()
}

func EchoHandler(ctx *context.Context, in *msg.MessageEnvelope) {
    req := msg.EchoRequest {}
    res := msg.EchoResponse {}
    _ = req.Unmarshal(in.Message)

    res.Bool = req.Bool
    	res.Int = req.Int
    	res.Timestamp = time.Now().UnixNano()
    	res.Delay = res.Timestamp - req.Timestamp
    
    ctx.PushMessage(ctx.AuthID, in.RequestID, msg.C_EchoResponse, res)
}

This code does not run, please check example directory for working examples

Redirect Handling
Code Generators

You must install the following protoc plugins which generate the appropriate codes

  1. protoc-gen-gorony
go get -u github.com/ronaksoft/rony/cmd/protoc-gen-rony

This is a protoc pluging which could generate codes based on the protobuf. In addition to
protobuffer syntax there are directive commands available which let the user generate more
customized code. 


Shoulders

Rony is made of big and popular packages. Without these great libraries building Rony was not possible.

Contribution

We need to make a clear and understandable documentation for Rony, so any help would be appreciated. We also appreciate benchmarking Rony against other platforms for common scenarios and will be cited.

TODOs
  • Model Descriptor implementation for:
    1. Scylla
    2. MongoDB
    3. Redis
    4. Aerospike
  • Middleware support for server side rpc handlers
  • Update documentation
  • Improve test coverage
  • Support Actor model for communication between nodes
  • CLI client generator, make testing server scenarios handy

Documentation

Index

Constants

View Source
const (
	ErrCodeInternal         = "E00" // When Error is Unknown or it is internal and should not be exposed to the client
	ErrCodeInvalid          = "E01"
	ErrCodeUnavailable      = "E02"
	ErrCodeTooMany          = "E03"
	ErrCodeTooFew           = "E04"
	ErrCodeIncomplete       = "E05"
	ErrCodeTimeout          = "E06"
	ErrCodeAccess           = "E07"
	ErrCodeAlreadyExists    = "E08"
	ErrCodeBusy             = "E09"
	ErrCodeOutOfRange       = "E10"
	ErrCodePartiallyApplied = "E11"
	ErrCodeExpired          = "E12"
	ErrCodeNotImplemented   = "E13"
)

Error Codes

View Source
const (
	ErrItemServer     = "SERVER"
	ErrItemRaftLeader = "RAFT_LEADER"
	ErrItemHandler    = "HANDLER"
	ErrItemRequest    = "REQUEST"
)

Error Items

View Source
const C_ClusterMessage int64 = 1078766375
View Source
const C_EdgeNode int64 = 999040174
View Source
const C_Error int64 = 2619118453
View Source
const C_KeyValue int64 = 4276272820
View Source
const C_MessageContainer int64 = 1972016308
View Source
const C_MessageEnvelope int64 = 535232465
View Source
const C_RaftCommand int64 = 2919813429
View Source
const C_Redirect int64 = 981138557

Variables

View Source
var (
	ErrGatewayAlreadyInitialized = errors.New("gateway already initialized")
	ErrNotFound                  = errors.New("not found")
	ErrNotRaftLeader             = errors.New("not raft leader")
	ErrRaftNotSet                = errors.New("raft not set")
	ErrRaftExecuteOnLeader       = errors.New("raft execute on leader")
	ErrRetriesExceeded           = wrapError("maximum retries exceeded")
)

Errors

View Source
var (
	RaftState_name = map[int32]string{
		0: "None",
		1: "Follower",
		2: "Candidate",
		3: "Leader",
		4: "Shutdown",
	}
	RaftState_value = map[string]int32{
		"None":      0,
		"Follower":  1,
		"Candidate": 2,
		"Leader":    3,
		"Shutdown":  4,
	}
)

Enum value maps for RaftState.

View Source
var (
	ConstructorNames = map[int64]string{}
)
View Source
var PoolClusterMessage = poolClusterMessage{}
View Source
var PoolEdgeNode = poolEdgeNode{}
View Source
var PoolError = poolError{}
View Source
var PoolKeyValue = poolKeyValue{}
View Source
var PoolMessageContainer = poolMessageContainer{}
View Source
var PoolMessageEnvelope = poolMessageEnvelope{}
View Source
var PoolRaftCommand = poolRaftCommand{}
View Source
var PoolRedirect = poolRedirect{}

Functions

func ConstructorOf

func ConstructorOf(x interface{}) int64

func ErrorMessage

func ErrorMessage(out *MessageEnvelope, reqID uint64, errCode, errItem string)

func SetLogLevel

func SetLogLevel(l int)

SetLogLevel is used for debugging purpose -1 : DEBUG 0 : INFO 1 : WARN 2 : ERROR

Types

type ClusterMessage

type ClusterMessage struct {
	Sender   []byte           `protobuf:"bytes,1,opt,name=Sender,proto3" json:"Sender,omitempty"`
	Store    []*KeyValue      `protobuf:"bytes,2,rep,name=Store,proto3" json:"Store,omitempty"`
	Envelope *MessageEnvelope `protobuf:"bytes,3,opt,name=Envelope,proto3" json:"Envelope,omitempty"`
	// contains filtered or unexported fields
}

ClusterMessage

func (*ClusterMessage) DeepCopy

func (x *ClusterMessage) DeepCopy(z *ClusterMessage)

func (*ClusterMessage) Descriptor deprecated

func (*ClusterMessage) Descriptor() ([]byte, []int)

Deprecated: Use ClusterMessage.ProtoReflect.Descriptor instead.

func (*ClusterMessage) Fill

func (x *ClusterMessage) Fill(senderID []byte, e *MessageEnvelope, kvs ...*KeyValue)

func (*ClusterMessage) GetEnvelope

func (x *ClusterMessage) GetEnvelope() *MessageEnvelope

func (*ClusterMessage) GetSender

func (x *ClusterMessage) GetSender() []byte

func (*ClusterMessage) GetStore

func (x *ClusterMessage) GetStore() []*KeyValue

func (*ClusterMessage) Marshal added in v0.0.17

func (x *ClusterMessage) Marshal() ([]byte, error)

func (*ClusterMessage) ProtoMessage

func (*ClusterMessage) ProtoMessage()

func (*ClusterMessage) ProtoReflect

func (x *ClusterMessage) ProtoReflect() protoreflect.Message

func (*ClusterMessage) Reset

func (x *ClusterMessage) Reset()

func (*ClusterMessage) String

func (x *ClusterMessage) String() string

func (*ClusterMessage) Unmarshal

func (x *ClusterMessage) Unmarshal(b []byte) error

type EdgeNode

type EdgeNode struct {
	ServerID      []byte    `protobuf:"bytes,1,opt,name=ServerID,proto3" json:"ServerID,omitempty"`
	ReplicaSet    uint64    `protobuf:"varint,2,opt,name=ReplicaSet,proto3" json:"ReplicaSet,omitempty"`
	ShardRangeMin uint32    `protobuf:"varint,3,opt,name=ShardRangeMin,proto3" json:"ShardRangeMin,omitempty"`
	ShardRangeMax uint32    `protobuf:"varint,4,opt,name=ShardRangeMax,proto3" json:"ShardRangeMax,omitempty"`
	RaftPort      uint32    `protobuf:"varint,5,opt,name=RaftPort,proto3" json:"RaftPort,omitempty"`
	RaftState     RaftState `protobuf:"varint,6,opt,name=RaftState,proto3,enum=rony.RaftState" json:"RaftState,omitempty"`
	GatewayAddr   []string  `protobuf:"bytes,7,rep,name=GatewayAddr,proto3" json:"GatewayAddr,omitempty"`
	// contains filtered or unexported fields
}

EdgeNode

func (*EdgeNode) DeepCopy

func (x *EdgeNode) DeepCopy(z *EdgeNode)

func (*EdgeNode) Descriptor deprecated

func (*EdgeNode) Descriptor() ([]byte, []int)

Deprecated: Use EdgeNode.ProtoReflect.Descriptor instead.

func (*EdgeNode) GetGatewayAddr

func (x *EdgeNode) GetGatewayAddr() []string

func (*EdgeNode) GetRaftPort

func (x *EdgeNode) GetRaftPort() uint32

func (*EdgeNode) GetRaftState

func (x *EdgeNode) GetRaftState() RaftState

func (*EdgeNode) GetReplicaSet

func (x *EdgeNode) GetReplicaSet() uint64

func (*EdgeNode) GetServerID

func (x *EdgeNode) GetServerID() []byte

func (*EdgeNode) GetShardRangeMax

func (x *EdgeNode) GetShardRangeMax() uint32

func (*EdgeNode) GetShardRangeMin

func (x *EdgeNode) GetShardRangeMin() uint32

func (*EdgeNode) Marshal added in v0.0.17

func (x *EdgeNode) Marshal() ([]byte, error)

func (*EdgeNode) ProtoMessage

func (*EdgeNode) ProtoMessage()

func (*EdgeNode) ProtoReflect

func (x *EdgeNode) ProtoReflect() protoreflect.Message

func (*EdgeNode) Reset

func (x *EdgeNode) Reset()

func (*EdgeNode) String

func (x *EdgeNode) String() string

func (*EdgeNode) Unmarshal

func (x *EdgeNode) Unmarshal(b []byte) error

type Error

type Error struct {
	Code               string   `protobuf:"bytes,1,opt,name=Code,proto3" json:"Code,omitempty"`
	Items              string   `protobuf:"bytes,2,opt,name=Items,proto3" json:"Items,omitempty"`
	Template           string   `protobuf:"bytes,3,opt,name=Template,proto3" json:"Template,omitempty"`
	TemplateItems      []string `protobuf:"bytes,4,rep,name=TemplateItems,proto3" json:"TemplateItems,omitempty"`
	LocalTemplate      string   `protobuf:"bytes,5,opt,name=LocalTemplate,proto3" json:"LocalTemplate,omitempty"`
	LocalTemplateItems []string `protobuf:"bytes,6,rep,name=LocalTemplateItems,proto3" json:"LocalTemplateItems,omitempty"`
	// contains filtered or unexported fields
}

Error

func (*Error) DeepCopy

func (x *Error) DeepCopy(z *Error)

func (*Error) Descriptor deprecated

func (*Error) Descriptor() ([]byte, []int)

Deprecated: Use Error.ProtoReflect.Descriptor instead.

func (*Error) GetCode

func (x *Error) GetCode() string

func (*Error) GetItems

func (x *Error) GetItems() string

func (*Error) GetLocalTemplate

func (x *Error) GetLocalTemplate() string

func (*Error) GetLocalTemplateItems

func (x *Error) GetLocalTemplateItems() []string

func (*Error) GetTemplate

func (x *Error) GetTemplate() string

func (*Error) GetTemplateItems

func (x *Error) GetTemplateItems() []string

func (*Error) Marshal added in v0.0.17

func (x *Error) Marshal() ([]byte, error)

func (*Error) ProtoMessage

func (*Error) ProtoMessage()

func (*Error) ProtoReflect

func (x *Error) ProtoReflect() protoreflect.Message

func (*Error) Reset

func (x *Error) Reset()

func (*Error) String

func (x *Error) String() string

func (*Error) Unmarshal

func (x *Error) Unmarshal(b []byte) error

type KeyValue

type KeyValue struct {
	Key   string `protobuf:"bytes,1,opt,name=Key,proto3" json:"Key,omitempty"`
	Value string `protobuf:"bytes,2,opt,name=Value,proto3" json:"Value,omitempty"`
	// contains filtered or unexported fields
}

KeyValue

func (*KeyValue) DeepCopy

func (x *KeyValue) DeepCopy(z *KeyValue)

func (*KeyValue) Descriptor deprecated

func (*KeyValue) Descriptor() ([]byte, []int)

Deprecated: Use KeyValue.ProtoReflect.Descriptor instead.

func (*KeyValue) GetKey

func (x *KeyValue) GetKey() string

func (*KeyValue) GetValue

func (x *KeyValue) GetValue() string

func (*KeyValue) Marshal added in v0.0.17

func (x *KeyValue) Marshal() ([]byte, error)

func (*KeyValue) ProtoMessage

func (*KeyValue) ProtoMessage()

func (*KeyValue) ProtoReflect

func (x *KeyValue) ProtoReflect() protoreflect.Message

func (*KeyValue) Reset

func (x *KeyValue) Reset()

func (*KeyValue) String

func (x *KeyValue) String() string

func (*KeyValue) Unmarshal

func (x *KeyValue) Unmarshal(b []byte) error

type MessageContainer

type MessageContainer struct {
	Length    int32              `protobuf:"varint,1,opt,name=Length,proto3" json:"Length,omitempty"`
	Envelopes []*MessageEnvelope `protobuf:"bytes,2,rep,name=Envelopes,proto3" json:"Envelopes,omitempty"`
	// contains filtered or unexported fields
}

MessageContainer This type of message will be used to send multi messages inside a single container message

func (*MessageContainer) DeepCopy

func (x *MessageContainer) DeepCopy(z *MessageContainer)

func (*MessageContainer) Descriptor deprecated

func (*MessageContainer) Descriptor() ([]byte, []int)

Deprecated: Use MessageContainer.ProtoReflect.Descriptor instead.

func (*MessageContainer) GetEnvelopes

func (x *MessageContainer) GetEnvelopes() []*MessageEnvelope

func (*MessageContainer) GetLength

func (x *MessageContainer) GetLength() int32

func (*MessageContainer) Marshal added in v0.0.17

func (x *MessageContainer) Marshal() ([]byte, error)

func (*MessageContainer) ProtoMessage

func (*MessageContainer) ProtoMessage()

func (*MessageContainer) ProtoReflect

func (x *MessageContainer) ProtoReflect() protoreflect.Message

func (*MessageContainer) Reset

func (x *MessageContainer) Reset()

func (*MessageContainer) String

func (x *MessageContainer) String() string

func (*MessageContainer) Unmarshal

func (x *MessageContainer) Unmarshal(b []byte) error

type MessageEnvelope

type MessageEnvelope struct {
	Constructor int64       `protobuf:"varint,1,opt,name=Constructor,proto3" json:"Constructor,omitempty"`
	RequestID   uint64      `protobuf:"fixed64,2,opt,name=RequestID,proto3" json:"RequestID,omitempty"`
	Message     []byte      `protobuf:"bytes,4,opt,name=Message,proto3" json:"Message,omitempty"`
	Auth        []byte      `protobuf:"bytes,8,opt,name=Auth,proto3" json:"Auth,omitempty"`
	Header      []*KeyValue `protobuf:"bytes,10,rep,name=Header,proto3" json:"Header,omitempty"`
	// contains filtered or unexported fields
}

MessageEnvelope This type of message will be used to contain another ProtoBuffer Message inside

func (*MessageEnvelope) Clone

func (x *MessageEnvelope) Clone() *MessageEnvelope

func (*MessageEnvelope) DeepCopy

func (x *MessageEnvelope) DeepCopy(z *MessageEnvelope)

func (*MessageEnvelope) Descriptor deprecated

func (*MessageEnvelope) Descriptor() ([]byte, []int)

Deprecated: Use MessageEnvelope.ProtoReflect.Descriptor instead.

func (*MessageEnvelope) Fill

func (x *MessageEnvelope) Fill(reqID uint64, constructor int64, p proto.Message, kvs ...*KeyValue)

func (*MessageEnvelope) Get added in v0.0.18

func (x *MessageEnvelope) Get(key, defaultVal string) string

func (*MessageEnvelope) GetAuth

func (x *MessageEnvelope) GetAuth() []byte

func (*MessageEnvelope) GetConstructor

func (x *MessageEnvelope) GetConstructor() int64

func (*MessageEnvelope) GetHeader

func (x *MessageEnvelope) GetHeader() []*KeyValue

func (*MessageEnvelope) GetMessage

func (x *MessageEnvelope) GetMessage() []byte

func (*MessageEnvelope) GetRequestID

func (x *MessageEnvelope) GetRequestID() uint64

func (*MessageEnvelope) Marshal added in v0.0.17

func (x *MessageEnvelope) Marshal() ([]byte, error)

func (*MessageEnvelope) ProtoMessage

func (*MessageEnvelope) ProtoMessage()

func (*MessageEnvelope) ProtoReflect

func (x *MessageEnvelope) ProtoReflect() protoreflect.Message

func (*MessageEnvelope) Reset

func (x *MessageEnvelope) Reset()

func (*MessageEnvelope) Set added in v0.0.18

func (x *MessageEnvelope) Set(KVs ...*KeyValue)

func (*MessageEnvelope) String

func (x *MessageEnvelope) String() string

func (*MessageEnvelope) Unmarshal

func (x *MessageEnvelope) Unmarshal(b []byte) error

type RaftCommand

type RaftCommand struct {
	Sender   []byte           `protobuf:"bytes,1,opt,name=Sender,proto3" json:"Sender,omitempty"`
	Store    []*KeyValue      `protobuf:"bytes,2,rep,name=Store,proto3" json:"Store,omitempty"`
	Envelope *MessageEnvelope `protobuf:"bytes,3,opt,name=Envelope,proto3" json:"Envelope,omitempty"`
	// contains filtered or unexported fields
}

RaftCommand

func (*RaftCommand) DeepCopy

func (x *RaftCommand) DeepCopy(z *RaftCommand)

func (*RaftCommand) Descriptor deprecated

func (*RaftCommand) Descriptor() ([]byte, []int)

Deprecated: Use RaftCommand.ProtoReflect.Descriptor instead.

func (*RaftCommand) Fill

func (x *RaftCommand) Fill(senderID []byte, e *MessageEnvelope, kvs ...*KeyValue)

func (*RaftCommand) GetEnvelope

func (x *RaftCommand) GetEnvelope() *MessageEnvelope

func (*RaftCommand) GetSender

func (x *RaftCommand) GetSender() []byte

func (*RaftCommand) GetStore

func (x *RaftCommand) GetStore() []*KeyValue

func (*RaftCommand) Marshal added in v0.0.17

func (x *RaftCommand) Marshal() ([]byte, error)

func (*RaftCommand) ProtoMessage

func (*RaftCommand) ProtoMessage()

func (*RaftCommand) ProtoReflect

func (x *RaftCommand) ProtoReflect() protoreflect.Message

func (*RaftCommand) Reset

func (x *RaftCommand) Reset()

func (*RaftCommand) String

func (x *RaftCommand) String() string

func (*RaftCommand) Unmarshal

func (x *RaftCommand) Unmarshal(b []byte) error

type RaftState

type RaftState int32
const (
	RaftState_None      RaftState = 0
	RaftState_Follower  RaftState = 1
	RaftState_Candidate RaftState = 2
	RaftState_Leader    RaftState = 3
	RaftState_Shutdown  RaftState = 4
)

func (RaftState) Descriptor

func (RaftState) Descriptor() protoreflect.EnumDescriptor

func (RaftState) Enum

func (x RaftState) Enum() *RaftState

func (RaftState) EnumDescriptor deprecated

func (RaftState) EnumDescriptor() ([]byte, []int)

Deprecated: Use RaftState.Descriptor instead.

func (RaftState) Number

func (x RaftState) Number() protoreflect.EnumNumber

func (RaftState) String

func (x RaftState) String() string

func (RaftState) Type

type Redirect

type Redirect struct {
	LeaderHostPort []string `protobuf:"bytes,1,rep,name=LeaderHostPort,proto3" json:"LeaderHostPort,omitempty"`
	HostPorts      []string `protobuf:"bytes,2,rep,name=HostPorts,proto3" json:"HostPorts,omitempty"`
	ServerID       string   `protobuf:"bytes,3,opt,name=ServerID,proto3" json:"ServerID,omitempty"`
	WaitInSec      uint32   `protobuf:"varint,4,opt,name=WaitInSec,proto3" json:"WaitInSec,omitempty"`
	// contains filtered or unexported fields
}

Redirect

func (*Redirect) DeepCopy

func (x *Redirect) DeepCopy(z *Redirect)

func (*Redirect) Descriptor deprecated

func (*Redirect) Descriptor() ([]byte, []int)

Deprecated: Use Redirect.ProtoReflect.Descriptor instead.

func (*Redirect) GetHostPorts

func (x *Redirect) GetHostPorts() []string

func (*Redirect) GetLeaderHostPort

func (x *Redirect) GetLeaderHostPort() []string

func (*Redirect) GetServerID

func (x *Redirect) GetServerID() string

func (*Redirect) GetWaitInSec

func (x *Redirect) GetWaitInSec() uint32

func (*Redirect) Marshal added in v0.0.17

func (x *Redirect) Marshal() ([]byte, error)

func (*Redirect) ProtoMessage

func (*Redirect) ProtoMessage()

func (*Redirect) ProtoReflect

func (x *Redirect) ProtoReflect() protoreflect.Message

func (*Redirect) Reset

func (x *Redirect) Reset()

func (*Redirect) String

func (x *Redirect) String() string

func (*Redirect) Unmarshal

func (x *Redirect) Unmarshal(b []byte) error

Directories

Path Synopsis
cmd
tcp
tcp/util
Package wsutil provides utilities for working with WebSocket protocol.
Package wsutil provides utilities for working with WebSocket protocol.
udp
internal
parser
Package parse builds parse trees for templates as defined by text/template and html/template.
Package parse builds parse trees for templates as defined by text/template and html/template.
repo
cql
kv

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL