Does FastProxy Support Load Balancing? Architecture Analysis and Solutions

FastProxy does not provide built-in load balancing; both the OutProxy and InProxy components are designed to forward traffic to a single upstream endpoint, requiring external load balancers or custom ServiceCenter implementations to distribute requests across multiple instances.

FastProxy (kingson4wu/fast_proxy) is a lightweight Go proxy library designed for service forwarding and discovery. While it efficiently handles request routing between services, developers evaluating this library for production deployments often need to understand its load balancing capabilities. This article examines the source code architecture to explain why FastProxy lacks native load distribution and how to implement it externally.

Why FastProxy Does Not Provide Built-In Load Balancing

The core architecture of FastProxy is intentionally simple: each request resolves to exactly one upstream target. Both major components enforce single-endpoint resolution:

  • OutProxy uses a single ForwardAddress configuration string (exposed via outconfig.Config.ForwardAddress()) and forwards every request to that specific URL using a standard net/http/httputil.ReverseProxy.

  • InProxy relies on a ServiceCenter (servicediscovery.Create()) that returns exactly one Address struct per service name lookup. The ServiceCenter holds a single addressFunc with the signature func(serviceName string) *Address, providing no mechanism for round-robin, weighted, or health-check-based selection.

Because neither component maintains a pool of upstream candidates or selection algorithms, the library cannot distribute traffic without external intervention.

OutProxy Single-Target Architecture

In outproxy/internal/proxy/reverseProxy.go, the OutProxy initializes a ReverseProxy with a Director function that unconditionally forwards requests to the configured ForwardAddress:

// outproxy/internal/proxy/reverseProxy.go
proxy = &httputil.ReverseProxy{
    Director: func(req *http.Request) {
        // Always forwards to the single configured address
        callUrl := outconfig.Get().ForwardAddress() + req.RequestURI
        u, _ := url.Parse(callUrl)
        req.URL = u
        req.Host = u.Host
    },
    // ... response handling omitted ...
}

The ForwardAddress is loaded from YAML configuration via outproxy/outconfig/yaml_config.go and exposed through the interface in outproxy/outconfig/outconfig.go. Since this is a static string value, there is no opportunity to alternate between multiple backends at the proxy level.

InProxy Service Discovery Limitations

The InProxy component resolves upstream locations through the ServiceCenter defined in common/servicediscovery/servicediscovery.go. This component maintains a single addressFunc callback that returns one Address pointer per service name:

// inproxy/internal/servicediscovery/serviceDiscovery.go
addr := server.Center().Address(serviceName) // returns one *Address
if addr == nil {
    return "", nil // no upstream found
}

// Constructs URL from the single returned address
builder := strings.Builder{}
builder.WriteString("http://")
builder.WriteString(addr.Ip)
builder.WriteString(":")
builder.WriteString(strconv.Itoa(addr.Port))
builder.WriteString(requestPath)
callUrl = builder.String()

The Address struct contains only Ip and Port fields. Without modifying the addressFunc implementation, the InProxy will always route to the same instance for a given service name.

How to Implement Load Balancing with FastProxy

While FastProxy lacks native load distribution, you can achieve it through two primary architectural approaches.

Option 1: Deploy an External Load Balancer

Place FastProxy instances behind an external load balancing layer such as Nginx, Envoy, HAProxy, or a cloud provider load balancer (AWS ALB, GCP LB). The external component handles health checking, failover, and request distribution across multiple FastProxy processes, treating each proxy instance as a downstream target.

Option 2: Build a Custom ServiceCenter

For InProxy deployments, implement a custom ServiceCenter with your own selection algorithm. The servicediscovery.Create() builder accepts an AddressFunc that you can implement with round-robin, random, or least-connections logic:

package main

import (
    "github.com/Kingson4Wu/fast_proxy/common/servicediscovery"
    "sync/atomic"
    "strconv"
    "strings"
)

var (
    targets = []string{
        "127.0.0.1:8081",
        "127.0.0.1:8082",
        "127.0.0.1:8083",
    }
    rrIdx int32
)

func main() {
    sc := servicediscovery.Create().
        AddressFunc(func(serviceName string) *servicediscovery.Address {
            // Atomic round-robin selection
            i := atomic.AddInt32(&rrIdx, 1)
            hostPort := strings.Split(targets[i%int32(len(targets))], ":")
            port, _ := strconv.Atoi(hostPort[1])
            return &servicediscovery.Address{
                Ip:   hostPort[0],
                Port: port,
            }
        }).
        Build()

    // Register the custom ServiceCenter with your server instance
    server.SetCenter(sc)
}

This approach embeds load balancing logic directly into the InProxy's resolution chain, allowing dynamic upstream selection without external infrastructure.

Summary

  • FastProxy routes each request to exactly one upstream target by design.
  • OutProxy forwards exclusively to the ForwardAddress configured in outconfig.Config.
  • InProxy retrieves a single Address from the ServiceCenter per request via server.Center().Address().
  • Native load balancing requires either external infrastructure (Nginx, Envoy) or a custom ServiceCenter implementation with selection algorithms.

Frequently Asked Questions

Does FastProxy support round-robin load balancing out of the box?

No. The ServiceCenter in common/servicediscovery/servicediscovery.go returns a single Address via the addressFunc callback without any rotation logic. To achieve round-robin behavior, you must implement a custom AddressFunc that tracks state across requests, as shown in the code example above.

Can I configure multiple upstream endpoints in FastProxy?

Not directly. OutProxy accepts only one ForwardAddress string from the YAML configuration, and InProxy resolves service names to a single IP/port pair. Supporting multiple endpoints requires either placing FastProxy behind an external load balancer or writing a custom ServiceCenter that selects from a pool of targets.

What is the ServiceCenter in FastProxy?

The ServiceCenter is the service discovery component defined in common/servicediscovery/servicediscovery.go. It resolves service names to network addresses using a configurable addressFunc. By default, it returns one static address per service, but you can inject custom logic to implement dynamic resolution or load balancing algorithms.

Is FastProxy suitable for high-availability deployments?

Yes, but only when paired with external load balancing infrastructure or custom ServiceCenter logic. The proxy itself handles single-endpoint forwarding efficiently, but relies on external systems or custom code to provide redundancy, failover, and traffic distribution across multiple upstream instances.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →