Skip to content

Latest commit

 

History

1 Commit

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Lancer

A high-performance SOCKS5 front proxy with load balancing, active health checking, and dead-state management. Lancer sits between your applications and a pool of upstream proxies, distributing traffic intelligently while continuously monitoring upstream health.

Features

  • Multi-Protocol Upstream Support — SOCKS4, SOCKS5, HTTP CONNECT, HTTPS CONNECT
  • Tiered P2C Load Balancing — Latency-biased Power of Two Choices with dynamic tuning
  • Active Health Checking — HTTP probe through upstreams with configurable intervals
  • Dead-State Management — Automatic quarantine, periodic revival, and permanent removal
  • SO_REUSEPORT Sharding — Kernel-level connection distribution across CPU cores
  • Zero-Copy Relay — Linux splice(2) for kernel-to-kernel TCP forwarding
  • Lock-Free Metrics — All hot-path metrics use atomic operations (no mutexes)
  • Standalone Proxy Checkerlancer ping works independently without any config file
  • UDP ASSOCIATE — Full SOCKS5 UDP relay support (RFC 1928 §7)

Quick Start

Build

go build -o lancer ./cmd/lancer

Configure

Create a config.json (see Configuration Reference below) and a proxy.txt file with your upstream proxies:

# proxy.txt — one proxy URI per line
socks5://127.0.0.1:1080
socks5://admin:password@10.0.0.1:1080
socks4://10.0.0.2:1080
http://proxy.example.com:8080
https://user:pass@secure-proxy.example.com:443

Run

# Start the proxy server (default: reads config.json)
./lancer

# With a custom config path
./lancer --config /path/to/config.json

Ping (Standalone Proxy Checker)

The ping command is fully standalone — it does not require config.json. All options are passed as CLI flags.

# Basic usage — probe all proxies in proxy.txt
./lancer ping --file proxy.txt

# Save alive proxies to a file
./lancer ping --file proxy.txt --output healthy.txt

# Custom timeout and probe URL
./lancer ping --file proxy.txt --output healthy.txt --timeout 10s

# Custom probe URL and expected status
./lancer ping --file proxy.txt --url https://example.com --status 200

# Full example with all flags
./lancer ping \
  --file proxy.txt \
  --output healthy.txt \
  --timeout 5s \
  --url https://google.com/generate_204 \
  --status 204 \
  --concurrency 200

Ping Flags:

Flag Default Description
--file proxy.txt Input file with proxy URIs (one per line)
--output (stdout only) Write alive proxy URIs to this file
--timeout 5s Per-probe dial + HTTP timeout
--url https://google.com/generate_204 URL to probe through each upstream
--status 204 Expected HTTP status code
--concurrency 200 Maximum concurrent probes

Upstream File Format

The upstream file (proxy.txt by default) contains one proxy URI per line. Blank lines and lines starting with # are ignored.

Supported URI formats:

# SOCKS5 (default port: 1080)
socks5://host:port
socks5://username:password@host:port

# SOCKS4 / SOCKS4a (default port: 1080)
socks4://host:port
socks4://username@host:port

# HTTP CONNECT (default port: 8080)
http://host:port
http://username:password@host:port

# HTTPS CONNECT — TLS to proxy, then HTTP CONNECT (default port: 8080)
https://host:port
https://username:password@host:port

Protocol Details:

Protocol Auth UDP ASSOCIATE Notes
SOCKS5 Username/Password (RFC 1929) ✅ Yes Full RFC 1928 compliance
SOCKS4 USERID field ❌ No SOCKS4a domain support included
HTTP Basic Auth ❌ No Standard HTTP CONNECT tunneling
HTTPS Basic Auth ❌ No TLS handshake to proxy, then CONNECT

Configuration Reference

Lancer reads its runtime configuration from config.json (or a path specified by --config). All fields have sensible defaults — an empty {} is a valid config.

