📄 mod_proxy_balancer.c
字号:
/* Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. *//* Load balancer module for Apache proxy */#define CORE_PRIVATE#include "mod_proxy.h"#include "scoreboard.h"#include "ap_mpm.h"#include "apr_version.h"#include "apr_hooks.h"#include "apr_uuid.h"module AP_MODULE_DECLARE_DATA proxy_balancer_module;static char balancer_nonce[APR_UUID_FORMATTED_LENGTH + 1];static int proxy_balancer_canon(request_rec *r, char *url){ char *host, *path; char *search = NULL; const char *err; apr_port_t port = 0; if (strncasecmp(url, "balancer:", 9) == 0) { url += 9; } else { return DECLINED; } ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, "proxy: BALANCER: canonicalising URL %s", url); /* do syntatic check. * We break the URL into host, port, path, search */ err = ap_proxy_canon_netloc(r->pool, &url, NULL, NULL, &host, &port); if (err) { ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "error parsing URL %s: %s", url, err); return HTTP_BAD_REQUEST; } /* * now parse path/search args, according to rfc1738: * process the path. With proxy-noncanon set (by * mod_proxy) we use the raw, unparsed uri */ if (apr_table_get(r->notes, "proxy-nocanon")) { path = url; /* this is the raw path */ } else { path = ap_proxy_canonenc(r->pool, url, strlen(url), enc_path, 0, r->proxyreq); search = r->args; } if (path == NULL) return HTTP_BAD_REQUEST; r->filename = apr_pstrcat(r->pool, "proxy:balancer://", host, "/", path, (search) ? "?" : "", (search) ? search : "", NULL); return OK;}static int init_balancer_members(proxy_server_conf *conf, server_rec *s, proxy_balancer *balancer){ int i; proxy_worker *workers; int worker_is_initialized; proxy_worker_stat *slot; workers = (proxy_worker *)balancer->workers->elts; for (i = 0; i < balancer->workers->nelts; i++) { worker_is_initialized = PROXY_WORKER_IS_INITIALIZED(workers); if (!worker_is_initialized) { /* * If the worker is not initialized check whether its scoreboard * slot is already initialized. */ slot = (proxy_worker_stat *) ap_get_scoreboard_lb(workers->id); if (slot) { worker_is_initialized = slot->status & PROXY_WORKER_INITIALIZED; } else { worker_is_initialized = 0; } } ap_proxy_initialize_worker_share(conf, workers, s); ap_proxy_initialize_worker(workers, s); if (!worker_is_initialized) { /* Set to the original configuration */ workers->s->lbstatus = workers->s->lbfactor = (workers->lbfactor ? workers->lbfactor : 1); workers->s->lbset = workers->lbset; } ++workers; } /* Set default number of attempts to the number of * workers. */ if (!balancer->max_attempts_set && balancer->workers->nelts > 1) { balancer->max_attempts = balancer->workers->nelts - 1; balancer->max_attempts_set = 1; } return 0;}/* Retrieve the parameter with the given name * Something like 'JSESSIONID=12345...N' */static char *get_path_param(apr_pool_t *pool, char *url, const char *name, int scolon_sep){ char *path = NULL; char *pathdelims = "?&"; if (scolon_sep) { pathdelims = ";?&"; } for (path = strstr(url, name); path; path = strstr(path + 1, name)) { path += strlen(name); if (*path == '=') { /* * Session path was found, get it's value */ ++path; if (strlen(path)) { char *q; path = apr_strtok(apr_pstrdup(pool, path), pathdelims, &q); return path; } } } return NULL;}static char *get_cookie_param(request_rec *r, const char *name){ const char *cookies; const char *start_cookie; if ((cookies = apr_table_get(r->headers_in, "Cookie"))) { for (start_cookie = ap_strstr_c(cookies, name); start_cookie; start_cookie = ap_strstr_c(start_cookie + 1, name)) { if (start_cookie == cookies || start_cookie[-1] == ';' || start_cookie[-1] == ',' || isspace(start_cookie[-1])) { start_cookie += strlen(name); while(*start_cookie && isspace(*start_cookie)) ++start_cookie; if (*start_cookie == '=' && start_cookie[1]) { /* * Session cookie was found, get it's value */ char *end_cookie, *cookie; ++start_cookie; cookie = apr_pstrdup(r->pool, start_cookie); if ((end_cookie = strchr(cookie, ';')) != NULL) *end_cookie = '\0'; if((end_cookie = strchr(cookie, ',')) != NULL) *end_cookie = '\0'; return cookie; } } } } return NULL;}/* Find the worker that has the 'route' defined */static proxy_worker *find_route_worker(proxy_balancer *balancer, const char *route, request_rec *r){ int i; int checking_standby; int checked_standby; proxy_worker *worker; checking_standby = checked_standby = 0; while (!checked_standby) { worker = (proxy_worker *)balancer->workers->elts; for (i = 0; i < balancer->workers->nelts; i++, worker++) { if ( (checking_standby ? !PROXY_WORKER_IS_STANDBY(worker) : PROXY_WORKER_IS_STANDBY(worker)) ) continue; if (*(worker->s->route) && strcmp(worker->s->route, route) == 0) { if (worker && PROXY_WORKER_IS_USABLE(worker)) { return worker; } else { /* * If the worker is in error state run * retry on that worker. It will be marked as * operational if the retry timeout is elapsed. * The worker might still be unusable, but we try * anyway. */ ap_proxy_retry_worker("BALANCER", worker, r->server); if (PROXY_WORKER_IS_USABLE(worker)) { return worker; } else { /* * We have a worker that is unusable. * It can be in error or disabled, but in case * it has a redirection set use that redirection worker. * This enables to safely remove the member from the * balancer. Of course you will need some kind of * session replication between those two remote. */ if (*worker->s->redirect) { proxy_worker *rworker = NULL; rworker = find_route_worker(balancer, worker->s->redirect, r); /* Check if the redirect worker is usable */ if (rworker && !PROXY_WORKER_IS_USABLE(rworker)) { /* * If the worker is in error state run * retry on that worker. It will be marked as * operational if the retry timeout is elapsed. * The worker might still be unusable, but we try * anyway. */ ap_proxy_retry_worker("BALANCER", rworker, r->server); } if (rworker && PROXY_WORKER_IS_USABLE(rworker)) return rworker; } } } } } checked_standby = checking_standby++; } return NULL;}static proxy_worker *find_session_route(proxy_balancer *balancer, request_rec *r, char **route, char **sticky_used, char **url){ proxy_worker *worker = NULL; char *sticky, *sticky_path, *path; if (!balancer->sticky) return NULL; sticky = sticky_path = apr_pstrdup(r->pool, balancer->sticky); if ((path = strchr(sticky, '|'))) { *path++ = '\0'; sticky_path = path; } /* Try to find the sticky route inside url */ *sticky_used = sticky_path; *route = get_path_param(r->pool, *url, sticky_path, balancer->scolonsep); if (!*route) { *route = get_cookie_param(r, sticky); *sticky_used = sticky; } ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, "proxy: BALANCER: Found value %s for " "stickysession %s", *route, balancer->sticky); /* * If we found a value for sticksession, find the first '.' within. * Everything after '.' (if present) is our route. */ if ((*route) && ((*route = strchr(*route, '.')) != NULL )) (*route)++; if ((*route) && (**route)) { ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, "proxy: BALANCER: Found route %s", *route); /* We have a route in path or in cookie * Find the worker that has this route defined. */ worker = find_route_worker(balancer, *route, r); if (worker && strcmp(*route, worker->s->route)) { /* * Notice that the route of the worker chosen is different from * the route supplied by the client. */ apr_table_setn(r->subprocess_env, "BALANCER_ROUTE_CHANGED", "1"); ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, "proxy: BALANCER: Route changed from %s to %s", *route, worker->s->route); } return worker; } else return NULL;}static proxy_worker *find_best_worker(proxy_balancer *balancer, request_rec *r){ proxy_worker *candidate = NULL; apr_status_t rv; if ((rv = PROXY_THREAD_LOCK(balancer)) != APR_SUCCESS) {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -