[MEDIUM] implement SMTP health checks

Peter van Dijk contributed this patch which implements the "smtpchk"
option, which is to SMTP what "httpchk" is to HTTP. By default, it sends
"HELO localhost" to the servers, and waits for the 250 message, but it
can also send a specific request.
This commit is contained in:
Willy Tarreau 2007-05-08 23:50:35 +02:00
parent f3d259868b
commit 23677908dd
6 changed files with 53 additions and 3 deletions

View File

@ -1034,6 +1034,13 @@ the response is considered as valid. Note that Apache does not generate a log
when it receives only an HELLO message, which makes this type of message when it receives only an HELLO message, which makes this type of message
perfectly suit this need. perfectly suit this need.
Version 1.3.10 introduced the SMTP health check. By default, it sends
"HELO localhost" to the servers, and waits for the 250 message. Note that it
can also send a specific request :
- option smtpchk -> sends "HELO localhost"
- option smtpchk EHLO mail.mydomain.com -> sends this ESMTP greeting
See examples below. See examples below.
Since version 1.1.17, it is possible to specify backup servers. These servers Since version 1.1.17, it is possible to specify backup servers. These servers

View File

@ -1044,6 +1044,14 @@ r
lorsqu'il reçoit des messages HELLO, ce qui en fait un type de message lorsqu'il reçoit des messages HELLO, ce qui en fait un type de message
parfaitement adapté à ce besoin. parfaitement adapté à ce besoin.
La version 1.3.10 est accompagnée d'un nouveau test d'état pour le SMTP. Par
défaut, il consiste à envoyer "HELO localhost" aux serveurs, et à attendre le
message "250" en retour. Notez qu'il peut aussi envoyer une requête plus
spécifique :
- option smtpchk -> envoie "HELO localhost"
- option smtpchk EHLO mail.mydomain.com -> envoie ce message ESMTP
Voir les exemples ci-après. Voir les exemples ci-après.
Depuis la version 1.1.17, il est possible de définir des serveurs de secours, Depuis la version 1.1.17, il est possible de définir des serveurs de secours,

View File

@ -92,6 +92,7 @@
#define DEF_FALLTIME 3 #define DEF_FALLTIME 3
#define DEF_RISETIME 2 #define DEF_RISETIME 2
#define DEF_CHECK_REQ "OPTIONS / HTTP/1.0\r\n\r\n" #define DEF_CHECK_REQ "OPTIONS / HTTP/1.0\r\n\r\n"
#define DEF_SMTP_CHECK_REQ "HELO localhost\r\n"
/* Default connections limit. /* Default connections limit.
* *

View File

@ -59,6 +59,7 @@
#define PR_O_TCPSPLICE 0x08000000 /* delegate data transfer to linux kernel's tcp_splice */ #define PR_O_TCPSPLICE 0x08000000 /* delegate data transfer to linux kernel's tcp_splice */
#define PR_O_BALANCE_UH 0x10000000 /* balance on URI hash */ #define PR_O_BALANCE_UH 0x10000000 /* balance on URI hash */
#define PR_O_BALANCE (PR_O_BALANCE_RR | PR_O_BALANCE_SH | PR_O_BALANCE_UH) #define PR_O_BALANCE (PR_O_BALANCE_RR | PR_O_BALANCE_SH | PR_O_BALANCE_UH)
#define PR_O_SMTP_CHK 0x20000000 /* use SMTP EHLO check for server health - pvandijk@vision6.com.au */
#endif /* _TYPES_BACKEND_H */ #endif /* _TYPES_BACKEND_H */

View File

