--- a/native/common/jk_ajp_common.c +++ a/native/common/jk_ajp_common.c @@ -3043,6 +3043,9 @@ int ajp_init(jk_worker_t *pThis, p->retries = jk_get_worker_retries(props, p->name, JK_RETRIES); + p->lb_retries = + jk_get_worker_lb_retries(props, p->name, + JK_LB_RETRIES); p->max_packet_size = jk_get_max_packet_size(props, p->name); --- a/native/common/jk_ajp_common.h +++ a/native/common/jk_ajp_common.h @@ -370,6 +370,13 @@ struct ajp_worker */ int retries; + /* + * Public property used in load balancer workers, meaning + * the maximum number of failover attempts between ajp + * workers of cluster. + */ + int lb_retries; + unsigned int max_packet_size; /* Maximum AJP Packet size */ int retry_interval; /* Number of milliseconds to sleep before doing a retry */ --- a/native/common/jk_lb_worker.c +++ a/native/common/jk_lb_worker.c @@ -1225,6 +1225,7 @@ static int JK_METHOD service(jk_endpoint_t *e, if (p->worker->sequence < p->worker->s->h.sequence) jk_lb_pull(p->worker, JK_FALSE, l); for (i = 0; i < num_of_workers; i++) { + jk_log(l, JK_LOG_DEBUG, "LB - num_of_workers: %d, retry: %d, lb_retries: %d", num_of_workers, i, p->worker->lb_retries); lb_sub_worker_t *rec = &(p->worker->lb_workers[i]); ajp_worker_t *aw = (ajp_worker_t *)rec->worker->worker_private; if (rec->s->state == JK_LB_STATE_BUSY) { @@ -1271,7 +1272,10 @@ static int JK_METHOD service(jk_endpoint_t *e, "service sticky_session=%d id='%s'", p->worker->sticky_session, sessionid ? sessionid : "empty"); - while (recoverable == JK_TRUE) { + while (recoverable == JK_TRUE && attempt <= p->worker->lb_retries) { + if (JK_IS_DEBUG_LEVEL(l)) + jk_log(l, JK_LOG_DEBUG, "attempt %d, max attempts %d", + attempt, p->worker->lb_retries); if (attempt >= num_of_workers) { retry++; if (retry >= p->worker->retries) { @@ -1903,6 +1907,8 @@ static int JK_METHOD init(jk_worker_t *pThis, p->worker.we = we; p->retries = jk_get_worker_retries(props, p->name, JK_RETRIES); + p->lb_retries = jk_get_worker_lb_retries(props, p->name, + JK_LB_RETRIES); p->retry_interval = jk_get_worker_retry_interval(props, p->name, JK_SLEEP_DEF); --- a/native/common/jk_lb_worker.h +++ a/native/common/jk_lb_worker.h @@ -187,6 +187,7 @@ struct lb_worker int error_escalation_time; int max_reply_timeouts; int retries; + int lb_retries; int retry_interval; int lbmethod; int lblock; --- a/native/common/jk_service.h +++ a/native/common/jk_service.h @@ -36,6 +36,7 @@ #include "jk_msg_buff.h" #define JK_RETRIES 2 +#define JK_LB_RETRIES 2 #ifdef __cplusplus extern "C" --- a/native/common/jk_util.c +++ a/native/common/jk_util.c @@ -111,6 +111,7 @@ #define DEFAULT_WORKER_TYPE JK_AJP13_WORKER_NAME #define SECRET_KEY_OF_WORKER "secretkey" #define RETRIES_OF_WORKER "retries" +#define LB_RETRIES_OF_WORKER "lb_retries" #define STATUS_FAIL_OF_WORKER "fail_on_status" #define DEFAULT_WORKER JK_AJP13_WORKER_NAME @@ -232,6 +233,7 @@ static const char *unique_properties[] = { STYLE_SHEET_OF_WORKER, READ_ONLY_OF_WORKER, RETRIES_OF_WORKER, + LB_RETRIES_OF_WORKER, WORKER_MAINTAIN_PROPERTY_NAME, NAMESPACE_OF_WORKER, XML_NAMESPACE_OF_WORKER, @@ -342,6 +344,7 @@ static const char *supported_properties[] = { BAD_RATING_OF_WORKER, SECRET_KEY_OF_WORKER, RETRIES_OF_WORKER, + LB_RETRIES_OF_WORKER, STATUS_FAIL_OF_WORKER, LIST_PROPERTY_NAME, MAINTAIN_PROPERTY_NAME, @@ -1245,6 +1248,24 @@ int jk_get_worker_retries(jk_map_t *m, const char *wname, int def) return rv; } +int jk_get_worker_lb_retries(jk_map_t *m, const char *wname, int def) +{ + char buf[1024]; + int rv; + if (!m || !wname) { + return -1; + } + + MAKE_WORKER_PARAM(LB_RETRIES_OF_WORKER); + + rv = jk_map_get_int(m, buf, def); + if (rv < 1) + rv = 1; + + return rv; +} + + int jk_get_worker_recovery_opts(jk_map_t *m, const char *wname, int def) { char buf[PARAM_BUFFER_SIZE];