Getty
a netty like asynchronous network I/O library based on tcp/udp/websocket; a bidirectional RPC framework based on JSON/Protobuf; a microservice framework based on zookeeper/etcd
Install / Use
/learn @AlexStocks/GettyREADME
getty 中文
a netty like asynchronous network I/O library
INTRO
Getty is an asynchronous network I/O library developed in Golang. It operates on TCP, UDP, and WebSocket network protocols, providing a consistent interface EventListener.
Within Getty, each connection (session) involves two separate goroutines. One handles the reading of TCP streams, UDP packets, or WebSocket packages, while the other manages the logic processing and writes responses into the network write buffer. If your logic processing might take a considerable amount of time, it's recommended to start a new logic process goroutine yourself within codec.go's (Codec)OnMessage method.
Additionally, you can manage heartbeat logic within the (Codec)OnCron method in codec.go. If you're using TCP or UDP, you should send heartbeat packages yourself and then call session.go's (Session)UpdateActive method to update the session's activity timestamp. You can verify if a TCP session has timed out or not in codec.go's (Codec)OnCron method using session.go's (Session)GetActive method.
If you're using WebSocket, you don't need to worry about heartbeat request/response, as Getty handles this task within session.go's (Session)handleLoop method by sending and receiving WebSocket ping/pong frames. Your responsibility is to check whether the WebSocket session has timed out or not within codec.go's (Codec)OnCron method using session.go's (Session)GetActive method.
For code examples, you can refer to getty-examples.
Network Transmission
In network communication, the data transmission interface of getty does not guarantee that data will be sent successfully; it lacks an internal retry mechanism. Instead, getty delegates the outcome of data transmission to the underlying operating system mechanism. Under this mechanism, if data is successfully transmitted, it is considered a success; if transmission fails, it is regarded as a failure. These outcomes are then communicated back to the upper-layer caller.
Upper-layer callers need to determine whether to incorporate a retry mechanism based on these outcomes. This implies that when data transmission fails, upper-layer callers must handle the situation differently depending on the circumstances. For instance, if the failure is due to a disconnect in the connection, upper-layer callers can attempt to resend the data based on the result of getty's automatic reconnection. Alternatively, if the failure is caused by the sending buffer of the underlying operating system being full, the sender can implement its own retry mechanism to wait for the sending buffer to become available before attempting another transmission.
In summary, the data transmission interface of getty does not come with an inherent retry mechanism; instead, it is up to upper-layer callers to decide whether to implement retry logic based on specific situations. This design approach provides developers with greater flexibility in controlling the behavior of data transmission.
Framework Architecture
Getty framework adopts a layered architecture design, from top to bottom: Application Layer, Getty Core Layer, and Network Layer:
┌─────────────────────────────────────────────────────────────┐
│ Application Layer │
├─────────────────────────────────────────────────────────────┤
│ Application Code │ Message Handler │ Codec/ReadWriter │
└─────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│ Getty Core Layer │
├─────────────────────────────────────────────────────────────┤
│ Session Management │ Server Management │ Client Management │
│ Connection Mgmt │ Event System │ Options & Config │
└─────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│ Network Layer │
├─────────────────────────────────────────────────────────────┤
│ TCP Protocol │ UDP Protocol │ WebSocket Protocol │
│ TLS/SSL │ │ │
└─────────────────────────────────────────────────────────────┘
Core Component Relationships
- Session is the core component, managing connection lifecycle
- Server/Client provides endpoint implementations for different protocols
- Connection encapsulates underlying network connections
- EventListener handles various events
- Options provides flexible configuration
Data Flow Processing
Complete Data Flow Diagram
┌─────────────────────────────────────────────────────────────────────────────┐
│ Incoming Data Flow │
├─────────────────────────────────────────────────────────────────────────────┤
│ Network → Getty → PkgHandler.Read() → EventListener.OnMessage() → Logic │
└─────────────────────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────────────────┐
│ Outgoing Data Flow │
├─────────────────────────────────────────────────────────────────────────────┤
│ Logic → WritePkg() → PkgHandler.Write() → Getty → Network │
└─────────────────────────────────────────────────────────────────────────────┘
Processing Order
- PkgHandler first: Handles protocol-level parsing/serialization
- EventListener second: Handles business logic and events
- Two separate goroutines: One for reading, one for processing
Key Components
- PkgHandler: Implements
ReadWriterinterface for data parsing/serialization - EventListener: Implements
EventListenerinterface for business logic - OnMessage(): Method of
EventListenerinterface for processing parsed packets
Quick Start
TCP Server Example
Here's a simplified TCP server example demonstrating Getty framework's core usage:
package main
import (
"fmt"
"log"
"time"
getty "github.com/AlexStocks/getty/transport"
gxsync "github.com/dubbogo/gost/sync"
)
// Packet handler - responsible for packet serialization/deserialization
type EchoPackageHandler struct{}
// Deserialize: parse network byte stream into application packets
func (h *EchoPackageHandler) Read(session getty.Session, data []byte) (interface{}, int, error) {
// Pseudo code: implement length-prefixed protocol
// 1. Check if there's enough data to read length header (4 bytes)
if len(data) < 4 {
return nil, 0, nil // Need more data
}
// 2. Parse packet length
length := int(data[0])<<24 | int(data[1])<<16 | int(data[2])<<8 | int(data[3])
// 3. Check if we have complete packet
if len(data) < 4+length {
return nil, 0, nil // Incomplete packet, wait for more data
}
// 4. Return parsed packet and bytes consumed
return data[4:4+length], 4 + length, nil
}
// Serialize: convert application packets to network byte stream
func (h *EchoPackageHandler) Write(session getty.Session, pkg interface{}) ([]byte, error) {
// Pseudo code: implement length-prefixed protocol
// 1. Convert application data to bytes
data := []byte(fmt.Sprintf("%v", pkg))
// 2. Build length header (4 bytes)
length := len(data)
header := []byte{
byte(length >> 24), byte(length >> 16),
byte(length >> 8), byte(length),
}
// 3. Return complete network packet
return append(header, data...), nil
}
// Event handler - responsible for business logic
type EchoMessageHandler struct{}
// Called when connection is established
func (h *EchoMessageHandler) OnOpen(session getty.Session) error {
log.Printf("New connection: %s", session.RemoteAddr())
return nil
}
// Called when connection is closed
func (h *EchoMessageHandler) OnClose(session getty.Session) {
log.Printf("Connection closed: %s", session.RemoteAddr())
}
// Called when error occurs
func (h *EchoMessageHandler) OnError(session getty.Session, err error) {
log.Printf("Connection error: %s, error: %v", session.RemoteAddr(), err)
}
// Heartbeat detection - called periodically
func (h *EchoMessageHandler) OnCron(session getty.Session) {
activeTime := session.GetActive()
if time.Since(activeTime) > 30*time.Second {
log.Printf("Connection timeout, closing: %s", session.RemoteAddr())
session.Close()
}
}
// Called when message is received - core business logic
func (h *EchoMessageHandler) OnMessage(session getty.Session, pkg interface{}) {
messageData, ok := pkg.([]byte)
if !ok {
log.Printf("invalid packet type: %T", pkg)
return
}
log.Printf("Received message: %s", string(messageData))
// Business logic: echo message
response := fmt.Sprintf("Echo: %s", string(messageData))
if _, _, err := session.WritePkg(response, 5*time.Second); err != nil {
log.Printf("send failed: %v", err)
}
}
// New connection callback - configure session
func newSession(session getty.Ses