@ -1029,13 +1029,14 @@ int cfg_parse_listen(const char *file, int linenum, char **args)
if (curproxy->check_req != NULL) { if (curproxy->check_req != NULL) {
free(curproxy->check_req); free(curproxy->check_req);
} }
curproxy->options |= PR_O_HTTP_CHK;
curproxy->options &= ~PR_O_SSL3_CHK; curproxy->options &= ~PR_O_SSL3_CHK;
curproxy->options &= ~PR_O_SMTP_CHK;
curproxy->options |= PR_O_HTTP_CHK;
if (!*args[2]) { /* no argument */ if (!*args[2]) { /* no argument */
curproxy->check_req = strdup(DEF_CHECK_REQ); /* default request */ curproxy->check_req = strdup(DEF_CHECK_REQ); /* default request */
curproxy->check_len = strlen(DEF_CHECK_REQ); curproxy->check_len = strlen(DEF_CHECK_REQ);
} else if (!*args[3]) { /* one argument : URI */ } else if (!*args[3]) { /* one argument : URI */
int reqlen = strlen(args[2]) + strlen("OPTIONS / HTTP/1.0\r\n\r\n"); int reqlen = strlen(args[2]) + strlen("OPTIONS HTTP/1.0\r\n\r\n") + 1;
curproxy->check_req = (char *)malloc(reqlen); curproxy->check_req = (char *)malloc(reqlen);
curproxy->check_len = snprintf(curproxy->check_req, reqlen, curproxy->check_len = snprintf(curproxy->check_req, reqlen,
"OPTIONS %s HTTP/1.0\r\n\r\n", args[2]); /* URI to use */ "OPTIONS %s HTTP/1.0\r\n\r\n", args[2]); /* URI to use */
@ -1060,8 +1061,35 @@ int cfg_parse_listen(const char *file, int linenum, char **args)
free(curproxy->check_req); free(curproxy->check_req);
} }
curproxy->options &= ~PR_O_HTTP_CHK; curproxy->options &= ~PR_O_HTTP_CHK;
curproxy->options &= ~PR_O_SMTP_CHK;
curproxy->options |= PR_O_SSL3_CHK; curproxy->options |= PR_O_SSL3_CHK;
} }
else if (!strcmp(args[1], "smtpchk")) {
/* use SMTP request to check servers' health */
if (curproxy->check_req != NULL) {
free(curproxy->check_req);
}
curproxy->options &= ~PR_O_HTTP_CHK;
curproxy->options &= ~PR_O_SSL3_CHK;
curproxy->options |= PR_O_SMTP_CHK;
if (!*args[2] || !*args[3]) { /* no argument or incomplete EHLO host */
curproxy->check_req = strdup(DEF_SMTP_CHECK_REQ); /* default request */
curproxy->check_len = strlen(DEF_SMTP_CHECK_REQ);
} else { /* ESMTP EHLO, or SMTP HELO, and a hostname */
if (!strcmp(args[2], "EHLO") || !strcmp(args[2], "HELO")) {
int reqlen = strlen(args[2]) + strlen(args[3]) + strlen(" \r\n") + 1;
curproxy->check_req = (char *)malloc(reqlen);
curproxy->check_len = snprintf(curproxy->check_req, reqlen,
"%s %s\r\n", args[2], args[3]); /* HELO hostname */
} else {
/* this just hits the default for now, but you could potentially expand it to allow for other stuff
though, it's unlikely you'd want to send anything other than an EHLO or HELO */
curproxy->check_req = strdup(DEF_SMTP_CHECK_REQ); /* default request */
curproxy->check_len = strlen(DEF_SMTP_CHECK_REQ);
}
}
}
else if (!strcmp(args[1], "forwardfor")) { else if (!strcmp(args[1], "forwardfor")) {
/* insert x-forwarded-for field, but not for the /* insert x-forwarded-for field, but not for the
* IP address listed as an except. * IP address listed as an except.

View File

@ -122,7 +122,8 @@ static int event_srv_chk_w(int fd)
if (s->result != -1) { if (s->result != -1) {
/* we don't want to mark 'UP' a server on which we detected an error earlier */ /* we don't want to mark 'UP' a server on which we detected an error earlier */
if ((s->proxy->options & PR_O_HTTP_CHK) || if ((s->proxy->options & PR_O_HTTP_CHK) ||
(s->proxy->options & PR_O_SSL3_CHK)) { (s->proxy->options & PR_O_SSL3_CHK) ||
(s->proxy->options & PR_O_SMTP_CHK)) {
int ret; int ret;
/* we want to check if this host replies to HTTP or SSLv3 requests /* we want to check if this host replies to HTTP or SSLv3 requests
* so we'll send the request, and won't wake the checker up now. * so we'll send the request, and won't wake the checker up now.
@ -252,6 +253,10 @@ static int event_srv_chk_r(int fd)
/* SSLv3 alert or handshake */ /* SSLv3 alert or handshake */
result = 1; result = 1;
} }
else if ((s->proxy->options & PR_O_SMTP_CHK) && (len >= 3) &&
(reply[0] == '2')) /* 2xx (should be 250) */ {
result = 1;
}
if (result == -1) if (result == -1)
fdtab[fd].state = FD_STERROR; fdtab[fd].state = FD_STERROR;