5#include <click/ipflowid.hh>
10#include <click/dpdk_glue.hh>
13#include <click/ipflowid.hh>
14#include <click/tcphelper.hh>
16#include <click/args.hh>
18#include <click/algorithm.hh>
21class LoadBalancer {
public:
23 LoadBalancer() : _current(0), _dsts(), _weights_helper(), _mode_case(round_robin) {
24 modetrans.find_insert(
"rr",round_robin);
25 modetrans.find_insert(
"hash",direct_hash);
26 modetrans.find_insert(
"chash",direct_chash);
27 modetrans.find_insert(
"hash_ip",direct_hash_ip);
29 modetrans.find_insert(
"hash_crc",direct_hash_crc);
31 modetrans.find_insert(
"hash_agg",direct_hash_agg);
32 modetrans.find_insert(
"cst_hash_agg",constant_hash_agg);
33 modetrans.find_insert(
"wrr",weighted_round_robin);
34 modetrans.find_insert(
"awrr",auto_weighted_round_robin);
35 modetrans.find_insert(
"least",least_load);
36 modetrans.find_insert(
"pow2",pow2);
37 modetrans.find_insert(
"table",table);
38 lsttrans.find_insert(
"conn",connections);
39 lsttrans.find_insert(
"packets",packets);
40 lsttrans.find_insert(
"bytes",bytes);
41 lsttrans.find_insert(
"cpu",cpu);
42#if __cplusplus > 201402L
43 std::random_device rd;
44 _gen= std::mt19937(rd());
51 auto_weighted_round_robin,
64 static bool isLoadBased(LBMode mode) {
65 return mode == pow2 || mode == least_load || mode == weighted_round_robin || mode == auto_weighted_round_robin || mode == table;
86 load_type_t connection_load;
87 load_type_t packets_load;
88 load_type_t bytes_load;
90 uint64_t raw_cpu_load;
101 Vector <MachineLoad, CLICK_CACHE_LINE_SIZE> _loads;
102 Vector <unsigned> _selector;
103 Vector <unsigned> _cst_hash;
104 Vector <unsigned> _spares;
106 bool _force_track_load;
110#if __cplusplus > 201402L
114 uint64_t get_load_metric(
int idx) {
115 return get_load_metric(idx, _lst_case);
118 uint64_t get_load_metric(
int idx,LSTMode metric) {
119 MachineLoad& l = _loads[idx];
122 return l.connection_load;
128 return l.packets_load;
138 int hash_4tuple(
const Packet* p,
int n) {
139 const unsigned char *data = p->
data();
142 if ((
int)p->
length() < o + l)
146 for (
int i = o; i < o + l; i++)
148 if (n == 2 || n == 4 || n == 8)
149 h = (d ^ (d>>4)) & (n-1);
156 inline void track_load(
Packet*p,
int b) {
159 _loads[b].connection_load++;
161 _loads[b].connection_load--;
163 _loads[b].packets_load++;
164 _loads[b].bytes_load += p->
length();
170 void build_hash_ring() {
173 int fac = ((new_hash.
size() - 1) / _selector.
size()) + 1;
174 for (
int j = 0; j < fac; j++) {
175 for (
int i = 0; i < _selector.
size(); i++) {
176 int server_place = cantor(_selector[i], j) % new_hash.
size();
177 new_hash[server_place] = _selector[i];
181 int cur = _selector[0];
182 for (
int i = 0; i < new_hash.
size(); i++) {
183 if (new_hash[i] == - 1) {
188 _cst_hash.
swap(new_hash);
191 static void atc(
Timer *timer,
void *user_data) {
192 LoadBalancer* lb = (LoadBalancer*)user_data;
193 uint64_t metric_tot = 0;
194 for (
int i = 0; i < lb->_loads.size(); i++) {
195 metric_tot += lb->get_load_metric(i);
197 uint64_t avg = metric_tot / lb->_dsts.size();
200 for (
int i = 0; i < lb->_dsts.size(); i++) {
201 float l = lb->get_load_metric(i);
216 buckets = 10 * avg / ((1 - lb->_alpha) * l + lb->_alpha*avg);
217 if (buckets > 30) buckets = 30;
218 else if (buckets < 2) buckets = 2;
223 lb->set_weights(new_weight.
data());
227 int hash_ip(
const Packet* p,
int n) {
228 const unsigned char *data = p->
data();
231 if ((
int)p->
length() < o + l)
235 for (
int i = o; i < o + l; i++)
237 if (n == 2 || n == 4 || n == 8)
238 h = (d ^ (d>>4)) & (n-1);
253 bool has_cst_buckets;
256 bool force_track_load;
262 .
read_or_set(
"FORCE_TRACK_LOAD", force_track_load,
false)
265 .read_or_set(
"AWRR_ALPHA", alpha, 0).consume();
271 _autoscale = autoscale;
272 _force_track_load = force_track_load;
273 if (has_cst_buckets) {
274 _cst_hash.
resize(cst_buckets, -1);
277 set_mode(lb_mode, lst_mode, lb, awrr_timer, nserver);
283 if (_spares.
size() == 0) {
284 click_chatter(
"No server to add!");
287 int spare = _spares.
front();
290 int id = click_random() % _selector.
size();
293 for (
int i = 0; i < _selector.
size(); i++) {
299 _selector.
swap(news);
300 if (_mode_case == constant_hash_agg) {
305 void remove_server() {
306 if (_selector.
size() == 0) {
307 click_chatter(
"No server to remove!");
311 int id = click_random() % _selector.
size();
312 int removed = _selector[id];
315 for (
int i = 0; i < _selector.
size(); i++) {
322 _selector.
swap(news);
323 if (_mode_case == constant_hash_agg) {
329 h_load,h_load_raw,h_nb_total_servers,h_nb_active_servers,h_load_conn,h_load_packets,h_load_bytes,h_add_server,h_remove_server,h_lb_max
333 int lb_handler(
int op,
String &data,
void *r_thunk,
void* w_thunk,
ErrorHandler *errh) {
335 LoadBalancer *cs =
this;
337 switch((uintptr_t) r_thunk) {
341 int i = atoi(data.
c_str());
342 if (cs->_loads.size() <= i) {
345 acc << cs->_loads[i].cpu_load ;
348 for (
int i = 0; i < cs->_dsts.size(); i ++) {
349 if (cs->_loads.size() <= i) {
352 acc << cs->_loads[i].cpu_load ;
354 acc << (i == cs->_dsts.size() -1?
"":
" ");
362 switch((uintptr_t)w_thunk) {
368 while (s.length() > 0) {
369 int ntoken = s.find_left(
',');
371 ntoken = s.length() - 1;
372 int pos = s.find_left(
':');
373 int server_id = atoi(s.substring(0,pos).c_str());
374 int server_load = atoi(s.substring(pos + 1, ntoken).c_str());
376 if (cs->_loads.size() <= server_id) {
377 click_chatter(
"Invalid server id %d", server_id);
380 if ((uintptr_t)w_thunk == h_load_raw) {
381 cs->_loads[server_id].cpu_load = server_load - cs->_loads[server_id].raw_cpu_load;
382 cs->_loads[server_id].raw_cpu_load = server_load;
384 cs->_loads[server_id].cpu_load = server_load;
386 s = s.substring(ntoken + 1);
400 int lb_write_handler(
402 LoadBalancer *cs =
this;
403 switch((uintptr_t) thunk) {
408 case h_remove_server: {
417 lb_read_handler(
void *thunk) {
418 LoadBalancer *cs =
this;
420 switch((uintptr_t) thunk) {
421 case h_nb_active_servers: {
422 return String(cs->_selector.size());
424 case h_nb_total_servers: {
425 return String(cs->_dsts.size());
430 for (
int i = 0; i < cs->_dsts.size(); i ++) {
431 acc << cs->get_load_metric(i,connections) << (i == cs->_dsts.size() -1?
"":
" ");
434 case h_load_packets:{
436 for (
int i = 0; i < cs->_dsts.size(); i ++) {
437 acc << cs->get_load_metric(i,packets) << (i == cs->_dsts.size() -1?
"":
" ");
442 for (
int i = 0; i <cs-> _dsts.
size(); i ++) {
443 acc << cs->get_load_metric(i,bytes) << (i == cs->_dsts.size() -1?
"":
" ");
453 for (
int i = 0; i < _selector.
size(); i++) {
454 tot += _loads[_selector[i]].cpu_load;
456 double avg = tot / _loads.
size();
457 if (avg > 80 && _spares.
size() > 0) {
458 click_chatter(
"Load is %f, adding a server", avg);
460 }
else if (avg < 40 && _selector.
size() > 1) {
462 click_chatter(
"Load is %f, removing a server", avg);
468 void add_lb_handlers(
Element* _e) {
471 e->set_handler(
"load_raw",
Handler::f_write, e->handler, h_load_raw, h_load_raw);
472 e->add_read_handler(
"nb_active_servers", e->read_handler, h_nb_active_servers);
473 e->add_read_handler(
"nb_total_servers", e->read_handler, h_nb_total_servers);
474 e->add_read_handler(
"load_conn", e->read_handler, h_load_conn);
475 e->add_read_handler(
"load_bytes", e->read_handler, h_load_bytes);
476 e->add_read_handler(
"load_packets", e->read_handler, h_load_packets);
477 e->add_write_handler(
"remove_server", e->write_handler, h_remove_server);
478 e->add_write_handler(
"add_server", e->write_handler, h_add_server);
481 void set_mode(
String mode,
String metric=
"cpu",
Element* owner=0,
int awrr_timer_interval = -1,
int nserver = 0) {
482 auto item = modetrans.
find(mode);
483 _mode_case = item.value();
484 if (_mode_case == weighted_round_robin || _mode_case == auto_weighted_round_robin || _mode_case == table) {
485 auto &wh = _weights_helper.write_begin();
486 wh.resize(_dsts.
size());
487 for(
int i=0; i<_dsts.
size(); i++) {
490 _weights_helper.write_commit();
493 _lst_case = lsttrans.
find(metric).value();
494 _track_load = ((isLoadBased(_mode_case)) && _lst_case != cpu) || _force_track_load;
496 if (_mode_case == auto_weighted_round_robin) {
499 _awrr_interval = awrr_timer_interval;
507 nserver= _dsts.
size();
511 if (_mode_case == round_robin ||_mode_case == weighted_round_robin || _mode_case == auto_weighted_round_robin) {
512 int p = nserver / _current.
weight();
515 for (
int i = 0; i < _current.
weight(); i++) {
516 _current.
get_value(i) = (i * p) % nserver;
521 for (
int i = nserver; i < _dsts.
size(); i++)
523 for (
int i = 0; i < nserver; i++)
526 if (_mode_case == constant_hash_agg) {
527 if (_cst_hash.
size() == 0)
533 CLICK_ASSERT_ALIGNED(_loads.
data());
537 void set_weights(
unsigned weigths_value[]) {
539 for(
int i=0; i<_dsts.
size(); i++) {
540 for (
unsigned j=0; j<weigths_value[i]; j++) {
544#if __cplusplus > 201402L
545 std::shuffle(weights_helper.
begin(), weights_helper.
end(), _gen);
547 std::random_shuffle(weights_helper.
begin(), weights_helper.
end());
549 auto& v = _weights_helper.write_begin();
551 _weights_helper.write_commit();
554 inline int pick_server(
const Packet* p) {
558 if ((
unsigned)*_current == (
unsigned)_selector.
size()) {
564 case direct_hash_crc: {
565 IPFlow5ID srv = IPFlow5ID(p);
566 unsigned server_val = ipv4_hash_crc(&srv,
sizeof(srv), 0);
567 server_val = ((server_val >> 16) ^ (server_val & 65535)) % _selector.
size();
571 case direct_hash_agg: {
572 unsigned server_val = AGGREGATE_ANNO(p);
573 server_val = ((server_val >> 16) ^ (server_val & 65535)) % _selector.
size();
576 case direct_hash_ip: {
577 unsigned server_val = hash_ip(p, _selector.
size());
581 unsigned server_val = IPFlowID(p,
false).hashcode();
582 server_val = ((server_val >> 16) ^ (server_val & 65535)) % _selector.
size();
585 case constant_hash_agg: {
586 unsigned server_val = AGGREGATE_ANNO(p);
587 server_val = ((server_val >> 16) ^ (server_val & 65535)) % _cst_hash.
size();
590 case auto_weighted_round_robin:
591 case weighted_round_robin: {
593 auto & wh = _weights_helper.read_begin();
594 int b = (*_current)++;
595 if (*_current >= wh.size())
600 int server = wh.unchecked_at(b);
601 _weights_helper.read_end();
606 std::function<bool(MachineLoad,MachineLoad)> comp;
610 comp = [&](MachineLoad m,MachineLoad n)->
bool {
return m.connection_load<n.connection_load;};
614 comp = [&](MachineLoad m,MachineLoad n)->
bool {
return m.bytes_load<n.bytes_load;};
618 comp = [&](MachineLoad m,MachineLoad n)->
bool {
return m.packets_load<n.packets_load;};
622 comp = [&](MachineLoad m,MachineLoad n)->
bool {
return m.cpu_load<n.cpu_load;};
637 auto result = std::min_element(std::begin(_loads), std::end(_loads),comp);
638 int sid = std::distance(std::begin(_loads), result);
648 return (_loads[a].connection_load > _loads[b].connection_load?b:a);
651 return (_loads[a].bytes_load > _loads[b].bytes_load?b:a);
654 return (_loads[a].packets_load > _loads[b].packets_load?b:a);
657 int ret = (_loads[a].cpu_load > _loads[b].cpu_load?b:a);
667 return hash_4tuple(p, _selector.
size());
671 auto & wh = _weights_helper.read_begin();
672 int hash = hash_4tuple(p, wh.size());
674 _weights_helper.read_end();
Argument parser class.
Definition args.hh:249
Args & read_or_set(const char *keyword, T &x, const V &default_value)
Read an argument using the default parser, or set it to a default value if the argument is was not su...
Definition args.hh:403
Args & read(const char *keyword, T &x)
Read an argument using its type's default parser.
Definition args.hh:371
bool read_status() const
Return true iff the last read request succeeded.
Definition args.hh:739
Args & bind(Vector< String > &conf)
Bind this parser's arguments to conf.
Definition args.cc:171
Base class for Click elements.
Definition element.hh:42
Error reporting class.
Definition error.hh:90
@ f_read
Handler supports read operations.
Definition handler.hh:22
@ f_read_param
Read handler takes parameters.
Definition handler.hh:24
@ f_write
Handler supports write operations.
Definition handler.hh:23
Hash table template.
Definition hashtable.hh:570
const_iterator find(key_const_reference key) const
Return an iterator for the element with key key, if any.
Definition hashtable.hh:693
A network packet.
Definition packet.hh:68
const unsigned char * data() const
Return the packet's data pointer.
Definition packet.hh:1207
uint32_t length() const
Return the packet's length.
Definition packet.hh:1276
Efficiently build up Strings from pieces.
Definition straccum.hh:28
String take_string()
Return a String object with this StringAccum's contents.
Definition straccum.cc:211
A string of characters.
Definition string.hh:22
const char * c_str() const
Null-terminate the string.
Definition string.hh:519
static bool isSyn(Packet *packet)
Indicate whether a packet is a SYN packet.
Definition tcphelper.hh:444
static bool isRst(Packet *packet)
Indicate whether a packet is a RST packet.
Definition tcphelper.hh:454
static bool isFin(Packet *packet)
Indicate whether a packet is a FIN packet.
Definition tcphelper.hh:449
Triggers execution at a given time.
Definition timer.hh:14
void schedule_after(const Timestamp &delta)
Schedule the timer to fire delta time in the future.
Definition timer.cc:310
void initialize(Element *owner, bool quiet=false)
Initialize the timer.
Definition timer.cc:238
void reschedule_after_msec(uint32_t delta_msec)
Schedule the timer to fire delta_msec milliseconds after its previous expiry.
Definition timer.hh:280
static Timestamp make_msec(seconds_type sec, uint32_t msec)
Return a timestamp representing sec seconds plus msec milliseconds.
Definition timestamp.hh:332
Vector template.
Definition vector.hh:111
T * data()
Return a pointer to the vector's data.
Definition vector.hh:376
T & front()
Return a reference to the first element.
Definition vector.hh:321
bool reserve(size_type n)
Reserve space for at least n more elements.
Definition vector.hh:487
void pop_front()
Remove the first element.
Definition vector.hh:434
iterator end()
Return an iterator for the end of the vector.
Definition vector.hh:266
iterator begin()
Return an iterator for the first element in the vector.
Definition vector.hh:253
void resize(size_type n, value_argument_type v=T())
Resize the vector to contain n elements.
Definition vector.hh:390
void swap(Vector< T, ALIGNMENT > &x)
Swap the contents of this vector and x.
Definition vector.hh:493
T & unchecked_at(size_type i)
Return a reference to the ith element.
Definition vector.hh:350
size_type size() const
Return the number of elements.
Definition vector.hh:231
void push_back(value_argument_type v)
Append element v.
Definition vector.hh:399
A 64-bit integer with support for atomic operations.
Definition atomic.hh:137
unsigned weight() const
Definition sync.hh:203
T & get_value(int i) const
Definition sync.hh:190
Definition multithread.hh:284
Click's hash table container template.
Click's StringAccum class, used to construct Strings efficiently from pieces.
Support for Timer, which triggers execution at a given time.