mirror of
https://git.haproxy.org/git/haproxy.git/
synced 2025-08-06 07:07:04 +02:00
MEDIUM: log/balance: support for the "hash" lb algorithm
hash lb algorithm can be configured with the "log-balance hash <cnv_list>" directive. With this algorithm, the user specifies a converter list with <cnv_list>. The produced log message will be passed as-is to the provided converter list, and the resulting hash will be used to select the log server that will receive the log message.
This commit is contained in:
parent
7251344748
commit
b30bd7adba
@ -8849,6 +8849,19 @@ log-balance <algorithm> [ <arguments> ]
|
|||||||
pool of available servers as it may avoid the hammering
|
pool of available servers as it may avoid the hammering
|
||||||
effect that could result from roundrobin in this situation.
|
effect that could result from roundrobin in this situation.
|
||||||
|
|
||||||
|
hash <arguments> should be found in the form: <cnv_list>
|
||||||
|
e.g.: log-balance hash <cnv_list>
|
||||||
|
|
||||||
|
Each log message will be passed to the converter list
|
||||||
|
specified in <cnv_list> (ie: "cnv1,cnv2..."), and it will
|
||||||
|
then be passed to haproxy hashing function according to
|
||||||
|
"hash-type" settings. The resulting hash will be used to
|
||||||
|
select the destination server among the ones declared in the
|
||||||
|
log backend. The goal of this algorithm is to be able to
|
||||||
|
extract a key within the final log message using string
|
||||||
|
converters and then be able to stick to the same server thanks
|
||||||
|
to the hash. Only "map-based" hashes are supported for now.
|
||||||
|
|
||||||
<arguments> is an optional list of arguments which may be needed by some
|
<arguments> is an optional list of arguments which may be needed by some
|
||||||
algorithms.
|
algorithms.
|
||||||
|
|
||||||
@ -8862,6 +8875,7 @@ log-balance <algorithm> [ <arguments> ]
|
|||||||
|
|
||||||
global
|
global
|
||||||
log backend@mylog-rrb local0 # send all logs to mylog-rrb backend
|
log backend@mylog-rrb local0 # send all logs to mylog-rrb backend
|
||||||
|
log backend@mylog-hash local0 # send all logs to mylog-hash backend
|
||||||
|
|
||||||
backend mylog-rrb
|
backend mylog-rrb
|
||||||
mode log
|
mode log
|
||||||
@ -8870,6 +8884,18 @@ log-balance <algorithm> [ <arguments> ]
|
|||||||
server s1 udp@127.0.0.1:514 # will receive 50% of log messages
|
server s1 udp@127.0.0.1:514 # will receive 50% of log messages
|
||||||
server s2 udp@127.0.0.1:514
|
server s2 udp@127.0.0.1:514
|
||||||
|
|
||||||
|
backend mylog-hash
|
||||||
|
mode log
|
||||||
|
|
||||||
|
# extract "METHOD URL PROTO" at the end of the log message,
|
||||||
|
# and let haproxy hash it so that log messages generated from
|
||||||
|
# similar requests get sent to the same syslog server:
|
||||||
|
log-balance hash 'field(-2,\")'
|
||||||
|
|
||||||
|
# server list here
|
||||||
|
server s1 127.0.0.1:514
|
||||||
|
#...
|
||||||
|
|
||||||
log-format <string>
|
log-format <string>
|
||||||
Specifies the log format string to use for traffic logs
|
Specifies the log format string to use for traffic logs
|
||||||
May be used in sections: defaults | frontend | listen | backend
|
May be used in sections: defaults | frontend | listen | backend
|
||||||
|
@ -147,6 +147,8 @@ static inline int srv_lb_status_changed(const struct server *srv)
|
|||||||
*/
|
*/
|
||||||
void set_backend_down(struct proxy *be);
|
void set_backend_down(struct proxy *be);
|
||||||
|
|
||||||
|
unsigned int gen_hash(const struct proxy* px, const char* key, unsigned long len);
|
||||||
|
|
||||||
#endif /* _HAPROXY_BACKEND_H */
|
#endif /* _HAPROXY_BACKEND_H */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -70,7 +70,7 @@ int be_lastsession(const struct proxy *be)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* helper function to invoke the correct hash method */
|
/* helper function to invoke the correct hash method */
|
||||||
static unsigned int gen_hash(const struct proxy* px, const char* key, unsigned long len)
|
unsigned int gen_hash(const struct proxy* px, const char* key, unsigned long len)
|
||||||
{
|
{
|
||||||
unsigned int hash;
|
unsigned int hash;
|
||||||
|
|
||||||
@ -2855,8 +2855,19 @@ int backend_parse_log_balance(const char **args, char **err, struct proxy *curpr
|
|||||||
curproxy->lbprm.algo &= ~BE_LB_ALGO;
|
curproxy->lbprm.algo &= ~BE_LB_ALGO;
|
||||||
curproxy->lbprm.algo |= BE_LB_ALGO_RND;
|
curproxy->lbprm.algo |= BE_LB_ALGO_RND;
|
||||||
}
|
}
|
||||||
|
else if (strcmp(args[0], "hash") == 0) {
|
||||||
|
if (!*args[1]) {
|
||||||
|
memprintf(err, "%s requires a converter list.", args[0]);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
curproxy->lbprm.algo &= ~BE_LB_ALGO;
|
||||||
|
curproxy->lbprm.algo |= BE_LB_ALGO_SMP;
|
||||||
|
|
||||||
|
ha_free(&curproxy->lbprm.arg_str);
|
||||||
|
curproxy->lbprm.arg_str = strdup(args[1]);
|
||||||
|
}
|
||||||
else {
|
else {
|
||||||
memprintf(err, "only supports 'roundrobin', 'sticky', 'random', options");
|
memprintf(err, "only supports 'roundrobin', 'sticky', 'random', 'hash' options");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -2576,8 +2576,8 @@ int cfg_parse_listen(const char *file, int linenum, char **args, int kwm)
|
|||||||
*/
|
*/
|
||||||
curproxy->lbprm.algo &= ~(BE_LB_HASH_TYPE | BE_LB_HASH_FUNC | BE_LB_HASH_MOD);
|
curproxy->lbprm.algo &= ~(BE_LB_HASH_TYPE | BE_LB_HASH_FUNC | BE_LB_HASH_MOD);
|
||||||
|
|
||||||
if (curproxy->mode != PR_MODE_TCP && curproxy->mode != PR_MODE_HTTP) {
|
if (curproxy->mode != PR_MODE_TCP && curproxy->mode != PR_MODE_HTTP && curproxy->mode != PR_MODE_SYSLOG) {
|
||||||
ha_alert("parsing [%s:%d] : '%s' requires TCP or HTTP mode.\n", file, linenum, args[0]);
|
ha_alert("parsing [%s:%d] : '%s' requires TCP, HTTP or LOG mode.\n", file, linenum, args[0]);
|
||||||
err_code |= ERR_ALERT | ERR_FATAL;
|
err_code |= ERR_ALERT | ERR_FATAL;
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
@ -3510,8 +3510,11 @@ int check_config_validity()
|
|||||||
curproxy->conf.args.line = 0;
|
curproxy->conf.args.line = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* "balance hash" needs to compile its expression */
|
/* "balance hash" needs to compile its expression
|
||||||
if ((curproxy->lbprm.algo & BE_LB_ALGO) == BE_LB_ALGO_SMP) {
|
* (log backends will handle this in proxy log postcheck)
|
||||||
|
*/
|
||||||
|
if (curproxy->mode != PR_MODE_SYSLOG &&
|
||||||
|
(curproxy->lbprm.algo & BE_LB_ALGO) == BE_LB_ALGO_SMP) {
|
||||||
int idx = 0;
|
int idx = 0;
|
||||||
const char *args[] = {
|
const char *args[] = {
|
||||||
curproxy->lbprm.arg_str,
|
curproxy->lbprm.arg_str,
|
||||||
|
78
src/log.c
78
src/log.c
@ -42,6 +42,7 @@
|
|||||||
#include <haproxy/stconn.h>
|
#include <haproxy/stconn.h>
|
||||||
#include <haproxy/stream.h>
|
#include <haproxy/stream.h>
|
||||||
#include <haproxy/time.h>
|
#include <haproxy/time.h>
|
||||||
|
#include <haproxy/hash.h>
|
||||||
#include <haproxy/tools.h>
|
#include <haproxy/tools.h>
|
||||||
|
|
||||||
/* global recv logs counter */
|
/* global recv logs counter */
|
||||||
@ -907,6 +908,65 @@ static int postcheck_log_backend(struct proxy *be)
|
|||||||
be->srv_act = 0;
|
be->srv_act = 0;
|
||||||
be->srv_bck = 0;
|
be->srv_bck = 0;
|
||||||
|
|
||||||
|
/* "log-balance hash" needs to compile its expression */
|
||||||
|
if ((be->lbprm.algo & BE_LB_ALGO) == BE_LB_ALGO_SMP) {
|
||||||
|
struct sample_expr *expr;
|
||||||
|
char *expr_str = NULL;
|
||||||
|
char *err_str = NULL;
|
||||||
|
int idx = 0;
|
||||||
|
|
||||||
|
/* only map-based hash method is supported for now */
|
||||||
|
if ((be->lbprm.algo & BE_LB_HASH_TYPE) != BE_LB_HASH_MAP) {
|
||||||
|
memprintf(&msg, "unsupported hash method (from \"hash-type\")");
|
||||||
|
err_code |= ERR_ALERT | ERR_FATAL;
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* a little bit of explanation about what we're going to do here:
|
||||||
|
* as the user gave us a list of converters, instead of the fetch+conv list
|
||||||
|
* tuple as we're used to, we need to insert a dummy fetch at the start of
|
||||||
|
* the converter list so that sample_parse_expr() is able to properly parse
|
||||||
|
* the expr. We're explicitly using str() as dummy fetch, since the input
|
||||||
|
* sample that will be passed to the converter list at runtime will be a
|
||||||
|
* string (the log message about to be sent). Doing so allows sample_parse_expr()
|
||||||
|
* to ensure that the provided converters will be compatible with string type.
|
||||||
|
*/
|
||||||
|
memprintf(&expr_str, "str(dummy),%s", be->lbprm.arg_str);
|
||||||
|
if (!expr_str) {
|
||||||
|
memprintf(&msg, "memory error during converter list argument parsing (from \"log-balance hash\")");
|
||||||
|
err_code |= ERR_ALERT | ERR_FATAL;
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
expr = sample_parse_expr((char*[]){expr_str, NULL}, &idx,
|
||||||
|
be->conf.file,
|
||||||
|
be->conf.line,
|
||||||
|
&err_str, NULL, NULL);
|
||||||
|
if (!expr) {
|
||||||
|
memprintf(&msg, "%s (from converter list argument in \"log-balance hash\")", err_str);
|
||||||
|
ha_free(&err_str);
|
||||||
|
err_code |= ERR_ALERT | ERR_FATAL;
|
||||||
|
ha_free(&expr_str);
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* We expect the log_message->conv_list expr to resolve as a binary-compatible
|
||||||
|
* value because its output will be passed to gen_hash() to compute the hash.
|
||||||
|
*
|
||||||
|
* So we check the last converter's output type to ensure that it can be
|
||||||
|
* converted into the expected type. Invalid output type will result in an
|
||||||
|
* error to prevent unexpected results during runtime.
|
||||||
|
*/
|
||||||
|
if (sample_casts[smp_expr_output_type(expr)][SMP_T_BIN] == NULL) {
|
||||||
|
memprintf(&msg, "invalid output type at the end of converter list for \"log-balance hash\" directive");
|
||||||
|
err_code |= ERR_ALERT | ERR_FATAL;
|
||||||
|
release_sample_expr(expr);
|
||||||
|
ha_free(&expr_str);
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
ha_free(&expr_str);
|
||||||
|
be->lbprm.expr = expr;
|
||||||
|
}
|
||||||
|
|
||||||
/* finish the initialization of proxy's servers */
|
/* finish the initialization of proxy's servers */
|
||||||
srv = be->srv;
|
srv = be->srv;
|
||||||
while (srv) {
|
while (srv) {
|
||||||
@ -2120,6 +2180,24 @@ static inline void __do_send_log_backend(struct proxy *be, struct log_header hdr
|
|||||||
/* random mode */
|
/* random mode */
|
||||||
targetid = statistical_prng() % nb_srv;
|
targetid = statistical_prng() % nb_srv;
|
||||||
}
|
}
|
||||||
|
else if ((be->lbprm.algo & BE_LB_ALGO) == BE_LB_ALGO_SMP) {
|
||||||
|
struct sample result;
|
||||||
|
|
||||||
|
/* log-balance hash */
|
||||||
|
memset(&result, 0, sizeof(result));
|
||||||
|
result.data.type = SMP_T_STR;
|
||||||
|
result.flags = SMP_F_CONST;
|
||||||
|
result.data.u.str.area = message;
|
||||||
|
result.data.u.str.data = size;
|
||||||
|
result.data.u.str.size = size + 1; /* with terminating NULL byte */
|
||||||
|
if (sample_process_cnv(be->lbprm.expr, &result)) {
|
||||||
|
/* gen_hash takes binary input, ensure that we provide such value to it */
|
||||||
|
if (result.data.type == SMP_T_BIN || sample_casts[result.data.type][SMP_T_BIN]) {
|
||||||
|
sample_casts[result.data.type][SMP_T_BIN](&result);
|
||||||
|
targetid = gen_hash(be, result.data.u.str.area, result.data.u.str.data) % nb_srv;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
skip_lb:
|
skip_lb:
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user