提交 dac0a693 编写于 作者: L lepdou

多环境容错处理

上级 dc493792
package com.ctrip.apollo.common.http;
import org.springframework.http.HttpStatus;
import java.util.LinkedList;
import java.util.List;
/**
* 一个Response中包含多个ResponseEntity,每个ResponseEntity都包含完整的Response信息
*/
public class MultiResponseEntity<T> {
private int code;
private List<RichResponseEntity<T>> entities = new LinkedList<>();
private MultiResponseEntity(HttpStatus httpCode) {
this.code = httpCode.value();
}
public static <T> MultiResponseEntity<T> instance(HttpStatus statusCode) {
return new MultiResponseEntity<>(statusCode);
}
public static <T> MultiResponseEntity<T> ok() {
return new MultiResponseEntity<>(HttpStatus.OK);
}
public void addResponseEntity(RichResponseEntity<T> responseEntity) {
if (responseEntity == null){
throw new IllegalArgumentException("sub response entity can not be null");
}
entities.add(responseEntity);
}
}
package com.ctrip.apollo.common.http;
import org.springframework.http.HttpStatus;
/**
* 增强ResponseEntity,使得ResponseEntity可以附加message信息
* @param <T>
*/
public class RichResponseEntity<T>{
private int code;
private Object message;
private T body;
public static <T> RichResponseEntity<T> ok(T body){
RichResponseEntity<T> richResponseEntity = new RichResponseEntity<>();
richResponseEntity.message = HttpStatus.OK.getReasonPhrase();
richResponseEntity.code = HttpStatus.OK.value();
richResponseEntity.body = body;
return richResponseEntity;
}
public static <T> RichResponseEntity<T> error(HttpStatus httpCode, Object message){
RichResponseEntity<T> richResponseEntity = new RichResponseEntity<>();
richResponseEntity.message = message;
richResponseEntity.code = httpCode.value();
return richResponseEntity;
}
public int getCode() {
return code;
}
public Object getMessage() {
return message;
}
public T getBody() {
return body;
}
}
package com.ctrip.apollo.portal.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.HttpClientErrorException;
import com.ctrip.apollo.common.http.MultiResponseEntity;
import com.ctrip.apollo.common.http.RichResponseEntity;
import com.ctrip.apollo.core.dto.AppDTO;
import com.ctrip.apollo.core.enums.Env;
import com.ctrip.apollo.core.exception.BadRequestException;
import com.ctrip.apollo.core.utils.StringUtils;
import com.ctrip.apollo.portal.entity.AppInfoVO;
import com.ctrip.apollo.portal.entity.ClusterNavTree;
import com.ctrip.apollo.portal.PortalSettings;
import com.ctrip.apollo.portal.entity.EnvClusterInfo;
import com.ctrip.apollo.portal.service.AppService;
import java.util.List;
......@@ -25,6 +29,8 @@ public class AppController {
@Autowired
private AppService appService;
@Autowired
private PortalSettings portalSettings;
@RequestMapping("/envs/{env}")
public List<AppDTO> findAllApp(@PathVariable String env){
......@@ -35,12 +41,23 @@ public class AppController {
}
@RequestMapping("/{appId}/navtree")
public ClusterNavTree nav(@PathVariable String appId) {
public MultiResponseEntity<EnvClusterInfo> nav(@PathVariable String appId) {
if (StringUtils.isEmpty(appId)) {
throw new BadRequestException("app id can not be empty.");
}
return appService.buildClusterNavTree(appId);
MultiResponseEntity<EnvClusterInfo> response = MultiResponseEntity.ok();
List<Env> envs = portalSettings.getEnvs();
for (Env env : envs) {
try {
response.addResponseEntity(RichResponseEntity.ok(appService.createEnvNavNode(env, appId)));
} catch (Exception e) {
response.addResponseEntity(RichResponseEntity.error(HttpStatus.INTERNAL_SERVER_ERROR,
"load env:" + env.name() + " cluster error." + e
.getMessage()));
}
}
return response;
}
@RequestMapping(value = "/envs/{env}", method = RequestMethod.POST, consumes = {"application/json"})
......@@ -57,13 +74,38 @@ public class AppController {
}
@RequestMapping(value = "/{appId}", method = RequestMethod.GET)
public AppInfoVO load(@PathVariable String appId){
public AppDTO load(@PathVariable String appId){
if (StringUtils.isEmpty(appId)){
throw new BadRequestException("app id can not be empty.");
}
return appService.load(appId);
}
@RequestMapping(value = "/{appId}/miss_envs")
public MultiResponseEntity<Env> findMissEnvs(@PathVariable String appId) {
MultiResponseEntity<Env> response = MultiResponseEntity.ok();
for (Env env : portalSettings.getEnvs()) {
try {
appService.load(env, appId);
} catch (Exception e) {
if (e instanceof HttpClientErrorException &&
((HttpClientErrorException)e).getStatusCode() == HttpStatus.NOT_FOUND){
response.addResponseEntity(RichResponseEntity.ok(env));
} else {
response.addResponseEntity(RichResponseEntity.error(HttpStatus.INTERNAL_SERVER_ERROR,
String
.format("load appId:%s from env %s error.", appId,
env)
+ e.getMessage()));
}
}
}
return response;
}
private boolean isInvalidApp(AppDTO app) {
return StringUtils.isContainEmpty(app.getName(), app.getAppId(), app.getOwnerEmail(), app.getOwnerName());
}
......
package com.ctrip.apollo.portal.entity;
import com.ctrip.apollo.core.dto.AppDTO;
import com.ctrip.apollo.core.enums.Env;
import java.util.List;
public class AppInfoVO {
private AppDTO app;
/**
* 在创建app的时候可能在某些环境下创建失败
*/
private List<Env> missEnvs;
public AppDTO getApp() {
return app;
}
public void setApp(AppDTO app) {
this.app = app;
}
public List<Env> getMissEnvs() {
return missEnvs;
}
public void setMissEnvs(List<Env> missEnvs) {
this.missEnvs = missEnvs;
}
}
package com.ctrip.apollo.portal.entity;
import com.ctrip.apollo.core.enums.Env;
import com.ctrip.apollo.core.dto.ClusterDTO;
import java.util.LinkedList;
import java.util.List;
public class ClusterNavTree {
private List<Node> nodes;
public void addNode(Node node){
if (nodes == null){
nodes = new LinkedList<>();
}
nodes.add(node);
}
public static class Node{
private Env env;
private List<ClusterDTO> clusters;
public Node(Env env){
this.env = env;
}
public Env getEnv() {
return env;
}
public void setEnv(Env env) {
this.env = env;
}
public List<ClusterDTO> getClusters() {
return clusters;
}
public void setClusters(List<ClusterDTO> clusters) {
this.clusters = clusters;
}
}
public List<Node> getNodes() {
return nodes;
}
public void setNodes(List<Node> nodes) {
this.nodes = nodes;
}
}
package com.ctrip.apollo.portal.entity;
import com.ctrip.apollo.core.dto.ClusterDTO;
import com.ctrip.apollo.core.enums.Env;
import java.util.List;
public class EnvClusterInfo {
private Env env;
private List<ClusterDTO> clusters;
public EnvClusterInfo(Env env){
this.env = env;
}
public Env getEnv() {
return env;
}
public void setEnv(Env env) {
this.env = env;
}
public List<ClusterDTO> getClusters() {
return clusters;
}
public void setClusters(List<ClusterDTO> clusters) {
this.clusters = clusters;
}
}
package com.ctrip.apollo.portal.service;
import java.util.LinkedList;
import java.util.List;
import org.slf4j.Logger;
......@@ -18,69 +17,56 @@ import com.ctrip.apollo.core.exception.BadRequestException;
import com.ctrip.apollo.core.exception.ServiceException;
import com.ctrip.apollo.portal.PortalSettings;
import com.ctrip.apollo.portal.api.AdminServiceAPI;
import com.ctrip.apollo.portal.entity.AppInfoVO;
import com.ctrip.apollo.portal.entity.ClusterNavTree;
import com.ctrip.apollo.portal.entity.EnvClusterInfo;
@Service
public class AppService {
private Logger logger = LoggerFactory.getLogger(AppService.class);
@Autowired
private ClusterService clusterService;
@Autowired
private PortalSettings portalSettings;
@Autowired
private AdminServiceAPI.AppAPI appAPI;
@Autowired ClusterService clusterService;
public List<AppDTO> findAll(Env env) {
return appAPI.findApps(env);
}
public AppInfoVO load(String appId) {
public AppDTO load(String appId) {
//轮询环境直到能找到此app的信息
AppDTO app = null;
List<Env> missEnvs = new LinkedList<>();
boolean isCallAdminServiceError = false;
for (Env env : portalSettings.getEnvs()) {
try {
app = appAPI.loadApp(env, appId);
break;
} catch (HttpClientErrorException e) {
//not exist maybe because create app fail.
if (e.getStatusCode() == HttpStatus.NOT_FOUND) {
missEnvs.add(env);
logger.warn("app:{} in {} not exist", appId, env);
} else {
isCallAdminServiceError = true;
logger.error("load app info({}) from env:{} error.", appId, env);
throw new ServiceException("can not load app from all envs");
}
}
}
if (app == null) {
throw new BadRequestException(String.format("invalid app id %s", appId));
if (isCallAdminServiceError){
throw new ServiceException("call admin service error");
}else {
throw new BadRequestException(String.format("invalid app id %s", appId));
}
}
AppInfoVO appInfo = new AppInfoVO();
appInfo.setApp(app);
appInfo.setMissEnvs(missEnvs);
return appInfo;
return app;
}
public ClusterNavTree buildClusterNavTree(String appId) {
ClusterNavTree tree = new ClusterNavTree();
List<Env> envs = portalSettings.getEnvs();
for (Env env : envs) {
ClusterNavTree.Node clusterNode = new ClusterNavTree.Node(env);
clusterNode.setClusters(clusterService.findClusters(env, appId));
tree.addNode(clusterNode);
}
return tree;
public AppDTO load(Env env, String appId){
return appAPI.loadApp(env, appId);
}
public void createAppInAllEnvs(AppDTO app) {
......@@ -104,4 +90,10 @@ public class AppService {
}
}
public EnvClusterInfo createEnvNavNode(Env env, String appId){
EnvClusterInfo node = new EnvClusterInfo(env);
node.setClusters(clusterService.findClusters(env, appId));
return node;
}
}
appUtil.service('AppUtil', [function () {
appUtil.service('AppUtil', ['toastr', function (toastr) {
return {
errorMsg: function (response) {
......@@ -22,6 +22,17 @@ appUtil.service('AppUtil', [function () {
result[kv[0]] = kv[1];
});
return result;
},
collectData: function (response) {
var data = [];
response.entities.forEach(function (entity) {
if (entity.code == 200){
data.push(entity.body);
}else {
toastr.warning(entity.message);
}
});
return data;
}
}
}]);
......@@ -2,7 +2,7 @@
var appService = angular.module('app.service', ['ngResource']);
/**utils*/
var appUtil = angular.module('app.util', []);
var appUtil = angular.module('app.util', ['toastr']);
/** directive */
var directive_module = angular.module('apollo.directive', ['app.service']);
......
......@@ -11,7 +11,8 @@ namespace_module.controller("LinkNamespaceController",
////// load env //////
AppService.load_nav_tree($scope.appId).then(function (result) {
$scope.namespaceIdentifers = [];
result.nodes.forEach(function (node) {
var envClusterInfo = AppUtil.collectData(result);
envClusterInfo.forEach(function (node) {
var env = node.env;
node.clusters.forEach(function (cluster) {
cluster.env = env;
......
......@@ -16,7 +16,8 @@ application_module.controller("ConfigBaseInfoController",
AppService.load_nav_tree($rootScope.pageContext.appId).then(function (result) {
var navTree = [];
var nodes = result.nodes;
var nodes = AppUtil.collectData(result);
nodes.forEach(function (item) {
var node = {};
//first nav
......@@ -41,6 +42,7 @@ application_module.controller("ConfigBaseInfoController",
node.nodes = clusterNodes;
navTree.push(node);
});
$('#treeview').treeview({
color: "#797979",
showBorder: true,
......@@ -57,22 +59,28 @@ application_module.controller("ConfigBaseInfoController",
$rootScope.refreshNamespaces();
}
});
}, function (result) {
toastr.error(AppUtil.errorMsg(result), "加载导航出错");
});
////// app info //////
AppService.load($rootScope.pageContext.appId).then(function (result) {
$scope.appBaseInfo = result.app;
$scope.missEnvs = result.missEnvs;
$scope.selectedEnvs = angular.copy($scope.missEnvs);
$scope.appBaseInfo = result;
},function (result) {
toastr.error(AppUtil.errorMsg(result), "加载App信息出错");
});
////// 补缺失的环境 //////
$scope.missEnvs = [];
AppService.find_miss_envs($rootScope.pageContext.appId).then(function (result) {
$scope.missEnvs = AppUtil.collectData(result);
},function (result) {
console.log(AppUtil.errorMsg(result));
});
$scope.toggleSelection = function toggleSelection(env) {
var idx = $scope.selectedEnvs.indexOf(env);
......
......@@ -13,7 +13,8 @@ sync_item_module.controller("SyncItemController",
////// load env //////
AppService.load_nav_tree($scope.pageContext.appId).then(function (result) {
$scope.namespaceIdentifers = [];
result.nodes.forEach(function (node) {
var envClusterInfo = AppUtil.collectData(result);
envClusterInfo.forEach(function (node) {
var env = node.env;
node.clusters.forEach(function (cluster) {
cluster.env = env;
......
......@@ -17,6 +17,10 @@ appService.service('AppService', ['$resource', '$q', function ($resource, $q) {
create_app: {
method: 'POST',
url: '/apps/envs/:env'
},
find_miss_envs: {
method: 'GET',
url: '/apps/:appId/miss_envs'
}
});
return {
......@@ -61,6 +65,17 @@ appService.service('AppService', ['$resource', '$q', function ($resource, $q) {
d.reject(result);
});
return d.promise;
},
find_miss_envs: function (appId) {
var d = $q.defer();
app_resource.find_miss_envs({
appId: appId
}, function (result) {
d.resolve(result);
}, function (result) {
d.reject(result);
});
return d.promise;
}
}
}]);
......@@ -8,8 +8,8 @@ import org.junit.runners.Suite.SuiteClasses;
@RunWith(Suite.class)
@SuiteClasses({
ConfigServiceTest.class, PropertyResolverTest.class,
AppServiceTest.class, NamespaceServiceTest.class
})
NamespaceServiceTest.class
})
public class AllTests {
}
package com.ctrip.apollo.portal;
import com.ctrip.apollo.core.dto.ClusterDTO;
import com.ctrip.apollo.core.enums.Env;
import com.ctrip.apollo.portal.api.AdminServiceAPI;
import com.ctrip.apollo.portal.entity.ClusterNavTree;
import com.ctrip.apollo.portal.service.AppService;
import com.ctrip.apollo.portal.service.ClusterService;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.runners.MockitoJUnitRunner;
import java.util.Arrays;
import java.util.List;
import static org.junit.Assert.assertEquals;
import static org.mockito.Mockito.when;
@RunWith(MockitoJUnitRunner.class)
public class AppServiceTest {
@Mock
private PortalSettings settings;
@Mock
private ClusterService clusterService;
@Mock
private AdminServiceAPI.AppAPI appAPI;
@InjectMocks
private AppService appService;
@Test
public void testBuildNavTree(){
String appId = "6666";
ClusterDTO c1 = new ClusterDTO();
c1.setAppId(appId);
c1.setName("default");
c1.setId(1);
ClusterDTO c2 = new ClusterDTO();
c2.setAppId(appId);
c2.setName("oy");
c2.setId(2);
List<ClusterDTO> clusterDTOs = Arrays.asList(c1, c2);
when(settings.getEnvs()).thenReturn(Arrays.asList(Env.DEV, Env.FAT));
when(clusterService.findClusters(Env.DEV, appId)).thenReturn(clusterDTOs);
when(clusterService.findClusters(Env.FAT, appId)).thenReturn(Arrays.asList(c1));
ClusterNavTree tree = appService.buildClusterNavTree(appId);
assertEquals(2, tree.getNodes().size());
ClusterNavTree.Node node1 = tree.getNodes().get(0);
assertEquals(Env.DEV, node1.getEnv());
assertEquals(2, node1.getClusters().size());
assertEquals("default", node1.getClusters().get(0).getName());
}
// @Test
// public void testSaveApp(){
// String appId = "6666";
// String appName = "hermas";
// AppDTO appDTO = new AppDTO();
// appDTO.setAppId(appId);
// appDTO.setName(appName);
// appDTO.setDataChangeLastModifiedBy("ll");
// appDTO.setDataChangeCreatedTime(new Date());
// appDTO.setOwnerEmail("qq@qq.com");
// appDTO.setOwnerName("zz");
//
// when(appService.createApp(appDTO)).thenReturn(appDTO);
//
// AppDTO createApp = appService.createApp(appDTO);
//
// assertEquals(appId, createApp.getAppId());
// assertEquals(appName, createApp.getName());
// }
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册