-
Notifications
You must be signed in to change notification settings - Fork 9.2k
YARN-8900. [Router] Federation: routing getContainers REST invocations transparently to multiple RMs #4543
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
YARN-8900. [Router] Federation: routing getContainers REST invocations transparently to multiple RMs #4543
Changes from 3 commits
c1b44fa
efc1181
72a1849
e0902f5
210bfd1
ffd3eb4
a8cf91d
8810220
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -19,15 +19,10 @@ | |
| package org.apache.hadoop.yarn.server.router.webapp; | ||
|
|
||
| import java.io.IOException; | ||
| import java.lang.reflect.Method; | ||
| import java.security.Principal; | ||
| import java.util.ArrayList; | ||
| import java.util.Collection; | ||
| import java.util.HashMap; | ||
| import java.util.List; | ||
| import java.util.Map; | ||
| import java.util.*; | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Leave expanded. |
||
| import java.util.Map.Entry; | ||
| import java.util.Random; | ||
| import java.util.Set; | ||
| import java.util.concurrent.Callable; | ||
| import java.util.concurrent.CompletionService; | ||
| import java.util.concurrent.ExecutorCompletionService; | ||
|
|
@@ -92,6 +87,7 @@ | |
| import org.apache.hadoop.yarn.server.resourcemanager.webapp.dao.SchedulerTypeInfo; | ||
| import org.apache.hadoop.yarn.server.router.RouterMetrics; | ||
| import org.apache.hadoop.yarn.server.router.RouterServerUtil; | ||
| import org.apache.hadoop.yarn.server.router.clientrm.ClientMethod; | ||
| import org.apache.hadoop.yarn.server.webapp.dao.AppAttemptInfo; | ||
| import org.apache.hadoop.yarn.server.webapp.dao.ContainerInfo; | ||
| import org.apache.hadoop.yarn.server.webapp.dao.ContainersInfo; | ||
|
|
@@ -1053,7 +1049,6 @@ public ClusterMetricsInfo call() { | |
| } | ||
|
|
||
| // Collect all the responses in parallel | ||
|
|
||
| for (int i = 0; i < subClustersActive.size(); i++) { | ||
| try { | ||
| Future<ClusterMetricsInfo> future = compSvc.take(); | ||
|
|
@@ -1336,7 +1331,32 @@ public AppAttemptInfo getAppAttempt(HttpServletRequest req, | |
| @Override | ||
| public ContainersInfo getContainers(HttpServletRequest req, | ||
| HttpServletResponse res, String appId, String appAttemptId) { | ||
| throw new NotImplementedException("Code is not implemented"); | ||
|
|
||
| ContainersInfo containersInfo = new ContainersInfo(); | ||
|
|
||
| Map<SubClusterId, SubClusterInfo> subClustersActive; | ||
| try { | ||
| subClustersActive = getActiveSubclusters(); | ||
| } catch (NotFoundException e) { | ||
| LOG.error("Get all active sub cluster(s) error.", e); | ||
| return containersInfo; | ||
| } | ||
|
|
||
| try { | ||
| ClientMethod remoteMethod = new ClientMethod("getContainers", | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The code would look more intuitive as:
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. your suggestion is great! |
||
| new Class[]{HttpServletRequest.class, HttpServletResponse.class, String.class, | ||
| String.class}, new Object[]{req, res, appId, appAttemptId}); | ||
| Map<SubClusterInfo, ContainersInfo> containersInfoMap = | ||
| invokeConcurrent(subClustersActive.values(), remoteMethod, ContainersInfo.class); | ||
| if (containersInfoMap != null) { | ||
| containersInfoMap.values().forEach(containers -> containersInfo.addAll(containers.getContainers())); | ||
| } | ||
|
|
||
| } catch (Exception ex) { | ||
| LOG.error("Failed to return GetContainers.", ex); | ||
| } | ||
|
|
||
| return containersInfo; | ||
| } | ||
|
|
||
| @Override | ||
|
|
@@ -1366,4 +1386,49 @@ public void shutdown() { | |
| threadpool.shutdown(); | ||
| } | ||
| } | ||
|
|
||
| <R> Map<SubClusterInfo, R> invokeConcurrent(Collection<SubClusterInfo> clusterIds, | ||
| ClientMethod request, Class<R> clazz) throws YarnException, IOException { | ||
| ArrayList<SubClusterInfo> clusterIdList = new ArrayList<>(clusterIds); | ||
| return invokeConcurrent(clusterIdList, request, clazz); | ||
| } | ||
|
|
||
| private <R> Map<SubClusterInfo, R> invokeConcurrent(ArrayList<SubClusterInfo> subClusterInfo, | ||
| ClientMethod request, Class<R> clazz) { | ||
|
|
||
| Map<SubClusterInfo, R> results = new HashMap<>(); | ||
|
|
||
| // Send the requests in parallel | ||
| CompletionService<R> compSvc = new ExecutorCompletionService<>(this.threadpool); | ||
|
|
||
| for (final SubClusterInfo info : subClusterInfo) { | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You could pass directly: No need to make it an array list.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Thanks a lot for the advice, let me learn a lot, the code has been modified. |
||
| compSvc.submit(() -> { | ||
| DefaultRequestInterceptorREST interceptor = getOrCreateInterceptorForSubCluster( | ||
| info.getSubClusterId(), info.getRMWebServiceAddress()); | ||
| try { | ||
| Method method = DefaultRequestInterceptorREST.class. | ||
| getMethod(request.getMethodName(), request.getTypes()); | ||
| return clazz.cast(method.invoke(interceptor, request.getParams())); | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Extract to make it clear what type is each thing.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I added cast to R. |
||
| } catch (Exception e) { | ||
| LOG.error("SubCluster {} failed to Call {} Method}.", info.getSubClusterId(), | ||
| request.getMethodName(), e); | ||
| return null; | ||
| } | ||
| }); | ||
| } | ||
|
|
||
| for (int i = 0; i < subClusterInfo.size(); i++) { | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This would be cleaner as: |
||
| try { | ||
| Future<R> future = compSvc.take(); | ||
| R response = future.get(); | ||
| if (response != null) { | ||
| results.put(subClusterInfo.get(i), response); | ||
| } | ||
| } catch (Throwable e) { | ||
| LOG.warn("Failed to get containers report. ", e); | ||
| } | ||
| } | ||
|
|
||
| return results; | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -24,9 +24,11 @@ | |
|
|
||
| import javax.ws.rs.core.Response; | ||
|
|
||
| import org.apache.hadoop.util.Time; | ||
| import org.apache.hadoop.yarn.api.records.ApplicationId; | ||
| import org.apache.hadoop.yarn.api.records.Resource; | ||
| import org.apache.hadoop.yarn.api.records.ResourceOption; | ||
| import org.apache.hadoop.yarn.api.records.ApplicationAttemptId; | ||
| import org.apache.hadoop.yarn.conf.YarnConfiguration; | ||
| import org.apache.hadoop.yarn.exceptions.YarnException; | ||
| import org.apache.hadoop.yarn.server.federation.policies.manager.UniformBroadcastPolicyManager; | ||
|
|
@@ -47,6 +49,7 @@ | |
| import org.apache.hadoop.yarn.server.resourcemanager.webapp.dao.NodesInfo; | ||
| import org.apache.hadoop.yarn.server.resourcemanager.webapp.dao.ResourceInfo; | ||
| import org.apache.hadoop.yarn.server.resourcemanager.webapp.dao.ResourceOptionInfo; | ||
| import org.apache.hadoop.yarn.server.webapp.dao.ContainersInfo; | ||
| import org.apache.hadoop.yarn.util.MonotonicClock; | ||
| import org.junit.Assert; | ||
| import org.junit.Test; | ||
|
|
@@ -160,7 +163,7 @@ public void testSubmitApplication() | |
| throws YarnException, IOException, InterruptedException { | ||
|
|
||
| ApplicationId appId = | ||
| ApplicationId.newInstance(System.currentTimeMillis(), 1); | ||
| ApplicationId.newInstance(Time.now(), 1); | ||
|
|
||
| ApplicationSubmissionContextInfo context = | ||
| new ApplicationSubmissionContextInfo(); | ||
|
|
@@ -187,7 +190,7 @@ public void testSubmitApplicationMultipleSubmission() | |
| throws YarnException, IOException, InterruptedException { | ||
|
|
||
| ApplicationId appId = | ||
| ApplicationId.newInstance(System.currentTimeMillis(), 1); | ||
| ApplicationId.newInstance(Time.now(), 1); | ||
| ApplicationSubmissionContextInfo context = | ||
| new ApplicationSubmissionContextInfo(); | ||
| context.setApplicationId(appId.toString()); | ||
|
|
@@ -259,7 +262,7 @@ public void testForceKillApplication() | |
| throws YarnException, IOException, InterruptedException { | ||
|
|
||
| ApplicationId appId = | ||
| ApplicationId.newInstance(System.currentTimeMillis(), 1); | ||
| ApplicationId.newInstance(Time.now(), 1); | ||
| ApplicationSubmissionContextInfo context = | ||
| new ApplicationSubmissionContextInfo(); | ||
| context.setApplicationId(appId.toString()); | ||
|
|
@@ -286,7 +289,7 @@ public void testForceKillApplicationNotExists() | |
| throws YarnException, IOException, InterruptedException { | ||
|
|
||
| ApplicationId appId = | ||
| ApplicationId.newInstance(System.currentTimeMillis(), 1); | ||
| ApplicationId.newInstance(Time.now(), 1); | ||
| AppState appState = new AppState("KILLED"); | ||
|
|
||
| Response response = | ||
|
|
@@ -317,7 +320,7 @@ public void testForceKillApplicationWrongFormat() | |
| public void testForceKillApplicationEmptyRequest() | ||
| throws YarnException, IOException, InterruptedException { | ||
| ApplicationId appId = | ||
| ApplicationId.newInstance(System.currentTimeMillis(), 1); | ||
| ApplicationId.newInstance(Time.now(), 1); | ||
|
|
||
| ApplicationSubmissionContextInfo context = | ||
| new ApplicationSubmissionContextInfo(); | ||
|
|
@@ -341,7 +344,7 @@ public void testGetApplicationReport() | |
| throws YarnException, IOException, InterruptedException { | ||
|
|
||
| ApplicationId appId = | ||
| ApplicationId.newInstance(System.currentTimeMillis(), 1); | ||
| ApplicationId.newInstance(Time.now(), 1); | ||
| ApplicationSubmissionContextInfo context = | ||
| new ApplicationSubmissionContextInfo(); | ||
| context.setApplicationId(appId.toString()); | ||
|
|
@@ -478,7 +481,7 @@ public void testGetApplicationState() | |
| throws YarnException, IOException, InterruptedException { | ||
|
|
||
| ApplicationId appId = | ||
| ApplicationId.newInstance(System.currentTimeMillis(), 1); | ||
| ApplicationId.newInstance(Time.now(), 1); | ||
| ApplicationSubmissionContextInfo context = | ||
| new ApplicationSubmissionContextInfo(); | ||
| context.setApplicationId(appId.toString()); | ||
|
|
@@ -505,7 +508,7 @@ public void testGetApplicationStateNotExists() | |
| throws YarnException, IOException, InterruptedException { | ||
|
|
||
| ApplicationId appId = | ||
| ApplicationId.newInstance(System.currentTimeMillis(), 1); | ||
| ApplicationId.newInstance(Time.now(), 1); | ||
|
|
||
| AppState response = interceptor.getAppState(null, appId.toString()); | ||
|
|
||
|
|
@@ -560,4 +563,49 @@ SubClusterState.SC_RUNNING, new MonotonicClock().getTime(), | |
| SubClusterRegisterRequest.newInstance(subClusterInfo)); | ||
| } | ||
|
|
||
| @Test | ||
| public void testGetContainers() | ||
| throws YarnException, IOException, InterruptedException { | ||
|
|
||
| ApplicationId appId = ApplicationId.newInstance(Time.now(), 1); | ||
| ApplicationSubmissionContextInfo context = | ||
|
goiri marked this conversation as resolved.
|
||
| new ApplicationSubmissionContextInfo(); | ||
| context.setApplicationId(appId.toString()); | ||
|
|
||
| // Submit the application we want the report later | ||
| Response response = interceptor.submitApplication(context, null); | ||
|
|
||
| Assert.assertNotNull(response); | ||
| Assert.assertNotNull(stateStoreUtil.queryApplicationHomeSC(appId)); | ||
|
|
||
| ApplicationAttemptId appAttempt = | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. One line |
||
| ApplicationAttemptId.newInstance(appId, 1); | ||
|
|
||
| ContainersInfo responseGet = interceptor.getContainers(null, null, | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Move all the args to the second line:
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I will fix it. |
||
| appId.toString(), appAttempt.toString()); | ||
|
|
||
| Assert.assertEquals(4, responseGet.getContainers().size()); | ||
| } | ||
|
|
||
| @Test | ||
| public void testGetContainersNotExists() { | ||
| ApplicationId appId = ApplicationId.newInstance(Time.now(), 1); | ||
| ContainersInfo response = | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. One line.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I will fix it. |
||
| interceptor.getContainers(null, null, appId.toString(), null); | ||
| Assert.assertTrue(response.getContainers().isEmpty()); | ||
| } | ||
|
|
||
| @Test | ||
| public void testGetContainersWrongFormat() { | ||
| ContainersInfo response = | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fits in one line.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I will fix it. |
||
| interceptor.getContainers(null, null, "Application_wrong_id", null); | ||
|
|
||
| Assert.assertNotNull(response); | ||
| Assert.assertTrue(response.getContainers().isEmpty()); | ||
|
|
||
| ApplicationId appId = ApplicationId.newInstance(Time.now(), 1); | ||
| response = interceptor.getContainers(null, null, appId.toString(), "AppAttempt_wrong_id"); | ||
|
|
||
| Assert.assertTrue(response.getContainers().isEmpty()); | ||
| } | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You don't need to create the ArrayList.
Should just work: https://docs.oracle.com/javase/7/docs/api/java/util/ArrayList.html#addAll(java.util.Collection)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the suggestion, I will modify the code.