From: <tho...@us...> - 2014-05-02 16:52:47
|
Revision: 8166 http://sourceforge.net/p/bigdata/code/8166 Author: thompsonbry Date: 2014-05-02 16:52:45 +0000 (Fri, 02 May 2014) Log Message: ----------- Refactoring of the HA Load Balancer to expose an interface that can be used by an application to take over the rewrite of the Request-URI when the request will be proxied to another service. See the new IHARequestURIRewriter interface and the new REWRITER init-param for the HALoadBalancerSerlet. See #624 (HA LBS) Modified Paths: -------------- branches/BIGDATA_RELEASE_1_3_0/bigdata-jini/src/java/com/bigdata/journal/jini/ha/HAJournalServer.java branches/BIGDATA_RELEASE_1_3_0/bigdata-jini/src/test/com/bigdata/journal/jini/ha/zkClient.config branches/BIGDATA_RELEASE_1_3_0/bigdata-sails/src/java/com/bigdata/rdf/sail/webapp/HALoadBalancerServlet.java branches/BIGDATA_RELEASE_1_3_0/bigdata-sails/src/java/com/bigdata/rdf/sail/webapp/IHALoadBalancerPolicy.java branches/BIGDATA_RELEASE_1_3_0/bigdata-sails/src/java/com/bigdata/rdf/sail/webapp/lbs/AbstractLBSPolicy.java branches/BIGDATA_RELEASE_1_3_0/bigdata-sails/src/java/com/bigdata/rdf/sail/webapp/lbs/ServiceScore.java branches/BIGDATA_RELEASE_1_3_0/bigdata-sails/src/java/com/bigdata/rdf/sail/webapp/lbs/policy/NOPLBSPolicy.java branches/BIGDATA_RELEASE_1_3_0/bigdata-sails/src/java/com/bigdata/rdf/sail/webapp/lbs/policy/RoundRobinLBSPolicy.java branches/BIGDATA_RELEASE_1_3_0/bigdata-sails/src/java/com/bigdata/rdf/sail/webapp/lbs/policy/ganglia/GangliaLBSPolicy.java Added Paths: ----------- branches/BIGDATA_RELEASE_1_3_0/bigdata-sails/src/java/com/bigdata/rdf/sail/webapp/DefaultHARequestURIRewriter.java branches/BIGDATA_RELEASE_1_3_0/bigdata-sails/src/java/com/bigdata/rdf/sail/webapp/IHAPolicyLifeCycle.java branches/BIGDATA_RELEASE_1_3_0/bigdata-sails/src/java/com/bigdata/rdf/sail/webapp/IHARequestURIRewriter.java Modified: branches/BIGDATA_RELEASE_1_3_0/bigdata-jini/src/java/com/bigdata/journal/jini/ha/HAJournalServer.java =================================================================== --- branches/BIGDATA_RELEASE_1_3_0/bigdata-jini/src/java/com/bigdata/journal/jini/ha/HAJournalServer.java 2014-05-02 14:59:01 UTC (rev 8165) +++ branches/BIGDATA_RELEASE_1_3_0/bigdata-jini/src/java/com/bigdata/journal/jini/ha/HAJournalServer.java 2014-05-02 16:52:45 UTC (rev 8166) @@ -756,7 +756,20 @@ final Quorum<HAGlue, QuorumService<HAGlue>> quorum = (Quorum) new ZKQuorumImpl<HAGlue, HAQuorumService<HAGlue, HAJournal>>( replicationFactor); - // The HAJournal. + /** + * The HAJournal. + * + * FIXME This step can block for a long time if we have a lot of + * HALogs to scan. While it blocks, the REST API (including the LBS) + * is down. This means that client requests to the service end point + * can not be proxied to a service that is online. The problem is + * the interaction with the BigdataRDFServletContextListener which + * needs to (a) set the IIndexManager on the ServletContext; and (b) + * initiate the default KB create (if it is the quorum leader). + * + * @see <a href="http://trac.bigdata.com/ticket/775" > HAJournal + * start() (optimization) </a> + */ this.journal = newHAJournal(this, config, quorum); } Modified: branches/BIGDATA_RELEASE_1_3_0/bigdata-jini/src/test/com/bigdata/journal/jini/ha/zkClient.config =================================================================== --- branches/BIGDATA_RELEASE_1_3_0/bigdata-jini/src/test/com/bigdata/journal/jini/ha/zkClient.config 2014-05-02 14:59:01 UTC (rev 8165) +++ branches/BIGDATA_RELEASE_1_3_0/bigdata-jini/src/test/com/bigdata/journal/jini/ha/zkClient.config 2014-05-02 16:52:45 UTC (rev 8166) @@ -1,22 +1,6 @@ -/* Zookeeper client only configuration. +/* + * Zookeeper client configuration. */ -import java.io.File; -import java.net.InetAddress; -import java.net.InetSocketAddress; -import java.util.UUID; - -import com.bigdata.util.NV; -import com.bigdata.util.config.NicUtil; -import com.bigdata.journal.Options; -import com.bigdata.journal.BufferMode; -import com.bigdata.journal.jini.ha.HAJournal; -import com.bigdata.jini.lookup.entry.*; -import com.bigdata.service.IBigdataClient; -import com.bigdata.service.AbstractTransactionService; -import com.bigdata.service.jini.*; -import com.bigdata.service.jini.lookup.DataServiceFilter; -import com.bigdata.service.jini.master.ServicesTemplate; -import com.bigdata.jini.start.config.*; import com.bigdata.jini.util.ConfigMath; import org.apache.zookeeper.ZooDefs; @@ -30,16 +14,6 @@ private static fedname = "benchmark"; - /* The logical service identifier shared by all members of the quorum. - * - * Note: The test fixture ignores this value. For the avoidance of - * doubt, the value is commented out. - */ - //private static logicalServiceId = "CI-HAJournal-1"; - - // zookeeper - static private sessionTimeout = (int)ConfigMath.s2ms(20); - } /* @@ -53,36 +27,16 @@ /* A comma separated list of host:port pairs, where the port is * the CLIENT port for the zookeeper server instance. */ - // standalone. servers = "localhost:2081"; - // ensemble -// servers = bigdata.zoo1+":2181" -// + ","+bigdata.zoo2+":2181" -// + ","+bigdata.zoo3+":2181" -// ; /* Session timeout (optional). */ - sessionTimeout = bigdata.sessionTimeout; + sessionTimeout = (int)ConfigMath.s2ms(20); - /* - * ACL for the zookeeper nodes created by the bigdata federation. - * - * Note: zookeeper ACLs are not transmitted over secure channels - * and are placed into plain text Configuration files by the - * ServicesManagerServer. - */ + // Zookeeper ACLs. acl = new ACL[] { new ACL(ZooDefs.Perms.ALL, new Id("world", "anyone")) }; - /* - * Note: Normally on the HAJournalServer component. Hacked in the test - * suite setup to look at the ZooKeeper component instead. - */ - - logicalServiceId = bigdata.logicalServiceId; - - replicationFactor = bigdata.replicationFactor; } Added: branches/BIGDATA_RELEASE_1_3_0/bigdata-sails/src/java/com/bigdata/rdf/sail/webapp/DefaultHARequestURIRewriter.java =================================================================== --- branches/BIGDATA_RELEASE_1_3_0/bigdata-sails/src/java/com/bigdata/rdf/sail/webapp/DefaultHARequestURIRewriter.java (rev 0) +++ branches/BIGDATA_RELEASE_1_3_0/bigdata-sails/src/java/com/bigdata/rdf/sail/webapp/DefaultHARequestURIRewriter.java 2014-05-02 16:52:45 UTC (rev 8166) @@ -0,0 +1,85 @@ +/** +Copyright (C) SYSTAP, LLC 2006-2007. All rights reserved. + +Contact: + SYSTAP, LLC + 4501 Tower Road + Greensboro, NC 27410 + lic...@bi... + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; version 2 of the License. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ +package com.bigdata.rdf.sail.webapp; + +import javax.servlet.ServletConfig; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; + +import com.bigdata.journal.IIndexManager; + +/** + * Default implementation. + * + * @author <a href="mailto:tho...@us...">Bryan Thompson</a> + */ +public class DefaultHARequestURIRewriter implements IHARequestURIRewriter { + + /** + * {@inheritDoc} + * <p> + * This implementation is a NOP. + */ + @Override + public void init(ServletConfig servletConfig, IIndexManager indexManager) + throws ServletException { + + } + + /** + * {@inheritDoc} + * <p> + * This implementation is a NOP. + */ + @Override + public void destroy() { + + } + + @Override + public StringBuilder rewriteURI(final boolean isLeaderRequest, + final String full_prefix, final String originalRequestURL, + final String proxyToRequestURL, final HttpServletRequest request) { + + final StringBuilder uri = new StringBuilder(proxyToRequestURL); + + if (proxyToRequestURL.endsWith("/")) + uri.setLength(uri.length() - 1); + + final String rest = originalRequestURL.substring(full_prefix.length()); + + if (!rest.startsWith("/")) + uri.append("/"); + + uri.append(rest); + + final String query = request.getQueryString(); + + if (query != null) + uri.append("?").append(query); + + return uri; + + } + +} Modified: branches/BIGDATA_RELEASE_1_3_0/bigdata-sails/src/java/com/bigdata/rdf/sail/webapp/HALoadBalancerServlet.java =================================================================== --- branches/BIGDATA_RELEASE_1_3_0/bigdata-sails/src/java/com/bigdata/rdf/sail/webapp/HALoadBalancerServlet.java 2014-05-02 14:59:01 UTC (rev 8165) +++ branches/BIGDATA_RELEASE_1_3_0/bigdata-sails/src/java/com/bigdata/rdf/sail/webapp/HALoadBalancerServlet.java 2014-05-02 16:52:45 UTC (rev 8166) @@ -92,11 +92,6 @@ * @author <a href="mailto:tho...@us...">Bryan Thompson</a> * * @see <a href="http://trac.bigdata.com/ticket/624"> HA Load Balancer </a> - * - * TODO If the target service winds up not joined with the met quorum by - * the time we get there, what should it do? Report an error since we are - * already on its internal interface? Will this servlet see that error? If - * it does, should it handle it? */ public class HALoadBalancerServlet extends ProxyServlet { @@ -110,10 +105,6 @@ public interface InitParams { -// String ENABLED = "enabled"; -// -// String DEFAULT_ENABLED = "false"; - /* * Note: /bigdata/LBS is now a base prefix. There are fully qualified * prefix values of /bigdata/LBS/leader and /bigdata/LBS/read. This is @@ -146,50 +137,78 @@ */ String DEFAULT_POLICY = NOPLBSPolicy.class.getName(); + /** + * The fully qualified class name of an {@link IHARequestURIRewriter} + * (optional - the default is {@value #DEFAULT_REWRITER}). This must be + * an instance of {@link IHARequestURIRewriter}. This may be used to + * impose application specific Request-URI rewrite semantics when a + * request will be proxied to another service. + */ + String REWRITER = "rewriter"; + + String DEFAULT_REWRITER = DefaultHARequestURIRewriter.class.getName(); + } public HALoadBalancerServlet() { + super(); + } -// /** -// * This servlet request attribute is used to mark a request as either an -// * update or a read-only operation. -// */ -// protected static final String ATTR_LBS_UPDATE_REQUEST = "lbs-update-request"; - /** - * The initial prefix that will be stripped off by the load balancer. + * The initial prefix and is formed as + * + * <pre> + * Context - Path / LBS + * </pre> * <p> * Note: This is set by {@link #init()}. It must not be <code>null</code>. - * The load balancer relies on the prefix to rewrite the requestURL when the - * {@link IHALoadBalancerPolicy} is disabled in order to forward the request - * to the local service. + * The load balancer relies on the prefix to rewrite the Request-URI: (a) + * when it is disabled (the request will be forwarded to a local service; + * and (b) when the request is proxied to a remote service. */ private String prefix; /** + * The URI path component that follows the servlet context-path to identify + * a request that will be handled by the load balancer component. + */ + private static final String PATH_LBS = "/LBS"; + + /** + * The URI path component that follows the {@link #prefix} to identify a + * request that will target the quorum leader. + */ + private static final String PATH_LEADER = "/leader"; + + /** + * The URI path component that follows the {@link #prefix} to identify a + * request that should be load balanced over the leader + followers. + */ + private static final String PATH_READ = "/read"; + + /** * The configured {@link IHALoadBalancerPolicy} and <code>null</code> iff * the load balancer is disabled. If the LBS is not enabled, then it will * strip its prefix from the URL requestURI and do a servlet forward to the * resulting requestURI. This allows the webapp to start even if the LBS is * not correctly configured. * - * TODO Since we are allowing programatic change of the policy, it would be - * a good idea to make that change atomic with respect to any specific - * request and to make the destroy of the policy something that occurs once - * any in flight request has been handled (there is more than one place - * where the policy is checked in the code). The atomic change might be - * accomplished by attaching the policy to the request as an attribute. The - * destroy could be achieved by reference counts for the #of in flight - * requests flowing through a policy. The request attribute and reference - * count could be handled together through handshaking with the policy when - * attaching it as a request attribute in - * {@link #service(HttpServletRequest, HttpServletResponse)}. + * @see InitParams#POLICY */ private final AtomicReference<IHALoadBalancerPolicy> policyRef = new AtomicReference<IHALoadBalancerPolicy>(); /** + * The {@link IHARequestURIRewriter} that rewrites the original Request-URI + * into a Request-URI for the target service to which the request will be + * proxied. + * + * @see InitParams#REWRITER + */ + private final AtomicReference<IHARequestURIRewriter> rewriterRef = new AtomicReference<IHARequestURIRewriter>(); + + /** * Change the {@link IHALoadBalancerPolicy} associated with this instance of * this servlet. The new policy will be installed iff it can be initialized * successfully. The old policy will be destroyed iff the new policy is @@ -205,7 +224,61 @@ if (log.isInfoEnabled()) log.info("newValue=" + newValue); + + setHAPolicy(newValue, policyRef); + + } + + /** + * Change the {@link IHARequestURIRewriter} associated with this instance of + * this servlet. The new policy will be installed iff it can be initialized + * successfully. The old policy will be destroyed iff the new policy is + * successfully installed. + * + * @param newValue + * The new value (required). + */ + public void setRewriter(final IHARequestURIRewriter newValue) { + if (newValue == null) + throw new IllegalArgumentException(); + + if (log.isInfoEnabled()) + log.info("newValue=" + newValue); + + setHAPolicy(newValue, rewriterRef); + + } + + /** + * Change the {@link IHAPolicyLifeCycle} associated with this instance of + * this servlet. The new policy will be installed iff it can be initialized + * successfully. The old policy will be destroyed iff the new policy is + * successfully installed. + * + * @param newValue + * The new value (required). + * @param ref + * The {@link AtomicReference} object that holds the current + * value of the policy. + * + * TODO Since we are allowing programatic change of the policy, + * it would be a good idea to make that change atomic with + * respect to any specific request and to make the destroy of the + * policy something that occurs once any in flight request has + * been handled (there is more than one place where the policy is + * checked in the code). The atomic change might be accomplished + * by attaching the policy to the request as an attribute. The + * destroy could be achieved by reference counts for the #of in + * flight requests flowing through a policy. The request + * attribute and reference count could be handled together + * through handshaking with the policy when attaching it as a + * request attribute in + * {@link #service(HttpServletRequest, HttpServletResponse)}. + */ + private <T extends IHAPolicyLifeCycle> void setHAPolicy(final T newValue, + final AtomicReference<T> ref) { + final ServletConfig servletConfig = getServletConfig(); final ServletContext servletContext = servletConfig.getServletContext(); @@ -252,8 +325,7 @@ } // Install the new policy. - final IHALoadBalancerPolicy oldValue = this.policyRef - .getAndSet(newValue); + final T oldValue = ref.getAndSet(newValue); if (oldValue != null && oldValue != newValue) { @@ -261,7 +333,7 @@ oldValue.destroy(); } - + } /** @@ -280,7 +352,7 @@ // // Get the as-configured prefix to be stripped from requests. // prefix = servletConfig.getInitParameter(InitParams.PREFIX); - prefix = BigdataStatics.getContextPath() + "/LBS"; + prefix = BigdataStatics.getContextPath() + PATH_LBS; final ServletContext servletContext = servletConfig.getServletContext(); @@ -293,32 +365,26 @@ return; } - /* - * Setup a fall back policy. This policy will strip off the configured - * prefix from the requestURL and forward the request to the local - * service. If we can not establish the as-configured policy, then - * the servlet will run with this fall back policy. - */ + { + // Get the as-configured policy. + final IHALoadBalancerPolicy policy = newInstance(servletConfig, + IHALoadBalancerPolicy.class, InitParams.POLICY, + InitParams.DEFAULT_POLICY); + // Set the as-configured policy. + setPolicy(policy); + + } { - - final IHALoadBalancerPolicy defaultPolicy = new NOPLBSPolicy(); - - // Initialize the fallback policy. - defaultPolicy.init(servletConfig, indexManager); - policyRef.set(defaultPolicy); + final IHARequestURIRewriter rewriter = newInstance(servletConfig, + IHARequestURIRewriter.class, InitParams.REWRITER, + InitParams.DEFAULT_REWRITER); + setRewriter(rewriter); + } - // Get the as-configured policy. - IHALoadBalancerPolicy policy = newInstance(servletConfig, - IHALoadBalancerPolicy.class, InitParams.POLICY, - InitParams.DEFAULT_POLICY); - - // Set the as-configured policy. - setPolicy(policy); - servletContext.setAttribute(BigdataServlet.ATTRIBUTE_LBS_PREFIX, prefix); @@ -326,7 +392,8 @@ if (log.isInfoEnabled()) log.info(servletConfig.getServletName() + " @ " + prefix - + " :: policy=" + policy); + + " :: policy=" + policyRef.get() + ", rewriter=" + + rewriterRef.get()); } @@ -441,15 +508,32 @@ removeServlet(getServletContext(), this/* servlet */); - final IHALoadBalancerPolicy policy = policyRef - .getAndSet(null/* newValue */); + { - if (policy != null) { + final IHALoadBalancerPolicy policy = policyRef + .getAndSet(null/* newValue */); - policy.destroy(); + if (policy != null) { + policy.destroy(); + + } + } + { + + final IHARequestURIRewriter rewriter = rewriterRef + .getAndSet(null/* newValue */); + + if (rewriter != null) { + + rewriter.destroy(); + + } + + } + prefix = null; getServletContext().setAttribute(BigdataServlet.ATTRIBUTE_LBS_PREFIX, @@ -592,8 +676,9 @@ * servlet in this servlet container rather than proxying it to either * itself or another service. * - * FIXME This does too much work if the request is for the leader and - * this service is not the leader. Look at it again under a debugger. + * TODO This does too much work if the request is for the leader and + * this service is not the leader. Look at it again under a debugger + * and optimize the code paths. */ if (policy.service(isLeaderRequest, request, response)) { @@ -710,8 +795,12 @@ * <code>true</code> iff this is a leader request. * @param prefix * the base prefix (typically <code>/bigdata/LBS</code>) - * + * * @return The full prefix. + * + * TODO This may need to configurable. It is currently static since + * {@link #forwardToThisService(boolean, HttpServletRequest, HttpServletResponse)} + * is static. */ private static String getFullPrefix(final boolean isLeaderRequest, final String prefix) { @@ -720,7 +809,7 @@ : prefix + "/read"; return full_prefix; - + } /** @@ -738,55 +827,72 @@ return null; } - final String path = request.getRequestURI(); - if (!path.startsWith(prefix)) + final String originalRequestURI = request.getRequestURI(); + + if (!originalRequestURI.startsWith(prefix)) return null; final Boolean isLeaderRequest = isLeaderRequest(request); + if (isLeaderRequest == null) { // Neither /LBS/leader -nor- /LBS/read. return null; } - final String proxyTo; + + final String proxyToRequestURI; + if(isLeaderRequest) { // Proxy to leader. - proxyTo = policy.getLeaderURL(request); + proxyToRequestURI = policy.getLeaderURL(request); } else { // Proxy to any joined service. - proxyTo = policy.getReaderURL(request); + proxyToRequestURI = policy.getReaderURL(request); } - if (proxyTo == null) { + + if (proxyToRequestURI == null) { // Could not rewrite. return null; } - final StringBuilder uri = new StringBuilder(proxyTo); - if (proxyTo.endsWith("/")) - uri.setLength(uri.length() - 1); + // the full LBS prefix (includes /leader or /read). final String full_prefix = getFullPrefix(isLeaderRequest, prefix); - final String rest = path.substring(full_prefix.length()); - if (!rest.startsWith("/")) - uri.append("/"); - uri.append(rest); - final String query = request.getQueryString(); - if (query != null) - uri.append("?").append(query); + + // The configured Request-URL rewriter. + final IHARequestURIRewriter rewriter = rewriterRef.get(); + + if (rewriter == null) { + // Could not rewrite. + log.warn("No rewriter: requestURI="+originalRequestURI); + return null; + } + + // Re-write requestURL. + final StringBuilder uri = rewriter.rewriteURI(// + isLeaderRequest,// iff request for the leader + full_prefix, // + originalRequestURI,// old + proxyToRequestURI, // new + request // request + ); + + // Normalize the request. final URI rewrittenURI = URI.create(uri.toString()).normalize(); if (!validateDestination(rewrittenURI.getHost(), rewrittenURI.getPort())) return null; if (log.isInfoEnabled()) - log.info("rewrote: " + path + " => " + rewrittenURI); + log.info("rewrote: " + originalRequestURI + " => " + rewrittenURI); return rewrittenURI; } /** - * TODO This offers an opportunity to handle a rewrite failure. It could be - * used to provide a default status code (e.g., 404 versus forbidden) or to - * forward the request to this server rather than proxying to another - * server. + * Note: This offers an opportunity to handle a failure where we were unable + * to rewrite the request to some service, e.g., because the quorum is not + * met. The implementation is overridden to forward the request to the local + * service. The local service will then generate an appropriate HTTP error + * response. */ @Override protected void onRewriteFailed(final HttpServletRequest request, @@ -846,13 +952,13 @@ final String rest = requestURI.substring(indexLBS + prefix.length()); - if (rest.startsWith("/leader")) { + if (rest.startsWith(PATH_LEADER)) { return Boolean.TRUE; } - if (rest.startsWith("/read")) { + if (rest.startsWith(PATH_READ)) { return Boolean.FALSE; Modified: branches/BIGDATA_RELEASE_1_3_0/bigdata-sails/src/java/com/bigdata/rdf/sail/webapp/IHALoadBalancerPolicy.java =================================================================== --- branches/BIGDATA_RELEASE_1_3_0/bigdata-sails/src/java/com/bigdata/rdf/sail/webapp/IHALoadBalancerPolicy.java 2014-05-02 14:59:01 UTC (rev 8165) +++ branches/BIGDATA_RELEASE_1_3_0/bigdata-sails/src/java/com/bigdata/rdf/sail/webapp/IHALoadBalancerPolicy.java 2014-05-02 16:52:45 UTC (rev 8166) @@ -24,13 +24,10 @@ import java.io.IOException; -import javax.servlet.ServletConfig; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; -import com.bigdata.journal.IIndexManager; - /** * Load balancer policy interface. * @@ -39,24 +36,9 @@ * @see HALoadBalancerServlet * @see <a href="http://trac.bigdata.com/ticket/624">HA Load Balancer</a> */ -public interface IHALoadBalancerPolicy { +public interface IHALoadBalancerPolicy extends IHAPolicyLifeCycle { /** - * Initialize the load balancer policy. - * - * @param servletConfig - * @param indexManager - */ - void init(ServletConfig servletConfig, IIndexManager indexManager) - throws ServletException; - - /** - * Destroy the load balancer policy (stop any asynchronous processing, - * release any resources). - */ - void destroy(); - - /** * Invoked for each request. If the response is not committed, then it will * be handled by the {@link HALoadBalancerServlet}. * Added: branches/BIGDATA_RELEASE_1_3_0/bigdata-sails/src/java/com/bigdata/rdf/sail/webapp/IHAPolicyLifeCycle.java =================================================================== --- branches/BIGDATA_RELEASE_1_3_0/bigdata-sails/src/java/com/bigdata/rdf/sail/webapp/IHAPolicyLifeCycle.java (rev 0) +++ branches/BIGDATA_RELEASE_1_3_0/bigdata-sails/src/java/com/bigdata/rdf/sail/webapp/IHAPolicyLifeCycle.java 2014-05-02 16:52:45 UTC (rev 8166) @@ -0,0 +1,47 @@ +/** +Copyright (C) SYSTAP, LLC 2006-2007. All rights reserved. + +Contact: + SYSTAP, LLC + 4501 Tower Road + Greensboro, NC 27410 + lic...@bi... + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; version 2 of the License. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ +package com.bigdata.rdf.sail.webapp; + +import javax.servlet.ServletConfig; +import javax.servlet.ServletException; + +import com.bigdata.journal.IIndexManager; + +public interface IHAPolicyLifeCycle { + + /** + * Initialize the policy. + * + * @param servletConfig + * @param indexManager + */ + void init(ServletConfig servletConfig, IIndexManager indexManager) + throws ServletException; + + /** + * Destroy the policy (stop any asynchronous processing, release any + * resources). + */ + void destroy(); + +} Added: branches/BIGDATA_RELEASE_1_3_0/bigdata-sails/src/java/com/bigdata/rdf/sail/webapp/IHARequestURIRewriter.java =================================================================== --- branches/BIGDATA_RELEASE_1_3_0/bigdata-sails/src/java/com/bigdata/rdf/sail/webapp/IHARequestURIRewriter.java (rev 0) +++ branches/BIGDATA_RELEASE_1_3_0/bigdata-sails/src/java/com/bigdata/rdf/sail/webapp/IHARequestURIRewriter.java 2014-05-02 16:52:45 UTC (rev 8166) @@ -0,0 +1,95 @@ +/** +Copyright (C) SYSTAP, LLC 2006-2007. All rights reserved. + +Contact: + SYSTAP, LLC + 4501 Tower Road + Greensboro, NC 27410 + lic...@bi... + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; version 2 of the License. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ +package com.bigdata.rdf.sail.webapp; + +import javax.servlet.http.HttpServletRequest; + +import com.bigdata.rdf.sail.webapp.lbs.ServiceScore; + +/** + * Interface for rewriting the Request-URI once the load balancer has determined + * the target host and service to which the request will be proxied. + * + * @author <a href="mailto:tho...@us...">Bryan Thompson</a> + */ +public interface IHARequestURIRewriter extends IHAPolicyLifeCycle { + + /** + * Rewrite the <code>originalRequestURI</code> into a <a href= + * "http://www.w3.org/Protocols/rfc2616/rfc2616-sec5.html#sec5.1.2" + * >Request-URL</a> for the web application whose servlet context root is + * given by the <code>proxyToRequestURI</code>. + * <p> + * Note: The <code>proxyToRequestURI</code> is include the protocol, host, + * port, and servlet context path for the target service. It DOES NOT + * include any information from the original request. The purpose of this + * method is to modify the <code>originalRequestURI</code> in order to + * obtain a fully qualified RequestURI for the service to which the request + * will be proxied. For example: + * + * <pre> + * full_prefix: /bigdata/LBS/leader + * -or- full_prefix: /bigdata/LBS/read + * originalRequestURI: http://ha1.example.com:8090/bigdata/LBS/read/sparql + * proxyToRequestURI: http://ha3.example.com:8090/bigdata/ + * return: http://ha2.example.com:8090/bigdata/LBS/read/sparql + * </pre> + * <p> + * Note: this method is only invoked if we will proxy to another service. + * Therefore, the <code>proxyToRequestURI</code> is never <code>null</code>. + * + * @param isLeaderRequest + * <code>true</code> iff the request is directed to the leader. + * @param full_prefix + * The path prefix in the <code>originalRequestURI</code> that + * which corresponds to the {@link HALoadBalancerServlet} and + * which must be removed if the request is to be forwarded to a + * local service. + * @param originalRequestURI + * The original <a href= + * "http://www.w3.org/Protocols/rfc2616/rfc2616-sec5.html#sec5.1.2" + * >Request-URL</a> from the <a + * href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec5.html#sec5.1" + * >HTTP Request-Line</a> + * @param proxyToRequestURI + * The RequestURI for the root of the web application for the + * target service and never <code>null</code>. + * @param request + * The original request. + * + * @return The fully qualified <a href= + * "http://www.w3.org/Protocols/rfc2616/rfc2616-sec5.html#sec5.1.2" + * >Request-URL</a> that will be used to proxy the http request to + * the service identified by the <code>proxyToRequestURI</code> + * + * @see ServiceScore#getRequestURI() + */ + public StringBuilder rewriteURI(// + boolean isLeaderRequest, + String full_prefix,// + String originalRequestURI, // + String proxyToRequestURI,// + HttpServletRequest request// + ); + +} Modified: branches/BIGDATA_RELEASE_1_3_0/bigdata-sails/src/java/com/bigdata/rdf/sail/webapp/lbs/AbstractLBSPolicy.java =================================================================== --- branches/BIGDATA_RELEASE_1_3_0/bigdata-sails/src/java/com/bigdata/rdf/sail/webapp/lbs/AbstractLBSPolicy.java 2014-05-02 14:59:01 UTC (rev 8165) +++ branches/BIGDATA_RELEASE_1_3_0/bigdata-sails/src/java/com/bigdata/rdf/sail/webapp/lbs/AbstractLBSPolicy.java 2014-05-02 16:52:45 UTC (rev 8166) @@ -63,9 +63,14 @@ * {@link AbstractQuorum#terminate()}. This happens any time the * {@link HAJournalServer} goes into the error state. When this occurs, * we stop getting {@link QuorumEvent}s and the policy stops being - * responsive. We probably need to either NOT clear the quorum listener - * and/or add an event type that is sent when {@link Quorum#terminate()} - * is called. + * responsive (it can not proxy the request to a service that is still + * up because it does not know what services are up, or maybe it just + * can not learn if services go down). + * <p> + * We probably need to either NOT clear the quorum listener and/or add + * an event type that is sent when {@link Quorum#terminate()} is called + * and/or use our own listener (independent of the HAJournalServer, + * which would require us to use an HAClient). */ abstract public class AbstractLBSPolicy implements IHALoadBalancerPolicy, QuorumListener, Serializable { @@ -184,7 +189,7 @@ if (quorum != null) { try { // Note: This is the *local* HAGlueService. - quorumService = (QuorumService) quorum.getClient(); + quorumService = (QuorumService<HAGlue>) quorum.getClient(); token = quorum.token(); isLeader = quorumService.isLeader(token); isQuorumMet = token != Quorum.NO_QUORUM; @@ -299,10 +304,10 @@ for (ServiceScore s : services) { - if (s.serviceUUID.equals(leaderId)) { + if (s.getServiceUUID().equals(leaderId)) { // Found it. Proxy if the serviceURL is defined. - return s.requestURL; + return s.getRequestURI(); } @@ -330,7 +335,7 @@ for (ServiceScore s : services) { - if (s.serviceUUID.equals(serviceIDRef.get())) { + if (s.getServiceUUID().equals(serviceIDRef.get())) { // Found it. return s; @@ -368,7 +373,7 @@ for (ServiceScore s : services) { - if (hostname.equals(s.hostname)) { + if (hostname.equals(s.getHostname())) { // Found it. return s; @@ -462,12 +467,14 @@ try { /* - * TODO Scan the existing table before doing an RMI to the - * service. We only need to do the RMI for a new service, not - * one in the table. - * - * TODO A services HashMap<UUID,HAGlueScore> would be much more - * efficient than a table. If we use a CHM, then we can do this + * TODO We only need to do this when a service enters the + * quorum, but we already have the information on hand for all + * services except the one that is entering. To reduce overhead + * and RMI calls, we should scan the existing table before doing + * an RMI to the service. We only need to do the RMI for a new + * service, not one in the table. A services + * HashMap<UUID,HAGlueScore> would be much more efficient than a + * table for this scan. If we use a CHM, then we can do this * purely asynchronously as the HAGlue services enter (or leave) * the set of joined services. */ Modified: branches/BIGDATA_RELEASE_1_3_0/bigdata-sails/src/java/com/bigdata/rdf/sail/webapp/lbs/ServiceScore.java =================================================================== --- branches/BIGDATA_RELEASE_1_3_0/bigdata-sails/src/java/com/bigdata/rdf/sail/webapp/lbs/ServiceScore.java 2014-05-02 14:59:01 UTC (rev 8165) +++ branches/BIGDATA_RELEASE_1_3_0/bigdata-sails/src/java/com/bigdata/rdf/sail/webapp/lbs/ServiceScore.java 2014-05-02 16:52:45 UTC (rev 8166) @@ -30,6 +30,7 @@ import com.bigdata.journal.IIndexManager; import com.bigdata.journal.jini.ha.HAJournal; import com.bigdata.quorum.Quorum; +import com.bigdata.rdf.sail.webapp.IHARequestURIRewriter; /** * Helper class caches metadata about an {@link HAGlue} service. @@ -42,25 +43,109 @@ */ public class ServiceScore { - final public UUID serviceUUID; - public HAGlue haGlue; - public String hostname; - public int port; /** - * The {@link #requestURL} is assigned IFF everything succeeds. This is what - * we will use to proxy a request to the service having the {@link UUID} - * given to the constuctor. + * The service {@link UUID} for the remote service. * - * Note: This needs to be a URL, not just a relative path. At least with the - * rewriteURI() code in the outer class. Otherwise you get an NPE. + * @see HAGlue#getServiceUUID() */ - public String requestURL; + final private UUID serviceUUID; + + /** + * The {@link HAGlue} interface for the remote service. + */ + private HAGlue haGlue; + + /** + * The hostname for the remote service. + * + * @see HAGlue#getHostname() + */ + private String hostname; + + /** + * The port for the NSS on the remote service. + * + * @see HAGlue#getNSSPort() + */ + private int port; + + /** + * The constructed Request-URL for the root of the servlet context for the + * NSS on the remote service -or- <code>null</code> if anything goes wrong. + */ + private String requestURI; + + /** + * The service {@link UUID} for the remote service. + * + * @see HAGlue#getServiceUUID() + */ + public UUID getServiceUUID() { + return serviceUUID; + + } + + /** + * The hostname for the remote service -or- <code>null</code> if something + * goes wrong. + * + * @see HAGlue#getHostname() + */ + public String getHostname() { + + return hostname; + + } + + /** + * The port for the NSS on the remote service. + * + * @see HAGlue#getNSSPort() + */ + public int getPort() { + + return port; + + } + + /** + * The {@link #requestURI} for the root of the web application on the target + * host. This is assigned IFF everything succeeds. This is what we will use + * to proxy a request to the service having the {@link UUID} given to the + * constructor. + * <p> + * Note: This needs to be a URL, not just a relative path. Otherwise you get + * an NPE. + * <p> + * This is formed as: + * + * <pre> + * requestURL = "http://" + hostname + ":" + port + contextPath; + * </pre> + * + * The <code>hostname</code> is obtained from {@link HAGlue#getHostname()}. + * <p> + * The <code>port</code> is obtained from {@link HAGlue#getNSSPort()}. + * + * TODO How do we configured the protocol for the remote NSS instance? This + * code assumes that it is <code>http</code>, but <code>https</code> is also + * possible. This could be handled by an {@link IHARequestURIRewriter} but + * maybe the {@link HAGlue} interface should be declaring this too? + */ + public String getRequestURI() { + + return requestURI; + + } + @Override public String toString() { + return getClass().getName() + "{serviceUUID=" + serviceUUID - + ", hostname=" + hostname + ", port=" + port + ", requestURL=" - + requestURL + "}"; + + ", hostname=" + hostname + ", port=" + port + ", requestURI=" + + requestURI + "}"; + } public ServiceScore(final IIndexManager indexManager, @@ -97,7 +182,7 @@ { QuorumService<HAGlue> t; try { - t = (QuorumService) quorum.getClient(); + t = (QuorumService<HAGlue>) quorum.getClient(); } catch (IllegalStateException ex) { // Note: Not available (quorum.start() not called). return; @@ -113,7 +198,8 @@ } /* - * TODO The hostname and port are RMIs. Use a smart proxy. + * TODO The hostname and port are RMIs. Use a smart proxy for HAGlue. Or + * consult a cache of existing ServiceScore objects. */ try { hostname = haGlue.getHostname(); @@ -124,7 +210,7 @@ } // The default URL for that host. - requestURL = "http://" + hostname + ":" + port + contextPath; + requestURI = "http://" + hostname + ":" + port + contextPath; } Modified: branches/BIGDATA_RELEASE_1_3_0/bigdata-sails/src/java/com/bigdata/rdf/sail/webapp/lbs/policy/NOPLBSPolicy.java =================================================================== --- branches/BIGDATA_RELEASE_1_3_0/bigdata-sails/src/java/com/bigdata/rdf/sail/webapp/lbs/policy/NOPLBSPolicy.java 2014-05-02 14:59:01 UTC (rev 8165) +++ branches/BIGDATA_RELEASE_1_3_0/bigdata-sails/src/java/com/bigdata/rdf/sail/webapp/lbs/policy/NOPLBSPolicy.java 2014-05-02 16:52:45 UTC (rev 8166) @@ -33,20 +33,13 @@ /** * This policy proxies all requests for update operations to the leader but - * forwards read requests to the local service. Thus, it does not provide a load - * balancing strategy, but it does allow update requests to be directed to any + * forwards read requests to the local service. Thus, it DOES NOT provide a load + * balancing strategy, but it DOES allow update requests to be directed to any * service in an non-HA aware manner. This policy can be combined with an * external round-robin strategy to load balance the read-requests over the * cluster. * * @author <a href="mailto:tho...@us...">Bryan Thompson</a> - * - * TODO A service that is not joined with the met quorum can not answer - * a read-request. In order to be generally useful (and not just as a - * debugging policy), we need to proxy a read-request when this service - * is not joined with the met quorum. If there is no met quorum, then we - * can just forward the request to the local service and it will report - * the NoQuorum error. */ public class NOPLBSPolicy extends AbstractLBSPolicy { Modified: branches/BIGDATA_RELEASE_1_3_0/bigdata-sails/src/java/com/bigdata/rdf/sail/webapp/lbs/policy/RoundRobinLBSPolicy.java =================================================================== --- branches/BIGDATA_RELEASE_1_3_0/bigdata-sails/src/java/com/bigdata/rdf/sail/webapp/lbs/policy/RoundRobinLBSPolicy.java 2014-05-02 14:59:01 UTC (rev 8165) +++ branches/BIGDATA_RELEASE_1_3_0/bigdata-sails/src/java/com/bigdata/rdf/sail/webapp/lbs/policy/RoundRobinLBSPolicy.java 2014-05-02 16:52:45 UTC (rev 8166) @@ -105,12 +105,12 @@ if (serviceScore == null) continue; - if (serviceScore.hostname == null) { + if (serviceScore.getHostname() == null) { // Can't use if no hostname. continue; } - if (serviceScore.requestURL == null) { + if (serviceScore.getRequestURI() == null) { // Can't use if no requestURL. continue; } @@ -127,7 +127,7 @@ } - return serviceScore.requestURL; + return serviceScore.getRequestURI(); } Modified: branches/BIGDATA_RELEASE_1_3_0/bigdata-sails/src/java/com/bigdata/rdf/sail/webapp/lbs/policy/ganglia/GangliaLBSPolicy.java =================================================================== --- branches/BIGDATA_RELEASE_1_3_0/bigdata-sails/src/java/com/bigdata/rdf/sail/webapp/lbs/policy/ganglia/GangliaLBSPolicy.java 2014-05-02 14:59:01 UTC (rev 8165) +++ branches/BIGDATA_RELEASE_1_3_0/bigdata-sails/src/java/com/bigdata/rdf/sail/webapp/lbs/policy/ganglia/GangliaLBSPolicy.java 2014-05-02 16:52:45 UTC (rev 8166) @@ -573,7 +573,7 @@ for (ServiceScore serviceScore : serviceScores) { if (serviceScore == null) // should never be null. continue; - tmp.add(serviceScore.hostname); + tmp.add(serviceScore.getHostname()); } hosts = tmp.toArray(new String[tmp.size()]); } @@ -600,7 +600,7 @@ ServiceScore theServiceScore = serviceScores[j]; if (theHostReport.getHostName() - .equals(theServiceScore.hostname)) { + .equals(theServiceScore.getHostname())) { // Found service on this host. foundServiceOnHost = true; @@ -688,10 +688,10 @@ if (tmp == null) // should never happen. continue; - if (tmp.requestURL == null) // can't proxy. + if (tmp.getRequestURI() == null) // can't proxy. continue; - if (hostScore.hostname.equals(tmp.hostname)) { + if (hostScore.hostname.equals(tmp.getHostname())) { // Found a joined service on that host. foundServices.add(tmp); @@ -719,7 +719,7 @@ final ServiceScore serviceScore = foundServices.get(n); - return serviceScore.requestURL; + return serviceScore.getRequestURI(); } This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |