YARN-1658. Modified web-app framework to let standby RMs redirect web-service calls to the active RM. Contributed by Cindy Li.

svn merge --ignore-ancestry -c 1577408 ../../trunk/


git-svn-id: https://svn.apache.org/repos/asf/hadoop/common/branches/branch-2@1577409 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Vinod Kumar Vavilapalli 2014-03-14 02:40:28 +00:00
parent bd2cb0117e
commit 3ac3e5f897
7 changed files with 55 additions and 39 deletions

View File

@ -276,6 +276,9 @@ Release 2.4.0 - UNRELEASED
YARN-1771. Reduce the number of NameNode operations during localization of YARN-1771. Reduce the number of NameNode operations during localization of
public resources using a cache. (Sangjin Lee via cdouglas) public resources using a cache. (Sangjin Lee via cdouglas)
YARN-1658. Modified web-app framework to let standby RMs redirect
web-service calls to the active RM. (Cindy Li via vinodkv)
OPTIMIZATIONS OPTIMIZATIONS
BUG FIXES BUG FIXES

View File

@ -271,6 +271,12 @@ public void testRMWebAppRedirect() throws YarnException,
header = getHeader("Refresh", rm2Url + "/cluster/cluster"); header = getHeader("Refresh", rm2Url + "/cluster/cluster");
assertEquals(null, header); assertEquals(null, header);
header = getHeader("Refresh", rm2Url + "/ws/v1/cluster/info");
assertEquals(null, header);
header = getHeader("Refresh", rm2Url + "/ws/v1/cluster/apps");
assertTrue(header.contains("; url=" + rm1Url));
// Due to the limitation of MiniYARNCluster and dispatcher is a singleton, // Due to the limitation of MiniYARNCluster and dispatcher is a singleton,
// we couldn't add the test case after explicitFailover(); // we couldn't add the test case after explicitFailover();
} }
@ -286,4 +292,5 @@ static String getHeader(String field, String url) {
} }
return fieldHeader; return fieldHeader;
} }
} }

View File

