A fast and easy-to-configure load balancer
Table of Contents
This project is designed to provide a fast and easy-to-configure load balancer in Go language. It currently includes round-robin, weighted round-robin, least-connection, least-response-time, ip-hash and random algorithms, but we have more to add to our TODO list.
The project is developed using the fasthttp library for HTTP/1.1, which ensures high performance. For HTTP/2 support, it uses the native Go net/http package with HTTP/2 configuration. Its purpose is to distribute the load evenly among multiple servers by routing incoming requests.
The project aims to simplify the configuration process for users while performing the essential functions of load balancers. Therefore, it offers several configuration options that can be adjusted to meet the users needs.
This project is particularly suitable for large-scale applications and websites. It can be used for any application that requires a load balancer, thanks to its high performance, ease of configuration, and support for different algorithms.
net/http package for HTTP/2, ensuring high performance and scalability.http://monitoring-host:monitoring-port/metrics can be used to get prometheus metrics)The latest release of Divisor can be downloaded from the releases page. Choose the suitable binary for your system, download and extract the archive, and then move the binary to a directory in your system's $PATH variable (e.g. /usr/local/bin).
Alternatively, you can build Divisor from source by cloning this repository to your local machine and running the following commands:
git clone https://github.com/aaydin-tr/divisor.git &&
cd divisor &&
go build -o divisor &&
./divisor
You can also install Divisor using the go install command:
go install github.com/aaydin-tr/divisor@latest
This will install the divisor binary to your system's $GOPATH/bin directory. Make sure this directory is included in your system's $PATH variable to make the divisor accessible from anywhere.
That's it! You're now ready to use Divisor in your project.
You need a config.yaml file to use Divisor, you can give this file to Divisor to use with the --config flag, by default it will try to use a config.yaml file in the directory it is in. Example config files
:warning: Please use absolute path for "config.yaml" while using "--config" flag
port: 8000 # Required
backends:
- url: localhost:8080
- url: localhost:7070
| Name | Description | Type | Default | Required |
|---|---|---|---|---|
| port | Server port | string | - | ⚠️ Yes |
| host | Server host | string | localhost |
No |
| type | Load balancing algorithm | string | round-robin |
No |
| health_checker_time | Health check interval for backends | duration | 30s |
No |
Valid algorithm types: round-robin, w-round-robin, ip-hash, random, least-connection, least-response-time
| Name | Description | Type | Default | Required |
|---|---|---|---|---|
| backends | List of backend servers | array | - | ⚠️ Yes (min: 1) |
| backends.url | Backend URL (without protocol) | string | - | ⚠️ Yes |
| backends.health_check_path | Health check endpoint | string | / |
No |
| backends.weight | Backend weight (w-round-robin only) | int | - | ⚠️ w-round-robin |
| backends.max_conn | Max connections per backend | int | 512 |
No |
| backends.max_conn_timeout | Max wait time for free connection | duration | 30s |
No |
| backends.max_conn_duration | Connection keep-alive duration | duration | 10s |
No |
| backends.max_idle_conn_duration | Idle connection timeout | duration | 10s |
No |
| backends.max_idemponent_call_attempts | Retry attempts for idempotent calls | int | 5 |
No |
| Name | Description | Type | Default |
|---|---|---|---|
| monitoring.host | Metrics server host | string | localhost |
| monitoring.port | Metrics server port | string | 8001 |
| Name | Description | Type | Default |
|---|---|---|---|
| server.http_version | HTTP protocol version (http1 or http2) |
string | http1 |
| server.cert_file | TLS certificate file path | string | - |
| server.key_file | TLS private key file path | string | - |
| server.max_idle_worker_duration | Worker pool idle timeout | duration | 10s |
| server.tcp_keepalive_period | TCP keep-alive interval (OS default if unset) | duration | - |
| server.concurrency | Max concurrent connections | int | 262144 |
| server.read_timeout | Request read timeout | duration | unlimited |
| server.write_timeout | Response write timeout | duration | unlimited |
| server.idle_timeout | Keep-alive idle timeout | duration | unlimited |
| server.disable_keepalive | Force connection close after response | bool | false |
| server.disable_header_names_normalizing | Preserve original header name casing | bool | false |
| Name | Description | Type |
|---|---|---|
| custom_headers | Headers injected into backend requests | map |
custom_headers.<name> |
Header value (special variables supported) | string |
Special variables: $remote_addr (client IP), $time (request timestamp), $uuid (request UUID), $incremental (per-backend counter)
Example:
custom_headers:
x-client-ip: $remote_addr
x-request-id: $uuid
| Name | Description | Type | Default | Required |
|---|---|---|---|---|
| middlewares | List of custom middleware | array | - | No |
| middlewares.name | Middleware identifier | string | - | ⚠️ Yes |
| middlewares.disabled | Skip middleware execution | bool | false |
No |
| middlewares.code | Inline Go code | string | - | ⚠️ Yes (or file) |
| middlewares.file | Path to Go code file | string | - | ⚠️ Yes (or code) |
| middlewares.config | Config passed to middleware constructor | map | - | No |
http:// or https:// removedserver.http_version: http2 requires both cert_file and key_filecode OR file (not both), unless disabled: truetype is omitted or invalid, defaults to round-robinPlease see example config files
Divisor supports custom middleware written in Go. You can define middleware to intercept requests and responses, allowing you to implement custom logic such as authentication, logging, header manipulation, etc.
The middleware is executed using the Yaegi interpreter.
Your middleware must implement the Middleware interface and provide a New function constructor.
:warning: Make sure you run
go get github.com/aaydin-tr/divisor/middlewareto import the middleware package.
package middleware
import (
"github.com/aaydin-tr/divisor/middleware"
"fmt"
)
type MyMiddleware struct {
config map[string]any
}
func New(config map[string]any) middleware.Middleware {
return &MyMiddleware{config: config}
}
func (m *MyMiddleware) OnRequest(ctx *middleware.Context) error {
// Logic to execute before request reached to backend server
// e.g. ctx.Request.Header.Set("X-Custom-Header", "Value")
fmt.Println("OnRequest")
return nil
}
func (m *MyMiddleware) OnResponse(ctx *middleware.Context, err error) error {
// Logic to execute after response is received from backend server
fmt.Println("OnResponse")
return nil
}
You can configure middlewares in config.yaml using either inline code or a file path.
Using a file:
middlewares:
- name: "my-logger"
file: "./middleware/logger.go"
config:
prefix: "[LOG]"
Using inline code:
middlewares:
- name: "simple-header"
code: |
package middleware
import "github.com/aaydin-tr/divisor/middleware"
type HeaderMiddleware struct {}
func New(config map[string]any) middleware.Middleware {
return &HeaderMiddleware{}
}
func (h *HeaderMiddleware) OnRequest(ctx *middleware.Context) error {
ctx.Request.Header.Set("X-Divisor", "True")
return nil
}
func (h *HeaderMiddleware) OnResponse(ctx *middleware.Context, err error) error {
return nil
}
The middleware execution flow allows you to intercept and control the complete request/response lifecycle. Here's exactly what happens when a request is processed:
Pre-Request Setup
OnRequest Middleware Execution
OnRequest returns an error:OnResponse is NOT calledOnRequest succeeds (returns nil):Backend Proxy
OnResponseOnResponse Middleware Execution
nil on successOnResponse returns an error:OnResponse returns nil:Post-Response Cleanup
Response Sent
```mermaid flowchart TD Start([Client Request]) --> PreReq[Pre-Request Setup] PreReq --> OnReq{OnRequest Middleware}
OnReq -->|Returns Error| PostRes1[Post-Response Cleanup]
PostRes1 --> ReturnErr([Return OnRequest Error])
OnReq -->|Returns nil| Proxy[Forward to Backend Server]
Proxy --> CaptureErr[Capture Backend Response/Error]
CaptureErr --> OnRes{OnResponse Middleware}
OnRes -->|R
$ claude mcp add divisor \
-- python -m otcore.mcp_server <graph>