Skip to content

Commit

Permalink
Merge pull request rstudio#4 from rstudio/feature/least-connections
Browse files Browse the repository at this point in the history
Load balancing based on score.
  • Loading branch information
cbarraford committed Oct 15, 2014
2 parents dadfc66 + 93917c2 commit 6fd9bf4
Show file tree
Hide file tree
Showing 13 changed files with 505 additions and 35 deletions.
6 changes: 5 additions & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,18 @@ env:
services:
- redis-server

branches:
only:
- master

install:
- sudo apt-get update
- sudo apt-get install luajit luarocks
- sudo apt-get install libreadline-dev libncurses5-dev libpcre3-dev libssl-dev perl make
- sudo luarocks install luasec OPENSSL_DIR=/usr OPENSSL_LIBDIR=/usr/lib/x86_64-linux-gnu
- sudo luarocks install luafilesystem
- sudo luarocks install busted
- sudo luarocks install lapis
- sudo luarocks install lapis 1.0.4-1
- sudo luarocks install moonscript
- sudo luarocks install inspect
- wget http:https://openresty.org/download/ngx_openresty-1.7.2.1.tar.gz
Expand Down
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
Changelog
=========

## 1.2.0 (2014-10-14)

### Feature
+ [PR](https://github.com/rstudio/redx/pulls): Add ability to probabilistically load balance backends based on their score value (most or least)

## 1.1.0 (2014-07-27)

### Feature
Expand Down
40 changes: 40 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ At [rstudio](http:https://www.rstudio.com/), we find that redx performs slightly slowe
Requirements
============

[lapis](http:https://leafo.net/lapis/) version 1.0.4-1

[openresty](http:https://openresty.org/) 1.7.2 or greater

A [redis](http:https://redis.io/) server
Expand Down Expand Up @@ -86,6 +88,17 @@ Currently max_path_length must be a minimum of 1, but that will change in the fu
##### stickiness
The amount of time (in seconds) you wish the session to be "sticky", and consistently use the same upstream server. If you wish to disable "stickiness", set value to 0 (zero).

##### balance\_algorithm
The load balancing algorithm you want to use to balance traffic to your backends. The options are `least-score`, `most-score`, and `random`. `Random` is the default.

Load Balancing Algorithms
=========================

Redx has a few options for how to load balance to various backends. The default, is `random` which works exactly how you would imagine. The other options are `least-score` and `most-score`.
For `least-score` and `most-score`, associated with each backend, is a score. This score number is arbitrary, and can be whatever you want it to be. It can represent the number of connections a backend has, the amount of cpu or memory a backend is using, or something custom to your application like number of threads.
Each set of backends can be configured to have a maximum score (ie max number of connections, CPU usage, max number of threads, etc). This maximum value is used in evaluating which backend traffic is sent to. It is important to note, that due to the score values are assumed not to be realtime, we use a probabilistic approach to routing traffic. This is so we don't send all traffic to a single backend in between each update of the score value. So efforts to balance traffic is "best efforts" and are **NOT** guarenteed. Similar to a casino, while you may statistically loose some money sometimes, eventually the house always wins.
Load balancing does **NOT** override stickiness. If you have stickiness enabled, it is honored while a stickiness session exists. But new traffic, aka traffic that doesn't have an active stickiness session, are load balanced according to the algorithm chosen.

API
===

Expand Down Expand Up @@ -134,6 +147,33 @@ curl -X DELETE localhost:8081/backends/mybackend
curl -X DELETE localhost:8081/backends/mybackend/google.com%3A80
```

### (PUT) /backends/\<name\>/\<server\>/score/\<score>

The `backend score` endpoint allows you to update the score a backend has. This score is used by the `least-score` and `most-score` load balancing algorithm to probabilistically send incoming requests to the most probably backend with the least or most score.

#### Examples

##### `PUT` example
```
curl -X PUT localhost:8081/backends/mybackend/google.com%3A80/score/31
```

### (GET|PUT) /backends/\<name\>/\<server\>/config/\<config_name\>/\<config_value\>

The `backend configuration` endpoint allows you to get, update, or replace a backend config. Be sure to character escape as needed.

#### Examples

##### `GET` example
```
curl localhost:8081/backends/mybackend/config/_max_score/30
```

##### `PUT` example
```
curl -X POST localhost:8081/backends/mybackend/config/_max_score
```

### (DELETE) /flush

Flush clears the redis database of all data. Its literally runs the [`FLUSHDB`](http:https://redis.io/commands/flushdb) command within redis.
Expand Down
6 changes: 5 additions & 1 deletion conf/config.moon
Original file line number Diff line number Diff line change
Expand Up @@ -28,5 +28,9 @@ M.max_path_length = 1
-- Stickiness
-- Amount of time (in seconds) you wish the session to be sticky
-- Set to 0 if you want to disable stickiness
M.stickiness = 900
M.stickiness = 0

-- Load Balancing Algorithm
-- "random" or "least-score" or "most-score"
M.balance_algorithm = 'random'
return M
3 changes: 2 additions & 1 deletion lua/conf/config.lua
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,6 @@ M.redis_timeout = 5000
M.redis_keepalive_pool_size = 0
M.redis_keepalive_max_idle_timeout = 10000
M.max_path_length = 1
M.stickiness = 900
M.stickiness = 0
M.balance_algorithm = 'random'
return M
15 changes: 14 additions & 1 deletion lua/spec/api_spec.lua
Original file line number Diff line number Diff line change
Expand Up @@ -283,7 +283,7 @@ return describe("redx_api", function()
}
})
end)
return it("should delete orphans #orphans_api", function()
it("should delete orphans #orphans_api", function()
local response, code, headers = make_json_request("/backends/5555/" .. tostring(escape('rstudio.com:80')), "POST")
assert.same(200, code)
response, code, headers = make_json_request("/backends/5555/" .. tostring(escape('rstudio.com:80')), "POST")
Expand All @@ -309,4 +309,17 @@ return describe("redx_api", function()
response, code, headers = make_json_request("/frontends/" .. tostring(escape('foobar.com/path')), "GET")
return assert.same(404, code)
end)
it("should create backend config #config_api", function()
local response, code, headers = make_json_request("/backends/5555/config/limit/5", "PUT")
assert.same(200, code)
response, code, headers = make_json_request("/backends/5555/config/limit", "GET")
assert.same(200, code)
return assert.same(response['data'], {
limit = '5'
})
end)
return it("Create backend and set score #score_api", function()
local response, code, headers = make_json_request("/backends/5555/" .. tostring(escape('rstudio.com:80')) .. "/score/30", "PUT")
return assert.same(200, code)
end)
end)
31 changes: 29 additions & 2 deletions lua/src/bin/api.lua
Original file line number Diff line number Diff line change
Expand Up @@ -114,16 +114,43 @@ do
}
end
}),
['/backends/:name/config/:config'] = respond_to({
GET = function(self)
redis.get_config(self, unescape(self.params.name), unescape(self.params.config))
return {
status = self.status,
json = json_response(self)
}
end
}),
['/backends/:name/config/:config/:value'] = respond_to({
PUT = function(self)
redis.set_config(self, unescape(self.params.name), unescape(self.params.config), unescape(self.params.value))
return {
status = self.status,
json = json_response(self)
}
end
}),
['/backends/:name/:value/score/:score'] = respond_to({
PUT = function(self)
redis.save_data(self, 'backends', unescape(self.params.name), unescape(self.params.value), unescape(self.params.score), false)
return {
status = self.status,
json = json_response(self)
}
end
}),
['/:type/:name/:value'] = respond_to({
POST = function(self)
redis.save_data(self, self.params.type, unescape(self.params.name), unescape(self.params.value), false)
redis.save_data(self, self.params.type, unescape(self.params.name), unescape(self.params.value), 0, false)
return {
status = self.status,
json = json_response(self)
}
end,
PUT = function(self)
redis.save_data(self, self.params.type, unescape(self.params.name), unescape(self.params.value), true)
redis.save_data(self, self.params.type, unescape(self.params.name), unescape(self.params.value), 0, true)
return {
status = self.status,
json = json_response(self)
Expand Down
2 changes: 2 additions & 0 deletions lua/src/bin/init_redx.lua
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,6 @@ redis = require('redis')
config = require('config')
library = require('library')
inspect = require('inspect')
socket = require('socket')
math.randomseed(socket.gettime() * 1000)
return library.log('Redis host: ' .. config.redis_host .. ':' .. config.redis_port)
Loading

0 comments on commit 6fd9bf4

Please sign in to comment.