{
    // --- Server Listener ---
    "listen": {
        "address": "0.0.0.0",   // Bind address (default: "0.0.0.0")
        "port": 4528             // Bind port (default: 1080)
    },

    // --- Upstream Proxy Source ---
    "upstream": {
        "file": "proxy.txt"     // Path to the proxy list file (default: "proxy.txt")
    },

    // --- Health Probing ---
    "probe": {
        "interval": 30,          // Seconds between health check rounds (default: 10)
        "timeout": 5,            // Per-probe dial timeout in seconds (default: 3)
        "concurrency": 200,      // Max concurrent health checks (default: 100)
        "ping": {
            "url": "https://google.com/generate_204",  // Probe URL (default: same)
            "status": 204,       // Expected HTTP status (default: 204)
            "max_fails": 10      // Consecutive failures before marking dead (default: 3)
        },
        "dead_state": {
            "revive_interval_seconds": 15,  // Seconds between revival attempts (default: 900)
            "remove_after_cycles": 5        // Failed revival cycles before permanent removal (default: 2)
        }
    },

    // --- Load Balancer ---
    "balancer": {
        "algorithm": "tiered_p2c",   // "p2c" or "tiered_p2c" (default: "p2c")
        "metrics": {
            "ewma_alpha": 0.1,       // EWMA smoothing factor for latency (default: 0.2)
            "load_penalty": 0.5      // Active connection penalty weight (default: 1.0)
        },
        "tiered": {
            "latency_exp": 2.0,          // Non-linear latency exponent (default: 2.0)
            "success_rate_exp": 1.0,     // Success rate exponent (default: 1.0)
            "load_exp": 1.0,             // Load penalty exponent (default: 1.0)
            "tier_boundaries": [0.05, 0.2],  // Latency thresholds in seconds (default: [0.05, 0.2])
            "tier_weights": [70, 20, 10],    // Traffic share per tier (default: [70, 20, 10])
            "max_conns_per_upstream": 0,     // Hard cap per upstream, 0 = unlimited (default: 0)
            "dynamic": {
                "enabled": true,
                "min_latency_exp": 1.0,
                "max_latency_exp": 4.0,
                "adaptation_rate": 0.1,
                "sampling_window": 256,
                "percentile_method": "p90",
                "auto_tiers": true,
                "stability_threshold": 0.05,
                "rebalance_interval": 10,
                "explore_ratio": 0.02
            }
        }
    },

    // --- Connection Tuning ---
    "connection": {
        "buffer_size": 65536,    // Relay buffer size in bytes (default: 32768)
        "dial_timeout": 10,      // Upstream dial timeout in seconds (default: 5)
        "idle_timeout": 120      // Client idle timeout in seconds (default: 60)
    },

    // --- Retry Policy ---
    "retry": {
        "attempts": 4,           // Retry count on upstream failure (default: 2)
        "jitter_ms": 50          // Max jitter between retries in ms (default: 3)
    },

    // --- System Tuning ---
    "tuning": {
        "max_connections": 100000,   // Global connection limit (default: 50000)
        "enable_sharding": true,     // Enable SO_REUSEPORT listener sharding (default: false)
        "shard_per_cpu": true        // One shard per CPU core (default: false)
    }
}

Architecture

Connection Flow

Client ──SOCKS5──▶ Lancer ──[protocol]──▶ Upstream Proxy ──▶ Target
                     │
                     ├── Tiered P2C Balancer (upstream selection)
                     ├── Health Checker (periodic probing)
                     ├── Dead State Manager (quarantine & revival)
                     └── Connection Manager (global limit enforcement)

Package Layout

cmd/lancer/
├── main.go          # Entry point, server lifecycle, connection handler
└── ping.go          # Standalone proxy checker (no config dependency)

pkg/
├── balancer/        # P2C and Tiered P2C selection algorithms
│   ├── p2c.go           # Basic Power of Two Choices
│   ├── tiered_p2c.go    # Latency-biased tiered variant with dynamic tuning
│   └── percentile.go    # Lock-free ring buffer for latency percentiles
├── config/          # Configuration loading and upstream parsing
│   ├── config.go        # JSON config structs, defaults, file loading
│   └── upstream.go      # URI parsing (socks4/5, http/s)
├── connmgr/         # Atomic global connection limiter
├── deadfile/        # Persistent dead upstream archive (JSON)
├── dialer/          # Multi-protocol upstream dialer
│   └── dialer.go        # SOCKS4, SOCKS5, HTTP CONNECT, HTTPS CONNECT
├── health/          # Active health checking
│   ├── checker.go       # Periodic probe loop (HTTP or TCP)
│   └── reviver.go       # Dead upstream recovery
├── relay/           # Bidirectional data relay with splice(2)
├── shard/           # SO_REUSEPORT listener sharding
├── socks5/          # SOCKS5 server protocol (RFC 1928)
│   ├── protocol.go      # Handshake, request parsing, reply encoding
│   └── server.go        # Accept loop and connection handler
├── udprelay/        # SOCKS5 UDP ASSOCIATE relay (RFC 1928 §7)
└── upstream/        # Upstream state management
    ├── upstream.go      # Lock-free atomic metrics (EWMA, conns, rewards)
    └── pool.go          # Atomic healthy/dead pool with zero-alloc reads

Load Balancing

P2C (Power of Two Choices)

The basic P2C algorithm randomly samples two upstreams and picks the one with the higher score:

score = successRate × weight × (1 / latency) × (1 / (1 + activeConns × loadPenalty))

Tiered P2C (Latency-Biased)