@ -57,11 +57,11 @@ public class Dispatcher extends HttpServlet {
private transient final Injector injector; private transient final Injector injector;
private transient final Router router; private transient final Router router;
protected transient final WebApp webApp; private transient final WebApp webApp;
private volatile boolean devMode = false; private volatile boolean devMode = false;
@Inject @Inject
protected Dispatcher(WebApp webApp, Injector injector, Router router) { Dispatcher(WebApp webApp, Injector injector, Router router) {
this.webApp = webApp; this.webApp = webApp;
this.injector = injector; this.injector = injector;
this.router = router; this.router = router;

View File

@ -44,7 +44,7 @@
* Manages path info to controller#action routing. * Manages path info to controller#action routing.
*/ */
@InterfaceAudience.LimitedPrivate({"YARN", "MapReduce"}) @InterfaceAudience.LimitedPrivate({"YARN", "MapReduce"})
public class Router { class Router {
static final Logger LOG = LoggerFactory.getLogger(Router.class); static final Logger LOG = LoggerFactory.getLogger(Router.class);
static final ImmutableList<String> EMPTY_LIST = ImmutableList.of(); static final ImmutableList<String> EMPTY_LIST = ImmutableList.of();
static final CharMatcher SLASH = CharMatcher.is('/'); static final CharMatcher SLASH = CharMatcher.is('/');

View File

@ -122,6 +122,10 @@ public void joinThread() {
public String name() { return this.name; } public String name() { return this.name; }
public String wsName() {
return this.wsName;
}
void addServePathSpec(String path) { this.servePathSpecs.add(path); } void addServePathSpec(String path) { this.servePathSpecs.add(path); }
public String[] getServePathSpecs() { public String[] getServePathSpecs() {
@ -134,7 +138,7 @@ public String[] getServePathSpecs() {
* more easily differentiate the different webapps. * more easily differentiate the different webapps.
* @param path the path to redirect to * @param path the path to redirect to
*/ */
protected void setRedirectPath(String path) { void setRedirectPath(String path) {
this.redirectPath = path; this.redirectPath = path;
} }
@ -160,10 +164,10 @@ public void configureServlets() {
serve(path).with(Dispatcher.class); serve(path).with(Dispatcher.class);
} }
configureRSServlets(); configureWebAppServlets();
} }
protected void configureRSServlets() { protected void configureWebAppServlets() {
// Add in the web services filters/serves if app has them. // Add in the web services filters/serves if app has them.
// Using Jersey/guice integration module. If user has web services // Using Jersey/guice integration module. If user has web services
// they must have also bound a default one in their webapp code. // they must have also bound a default one in their webapp code.
@ -182,9 +186,12 @@ protected void configureRSServlets() {
params.put(FeaturesAndProperties.FEATURE_XMLROOTELEMENT_PROCESSING, "true"); params.put(FeaturesAndProperties.FEATURE_XMLROOTELEMENT_PROCESSING, "true");
params.put(ResourceConfig.PROPERTY_CONTAINER_REQUEST_FILTERS, GZIPContentEncodingFilter.class.getName()); params.put(ResourceConfig.PROPERTY_CONTAINER_REQUEST_FILTERS, GZIPContentEncodingFilter.class.getName());
params.put(ResourceConfig.PROPERTY_CONTAINER_RESPONSE_FILTERS, GZIPContentEncodingFilter.class.getName()); params.put(ResourceConfig.PROPERTY_CONTAINER_RESPONSE_FILTERS, GZIPContentEncodingFilter.class.getName());
filter("/*").through(GuiceContainer.class, params); filter("/*").through(getWebAppFilterClass(), params);
} }
}
protected Class<? extends GuiceContainer> getWebAppFilterClass() {
return GuiceContainer.class;
} }
/** /**
@ -274,4 +281,5 @@ static String getPrefix(String pathSpec) {
} }
public abstract void setup(); public abstract void setup();
} }

View File

@ -29,11 +29,12 @@
import org.apache.hadoop.yarn.server.resourcemanager.ResourceManager; import org.apache.hadoop.yarn.server.resourcemanager.ResourceManager;
import org.apache.hadoop.yarn.server.resourcemanager.security.QueueACLsManager; import org.apache.hadoop.yarn.server.resourcemanager.security.QueueACLsManager;
import org.apache.hadoop.yarn.server.security.ApplicationACLsManager; import org.apache.hadoop.yarn.server.security.ApplicationACLsManager;
import org.apache.hadoop.yarn.webapp.Dispatcher;
import org.apache.hadoop.yarn.webapp.GenericExceptionHandler; import org.apache.hadoop.yarn.webapp.GenericExceptionHandler;
import org.apache.hadoop.yarn.webapp.WebApp; import org.apache.hadoop.yarn.webapp.WebApp;
import org.apache.hadoop.yarn.webapp.YarnWebParams; import org.apache.hadoop.yarn.webapp.YarnWebParams;
import com.sun.jersey.guice.spi.container.servlet.GuiceContainer;
/** /**
* The RM webapp * The RM webapp
*/ */
@ -51,6 +52,8 @@ public void setup() {
bind(JAXBContextResolver.class); bind(JAXBContextResolver.class);
bind(RMWebServices.class); bind(RMWebServices.class);
bind(GenericExceptionHandler.class); bind(GenericExceptionHandler.class);
bind(RMWebApp.class).toInstance(this);
if (rm != null) { if (rm != null) {
bind(ResourceManager.class).toInstance(rm); bind(ResourceManager.class).toInstance(rm);
bind(RMContext.class).toInstance(rm.getRMContext()); bind(RMContext.class).toInstance(rm.getRMContext());
@ -68,17 +71,8 @@ public void setup() {
} }
@Override @Override
public void configureServlets() { protected Class<? extends GuiceContainer> getWebAppFilterClass() {
setup(); return RMWebAppFilter.class;
serve("/").with(RMDispatcher.class);
serve("/__stop").with(Dispatcher.class);
for (String path : super.getServePathSpecs()) {
serve(path).with(RMDispatcher.class);
}
configureRSServlets();
} }
public void checkIfStandbyRM() { public void checkIfStandbyRM() {

View File

@ -21,59 +21,63 @@
import java.io.IOException; import java.io.IOException;
import java.io.PrintWriter; import java.io.PrintWriter;
import javax.inject.Inject;
import javax.inject.Singleton;
import javax.servlet.FilterChain;
import javax.servlet.ServletException; import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponse;
import org.apache.hadoop.classification.InterfaceAudience;
import org.apache.hadoop.http.HtmlQuoting; import org.apache.hadoop.http.HtmlQuoting;
import org.apache.hadoop.yarn.webapp.Dispatcher;
import org.apache.hadoop.yarn.webapp.Router;
import org.apache.hadoop.yarn.webapp.WebApp;
import com.google.inject.Inject;
import com.google.inject.Injector; import com.google.inject.Injector;
import com.google.inject.Singleton; import com.sun.jersey.guice.spi.container.servlet.GuiceContainer;
@InterfaceAudience.LimitedPrivate({ "YARN", "MapReduce" })
@Singleton @Singleton
public class RMDispatcher extends Dispatcher { public class RMWebAppFilter extends GuiceContainer {
private Injector injector;
/** /**
* *
*/ */
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
@Inject @Inject
RMDispatcher(WebApp webApp, Injector injector, Router router) { public RMWebAppFilter(Injector injector) {
super(webApp, injector, router); super(injector);
this.injector=injector;
} }
@Override @Override
public void service(HttpServletRequest req, HttpServletResponse res) public void doFilter(HttpServletRequest request,
throws ServletException, IOException { HttpServletResponse response, FilterChain chain) throws IOException,
res.setCharacterEncoding("UTF-8"); ServletException {
String uri = HtmlQuoting.quoteHtmlChars(req.getRequestURI()); response.setCharacterEncoding("UTF-8");
String uri = HtmlQuoting.quoteHtmlChars(request.getRequestURI());
if (uri == null) { if (uri == null) {
uri = "/"; uri = "/";
} }
RMWebApp rmWebApp = injector.getInstance(RMWebApp.class);
RMWebApp rmWebApp = (RMWebApp) webApp;
rmWebApp.checkIfStandbyRM(); rmWebApp.checkIfStandbyRM();
if (rmWebApp.isStandby() if (rmWebApp.isStandby()
&& !uri.equals("/" + rmWebApp.wsName() + "/v1/cluster/info")
&& !uri.equals("/" + rmWebApp.name() + "/cluster")) { && !uri.equals("/" + rmWebApp.name() + "/cluster")) {
String redirectPath = rmWebApp.getRedirectPath() + uri; String redirectPath = rmWebApp.getRedirectPath() + uri;
if (redirectPath != null && !redirectPath.isEmpty()) { if (redirectPath != null && !redirectPath.isEmpty()) {
String redirectMsg = String redirectMsg =
"This is standby RM. Redirecting to the current active RM: " "This is standby RM. Redirecting to the current active RM: "
+ redirectPath; + redirectPath;
res.addHeader("Refresh", "3; url=" + redirectPath); response.addHeader("Refresh", "3; url=" + redirectPath);
PrintWriter out = res.getWriter(); PrintWriter out = response.getWriter();
out.println(redirectMsg); out.println(redirectMsg);
return; return;
} }
} }
super.service(req, res);
super.doFilter(request, response, chain);
} }
} }