The tiered variant adds three improvements:

  1. Tier Bucketing — Upstreams are grouped into latency tiers (Fast < 50ms, Medium 50–200ms, Slow > 200ms) based on EWMA latency.

  2. Weighted Tier Selection — A tier is chosen via weighted random (default: 70% Fast, 20% Medium, 10% Slow), then P2C runs within the selected tier.

  3. Non-Linear Scoring — Exponents amplify differences:

    score = sr^srExp × weight × reward × (1/lat^latExp) × (1/(1+conns)^loadExp)
    
  4. Dynamic Tuning — A background goroutine adapts the latency exponent based on the p90/p50 spread, and optionally recomputes tier boundaries from percentile data.

  5. Exploration — A small fraction of picks (explore_ratio) bypass tier selection entirely to gather latency data from under-sampled upstreams.

  6. Reinforcement Rewards — Successful relays increase an upstream's reward factor; failures decrease it (clamped to [0.01, 2.0]).


Health Checking

Probe System

Every probe.interval seconds, the health checker probes all healthy upstreams concurrently:

  1. HTTP Probe (default) — Dials through each upstream to probe.ping.url, performs an HTTP GET, and checks the response status code.
  2. TCP Probe (fallback) — If no ping URL is configured, performs a simple TCP dial to the upstream.

Dead State Lifecycle

HEALTHY ──[max_fails consecutive failures]──▶ DEAD
   ▲                                            │
   │                                            ▼
   └──[TCP dial succeeds]── REVIVAL ◀── [revive_interval_seconds]
                                            │
                                            ▼
                              [remove_after_cycles failures]
                                            │
                                            ▼
                                    PERMANENTLY REMOVED
                                    (archived to dead.json)
  • Upstreams move from HEALTHY → DEAD after max_fails consecutive probe failures.
  • Every revive_interval_seconds, dead upstreams get a TCP dial check.
  • If the dial succeeds, the upstream is revived with a slow-start weight (0.1 → 1.0).
  • If revival fails remove_after_cycles times, the upstream is permanently removed and archived to dead.json.

Performance

Zero-Copy Relay

On Linux, when both client and upstream connections are raw TCP (*net.TCPConn), Go's ReadFrom automatically invokes splice(2) for kernel-to-kernel data transfer — data never enters userspace. The bufferedConn wrapper preserves splice eligibility by implementing io.ReaderFrom and io.WriterTo.

SO_REUSEPORT Sharding

When tuning.enable_sharding is true, Lancer creates one listener per CPU core, all bound to the same address via SO_REUSEPORT. The kernel distributes incoming connections across listeners, eliminating accept contention.

Lock-Free Hot Paths

All per-upstream metrics (EWMA latency, active connections, success/failure counters, rewards) use sync/atomic CAS loops. The healthy upstream slice is stored in an atomic.Value for zero-allocation reads on every Pick() call. Mutexes are only used for structural mutations (add/remove/mark-dead), which are infrequent.

Buffer Pooling

  • sync.Pool for relay buffers (configurable size, default 32KB)
  • sync.Pool for bufio.Reader in dialer and health checker
  • Stack-allocated arrays for protocol negotiation (SOCKS4/5 handshakes, replies)

Tuning Guide

Small Pool (< 50 upstreams)

{
    "balancer": {"algorithm": "p2c"},
    "probe": {"interval": 10, "concurrency": 50},
    "tuning": {"enable_sharding": false}
}

Large Pool (> 500 upstreams)

{
    "balancer": {
        "algorithm": "tiered_p2c",
        "tiered": {
            "dynamic": {"enabled": true, "auto_tiers": true}
        }
    },
    "probe": {"interval": 30, "concurrency": 200},
    "connection": {"buffer_size": 65536},
    "tuning": {
        "max_connections": 100000,
        "enable_sharding": true,
        "shard_per_cpu": true
    }
}

Key Parameters

Parameter Effect Guidance
probe.interval How often upstreams are checked Lower = faster failover, more probe traffic
probe.ping.max_fails Failures before marking dead Lower = aggressive, Higher = tolerant
balancer.tiered.latency_exp Fast upstream preference strength 1.0 = linear, 4.0 = heavily favors fast
balancer.tiered.dynamic.explore_ratio Random exploration fraction 0.02 = 2% of picks are random exploration
connection.buffer_size Relay buffer (non-splice fallback) 32KB–64KB typical; larger for high-throughput
tuning.max_connections Global connection hard limit Match your ulimit -n
retry.attempts Retries on upstream dial failure 2–4 typical
retry.jitter_ms Random delay cap between retries Prevents thundering herd

License

MIT

About

A high-performance SOCKS5 front proxy with load balancing, active health checking, and dead-state management. Lancer sits between your applications and a pool of upstream proxies, distributing traffic intelligently while continuously monitoring upstream health.

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages