mirror of
https://gitee.com/durcframework/SOP.git
synced 2025-08-11 21:57:56 +08:00
支持预发布
This commit is contained in:
@@ -10,6 +10,7 @@ import com.gitee.sop.adminserver.api.service.param.ServiceSearchParam;
|
|||||||
import com.gitee.sop.adminserver.api.service.result.RouteServiceInfo;
|
import com.gitee.sop.adminserver.api.service.result.RouteServiceInfo;
|
||||||
import com.gitee.sop.adminserver.api.service.result.ServiceInfoVo;
|
import com.gitee.sop.adminserver.api.service.result.ServiceInfoVo;
|
||||||
import com.gitee.sop.adminserver.api.service.result.ServiceInstanceVO;
|
import com.gitee.sop.adminserver.api.service.result.ServiceInstanceVO;
|
||||||
|
import com.gitee.sop.adminserver.bean.MetadataEnum;
|
||||||
import com.gitee.sop.adminserver.bean.ServiceRouteInfo;
|
import com.gitee.sop.adminserver.bean.ServiceRouteInfo;
|
||||||
import com.gitee.sop.adminserver.bean.ZookeeperContext;
|
import com.gitee.sop.adminserver.bean.ZookeeperContext;
|
||||||
import com.gitee.sop.adminserver.common.BizException;
|
import com.gitee.sop.adminserver.common.BizException;
|
||||||
@@ -144,6 +145,9 @@ public class ServiceApi {
|
|||||||
int id = idGen.getAndIncrement();
|
int id = idGen.getAndIncrement();
|
||||||
instanceVO.setId(id);
|
instanceVO.setId(id);
|
||||||
instanceVO.setParentId(pid);
|
instanceVO.setParentId(pid);
|
||||||
|
if (instanceVO.getMetadata() == null) {
|
||||||
|
instanceVO.setMetadata(Collections.emptyMap());
|
||||||
|
}
|
||||||
serviceInfoVoList.add(instanceVO);
|
serviceInfoVoList.add(instanceVO);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -152,21 +156,57 @@ public class ServiceApi {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Api(name = "service.instance.offline")
|
@Api(name = "service.instance.offline")
|
||||||
@ApiDocMethod(description = "服务下线")
|
@ApiDocMethod(description = "服务禁用")
|
||||||
void serviceOffline(ServiceInstance param) {
|
void serviceOffline(ServiceInstance param) {
|
||||||
try {
|
try {
|
||||||
registryService.offlineInstance(param);
|
registryService.offlineInstance(param);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.error("下线失败,param:{}", param, e);
|
log.error("服务禁用失败,param:{}", param, e);
|
||||||
throw new BizException("下线失败,请查看日志");
|
throw new BizException("服务禁用失败,请查看日志");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Api(name = "service.instance.online")
|
@Api(name = "service.instance.online")
|
||||||
@ApiDocMethod(description = "服务上线")
|
@ApiDocMethod(description = "服务启用")
|
||||||
void serviceOnline(ServiceInstance param) throws IOException {
|
void serviceOnline(ServiceInstance param) throws IOException {
|
||||||
try {
|
try {
|
||||||
registryService.onlineInstance(param);
|
registryService.onlineInstance(param);
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("服务启用失败,param:{}", param, e);
|
||||||
|
throw new BizException("服务启用失败,请查看日志");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Api(name = "service.instance.env.pre")
|
||||||
|
@ApiDocMethod(description = "预发布")
|
||||||
|
void serviceEnvPre(ServiceInstance param) throws IOException {
|
||||||
|
try {
|
||||||
|
MetadataEnum envPre = MetadataEnum.ENV_PRE;
|
||||||
|
registryService.setMetadata(param, envPre.getKey(), envPre.getValue());
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("预发布失败,param:{}", param, e);
|
||||||
|
throw new BizException("预发布失败,请查看日志");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Api(name = "service.instance.env.gray")
|
||||||
|
@ApiDocMethod(description = "灰度发布")
|
||||||
|
void serviceEnvGray(ServiceInstance param) throws IOException {
|
||||||
|
try {
|
||||||
|
MetadataEnum envPre = MetadataEnum.ENV_GRAY;
|
||||||
|
registryService.setMetadata(param, envPre.getKey(), envPre.getValue());
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("灰度发布失败,param:{}", param, e);
|
||||||
|
throw new BizException("灰度发布失败,请查看日志");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Api(name = "service.instance.env.online")
|
||||||
|
@ApiDocMethod(description = "上线")
|
||||||
|
void serviceEnvOnline(ServiceInstance param) throws IOException {
|
||||||
|
try {
|
||||||
|
MetadataEnum envPre = MetadataEnum.ENV_ONLINE;
|
||||||
|
registryService.setMetadata(param, envPre.getKey(), envPre.getValue());
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.error("上线失败,param:{}", param, e);
|
log.error("上线失败,param:{}", param, e);
|
||||||
throw new BizException("上线失败,请查看日志");
|
throw new BizException("上线失败,请查看日志");
|
||||||
|
@@ -3,6 +3,8 @@ package com.gitee.sop.adminserver.api.service.result;
|
|||||||
import com.gitee.easyopen.doc.annotation.ApiDocField;
|
import com.gitee.easyopen.doc.annotation.ApiDocField;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author tanghc
|
* @author tanghc
|
||||||
*/
|
*/
|
||||||
@@ -35,6 +37,9 @@ public class ServiceInstanceVO {
|
|||||||
@ApiDocField(description = "parentId")
|
@ApiDocField(description = "parentId")
|
||||||
private Integer parentId;
|
private Integer parentId;
|
||||||
|
|
||||||
|
@ApiDocField(description = "metadata")
|
||||||
|
private Map<String, String> metadata;
|
||||||
|
|
||||||
public String getIpPort() {
|
public String getIpPort() {
|
||||||
return ip != null && port > 0 ? ip + ":" + port : "";
|
return ip != null && port > 0 ? ip + ":" + port : "";
|
||||||
}
|
}
|
||||||
|
@@ -0,0 +1,37 @@
|
|||||||
|
package com.gitee.sop.adminserver.bean;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author tanghc
|
||||||
|
*/
|
||||||
|
public enum MetadataEnum {
|
||||||
|
/**
|
||||||
|
* 预发布环境
|
||||||
|
*/
|
||||||
|
ENV_PRE("env", "pre"),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 上线环境
|
||||||
|
*/
|
||||||
|
ENV_ONLINE("env", ""),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 灰度环境
|
||||||
|
*/
|
||||||
|
ENV_GRAY("env", "gray"),
|
||||||
|
;
|
||||||
|
private String key,value;
|
||||||
|
|
||||||
|
MetadataEnum(String key, String value) {
|
||||||
|
this.key = key;
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getKey() {
|
||||||
|
return key;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getValue() {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -28,16 +28,36 @@
|
|||||||
label="IP端口"
|
label="IP端口"
|
||||||
width="250"
|
width="250"
|
||||||
/>
|
/>
|
||||||
|
<el-table-column
|
||||||
|
prop="metadata"
|
||||||
|
label="当前环境"
|
||||||
|
width="100"
|
||||||
|
>
|
||||||
|
<template slot-scope="scope">
|
||||||
|
<el-tag v-if="scope.row.parentId > 0 && scope.row.metadata.env === 'pre'" type="warning">预发布</el-tag>
|
||||||
|
<el-tag v-if="scope.row.parentId > 0 && scope.row.metadata.env === 'gray'" type="info">灰度</el-tag>
|
||||||
|
<el-tag v-if="scope.row.parentId > 0 && !scope.row.metadata.env" type="success">线上</el-tag>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column
|
||||||
|
prop="metadata"
|
||||||
|
label="metadata"
|
||||||
|
width="250"
|
||||||
|
>
|
||||||
|
<template slot-scope="scope">
|
||||||
|
<span v-if="scope.row.parentId > 0">{{ JSON.stringify(scope.row.metadata) }}</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
<el-table-column
|
<el-table-column
|
||||||
prop="status"
|
prop="status"
|
||||||
label="服务状态"
|
label="服务状态"
|
||||||
width="100"
|
width="100"
|
||||||
>
|
>
|
||||||
<template slot-scope="scope">
|
<template slot-scope="scope">
|
||||||
<el-tag v-if="scope.row.parentId > 0 && scope.row.status === 'UP'" type="success">已上线</el-tag>
|
<el-tag v-if="scope.row.parentId > 0 && scope.row.status === 'UP'" type="success">已启用</el-tag>
|
||||||
<el-tag v-if="scope.row.parentId > 0 && scope.row.status === 'STARTING'" type="info">正在启动</el-tag>
|
<el-tag v-if="scope.row.parentId > 0 && scope.row.status === 'STARTING'" type="info">正在启动</el-tag>
|
||||||
<el-tag v-if="scope.row.parentId > 0 && scope.row.status === 'UNKNOWN'">未知</el-tag>
|
<el-tag v-if="scope.row.parentId > 0 && scope.row.status === 'UNKNOWN'">未知</el-tag>
|
||||||
<el-tag v-if="scope.row.parentId > 0 && (scope.row.status === 'OUT_OF_SERVICE' || scope.row.status === 'DOWN')" type="danger">已下线</el-tag>
|
<el-tag v-if="scope.row.parentId > 0 && (scope.row.status === 'OUT_OF_SERVICE' || scope.row.status === 'DOWN')" type="danger">已禁用</el-tag>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column
|
<el-table-column
|
||||||
@@ -47,11 +67,14 @@
|
|||||||
/>
|
/>
|
||||||
<el-table-column
|
<el-table-column
|
||||||
label="操作"
|
label="操作"
|
||||||
width="100"
|
width="200"
|
||||||
>
|
>
|
||||||
<template slot-scope="scope">
|
<template slot-scope="scope">
|
||||||
<el-button v-if="scope.row.parentId > 0 && scope.row.status === 'UP'" type="text" size="mini" @click="onOffline(scope.row)">下线</el-button>
|
<el-button v-if="scope.row.parentId > 0 && scope.row.metadata.env" type="text" size="mini" @click="onEnvOnline(scope.row)">上线</el-button>
|
||||||
<el-button v-if="scope.row.parentId > 0 && scope.row.status === 'OUT_OF_SERVICE'" type="text" size="mini" @click="onOnline(scope.row)">上线</el-button>
|
<el-button v-if="scope.row.parentId > 0 && !scope.row.metadata.env" type="text" size="mini" @click="onEnvPre(scope.row)">预发布</el-button>
|
||||||
|
<el-button v-if="scope.row.parentId > 0 && !scope.row.metadata.env" type="text" size="mini" @click="onEnvGray(scope.row)">灰度发布</el-button>
|
||||||
|
<el-button v-if="scope.row.parentId > 0 && scope.row.status === 'UP'" type="text" size="mini" @click="onDisable(scope.row)">禁用</el-button>
|
||||||
|
<el-button v-if="scope.row.parentId > 0 && scope.row.status === 'OUT_OF_SERVICE'" type="text" size="mini" @click="onEnable(scope.row)">启用</el-button>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
</el-table>
|
</el-table>
|
||||||
@@ -103,22 +126,46 @@ export default {
|
|||||||
onSearchTable: function() {
|
onSearchTable: function() {
|
||||||
this.loadTable()
|
this.loadTable()
|
||||||
},
|
},
|
||||||
onOffline: function(row) {
|
onDisable: function(row) {
|
||||||
this.confirm('确定要下线【' + row.serviceId + '】吗?', function(done) {
|
this.confirm('确定要禁用【' + row.serviceId + '】吗?', function(done) {
|
||||||
this.post('service.instance.offline', row, function() {
|
this.post('service.instance.offline', row, function() {
|
||||||
this.tip('下线成功')
|
this.tip('下线成功')
|
||||||
done()
|
done()
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
onOnline: function(row) {
|
onEnable: function(row) {
|
||||||
this.confirm('确定要上线【' + row.serviceId + '】吗?', function(done) {
|
this.confirm('确定要启用【' + row.serviceId + '】吗?', function(done) {
|
||||||
this.post('service.instance.online', row, function() {
|
this.post('service.instance.online', row, function() {
|
||||||
this.tip('上线成功')
|
this.tip('上线成功')
|
||||||
done()
|
done()
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
onEnvOnline: function(row) {
|
||||||
|
this.confirm('确定要上线【' + row.serviceId + '】吗?', function(done) {
|
||||||
|
this.post('service.instance.env.online', row, function() {
|
||||||
|
this.tip('上线成功')
|
||||||
|
done()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
},
|
||||||
|
onEnvPre: function(row) {
|
||||||
|
this.confirm('确定要预发布【' + row.serviceId + '】吗?', function(done) {
|
||||||
|
this.post('service.instance.env.pre', row, function() {
|
||||||
|
this.tip('预发布成功')
|
||||||
|
done()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
},
|
||||||
|
onEnvGray: function(row) {
|
||||||
|
this.confirm('确定要灰度发布【' + row.serviceId + '】吗?', function(done) {
|
||||||
|
this.post('service.instance.env.gray', row, function() {
|
||||||
|
this.tip('灰度发布发成功')
|
||||||
|
done()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
},
|
||||||
renderServiceName: function(row) {
|
renderServiceName: function(row) {
|
||||||
let instanceCount = ''
|
let instanceCount = ''
|
||||||
if (row.children && row.children.length > 0) {
|
if (row.children && row.children.length > 0) {
|
||||||
@@ -128,6 +175,14 @@ export default {
|
|||||||
instanceCount = ` (${onlineCount}/${row.children.length})`
|
instanceCount = ` (${onlineCount}/${row.children.length})`
|
||||||
}
|
}
|
||||||
return row.serviceId + instanceCount
|
return row.serviceId + instanceCount
|
||||||
|
},
|
||||||
|
renderMetadata: function(row) {
|
||||||
|
const metadata = row.metadata
|
||||||
|
const html = []
|
||||||
|
for (const key in metadata) {
|
||||||
|
html.push(key + '=' + metadata[key])
|
||||||
|
}
|
||||||
|
return html.join('<br />')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -54,4 +54,5 @@ public class SopConstants {
|
|||||||
public static final String UNKNOWN_SERVICE= "_sop_unknown_service_";
|
public static final String UNKNOWN_SERVICE= "_sop_unknown_service_";
|
||||||
public static final String UNKNOWN_METHOD = "_sop_unknown_method_";
|
public static final String UNKNOWN_METHOD = "_sop_unknown_method_";
|
||||||
public static final String UNKNOWN_VERSION = "_sop_unknown_version_";
|
public static final String UNKNOWN_VERSION = "_sop_unknown_version_";
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -69,7 +69,7 @@ public class GatewayResultExecutor extends BaseExecutorAdapter<ServerWebExchange
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (error == null) {
|
if (error == null) {
|
||||||
error = ErrorEnum.AOP_UNKNOW_ERROR.getErrorMeta().getError();
|
error = ErrorEnum.ISP_UNKNOWN_ERROR.getErrorMeta().getError();
|
||||||
}
|
}
|
||||||
|
|
||||||
JSONObject jsonObject = (JSONObject) JSON.toJSON(error);
|
JSONObject jsonObject = (JSONObject) JSON.toJSON(error);
|
||||||
|
@@ -84,7 +84,7 @@ public class ReadBodyRoutePredicateFactory extends AbstractRoutePredicateFactory
|
|||||||
return Mono.just(test);
|
return Mono.just(test);
|
||||||
} catch (ClassCastException e) {
|
} catch (ClassCastException e) {
|
||||||
if (LOGGER.isDebugEnabled()) {
|
if (LOGGER.isDebugEnabled()) {
|
||||||
LOGGER.debug("Predicate test failed because class in predicate does not match the cached body object",
|
LOGGER.debug("Predicate test failed because class in predicate does not canVisit the cached body object",
|
||||||
e);
|
e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -9,9 +9,7 @@ public enum ErrorEnum {
|
|||||||
SUCCESS(Codes.CODE_SUCCESS, ""),
|
SUCCESS(Codes.CODE_SUCCESS, ""),
|
||||||
|
|
||||||
/** 服务暂不可用 */
|
/** 服务暂不可用 */
|
||||||
ISP_UNKNOW_ERROR(Codes.CODE_UNKNOW, "isp.unknow-error"),
|
ISP_UNKNOWN_ERROR(Codes.CODE_UNKNOW, "isp.unknown-error"),
|
||||||
/** 服务暂不可用 */
|
|
||||||
AOP_UNKNOW_ERROR(Codes.CODE_UNKNOW, "aop.unknow-error"),
|
|
||||||
/** 服务不可用,路由被禁用 */
|
/** 服务不可用,路由被禁用 */
|
||||||
ISP_API_DISABLED(Codes.CODE_UNKNOW, "isp.service-not-available"),
|
ISP_API_DISABLED(Codes.CODE_UNKNOW, "isp.service-not-available"),
|
||||||
|
|
||||||
|
@@ -76,7 +76,7 @@ public class ErrorFactory {
|
|||||||
// open.error_20000=Service is temporarily unavailable
|
// open.error_20000=Service is temporarily unavailable
|
||||||
String msg = getErrorMessage(modulePrefix + code, locale);
|
String msg = getErrorMessage(modulePrefix + code, locale);
|
||||||
String subCode = errorMeta.getSubCode();
|
String subCode = errorMeta.getSubCode();
|
||||||
// open.error_20000_isp.unknow-error=Service is temporarily unavailable
|
// open.error_20000_isp.unknown-error=Service is temporarily unavailable
|
||||||
String subMsg = getErrorMessage(modulePrefix + code + UNDERLINE + subCode, locale, params);
|
String subMsg = getErrorMessage(modulePrefix + code + UNDERLINE + subCode, locale, params);
|
||||||
if (StringUtils.isEmpty(msg)) {
|
if (StringUtils.isEmpty(msg)) {
|
||||||
msg = SYS_ERR;
|
msg = SYS_ERR;
|
||||||
@@ -85,7 +85,7 @@ public class ErrorFactory {
|
|||||||
subMsg = SYS_ERR;
|
subMsg = SYS_ERR;
|
||||||
}
|
}
|
||||||
// solution暂未实现,如果要实现,可以这样配置:
|
// solution暂未实现,如果要实现,可以这样配置:
|
||||||
// open.error_20000_isp.unknow-error_solution=Service is temporarily unavailable
|
// open.error_20000_isp.unknown-error_solution=Service is temporarily unavailable
|
||||||
// <code>String solution = getErrorMessage(modulePrefix + code + UNDERLINE + subCode + "_solution", locale, params);</code>
|
// <code>String solution = getErrorMessage(modulePrefix + code + UNDERLINE + subCode + "_solution", locale, params);</code>
|
||||||
error = new ErrorImpl(code, msg, subCode, subMsg, null);
|
error = new ErrorImpl(code, msg, subCode, subMsg, null);
|
||||||
errorCache.put(key, error);
|
errorCache.put(key, error);
|
||||||
|
@@ -51,7 +51,7 @@ public abstract class BaseParamBuilder<T> implements ParamBuilder<T> {
|
|||||||
RouteRepository<? extends TargetRoute> routeRepository = RouteRepositoryContext.getRouteRepository();
|
RouteRepository<? extends TargetRoute> routeRepository = RouteRepositoryContext.getRouteRepository();
|
||||||
if (routeRepository == null) {
|
if (routeRepository == null) {
|
||||||
log.error("RouteRepositoryContext.setRouteRepository()方法未使用");
|
log.error("RouteRepositoryContext.setRouteRepository()方法未使用");
|
||||||
throw ErrorEnum.AOP_UNKNOW_ERROR.getErrorMeta().getException();
|
throw ErrorEnum.ISP_UNKNOWN_ERROR.getErrorMeta().getException();
|
||||||
}
|
}
|
||||||
|
|
||||||
String nameVersion = Optional.ofNullable(apiParam.fetchNameVersion()).orElse(String.valueOf(System.currentTimeMillis()));
|
String nameVersion = Optional.ofNullable(apiParam.fetchNameVersion()).orElse(String.valueOf(System.currentTimeMillis()));
|
||||||
|
@@ -35,7 +35,7 @@ import java.util.Optional;
|
|||||||
@Slf4j
|
@Slf4j
|
||||||
public abstract class BaseExecutorAdapter<T, R> implements ResultExecutor<T, R> {
|
public abstract class BaseExecutorAdapter<T, R> implements ResultExecutor<T, R> {
|
||||||
private static final ErrorMeta SUCCESS_META = ErrorEnum.SUCCESS.getErrorMeta();
|
private static final ErrorMeta SUCCESS_META = ErrorEnum.SUCCESS.getErrorMeta();
|
||||||
private static final ErrorMeta ISP_UNKNOW_ERROR_META = ErrorEnum.ISP_UNKNOW_ERROR.getErrorMeta();
|
private static final ErrorMeta ISP_UNKNOW_ERROR_META = ErrorEnum.ISP_UNKNOWN_ERROR.getErrorMeta();
|
||||||
private static final ErrorMeta ISP_BIZ_ERROR = ErrorEnum.BIZ_ERROR.getErrorMeta();
|
private static final ErrorMeta ISP_BIZ_ERROR = ErrorEnum.BIZ_ERROR.getErrorMeta();
|
||||||
|
|
||||||
public static final String GATEWAY_CODE_NAME = "code";
|
public static final String GATEWAY_CODE_NAME = "code";
|
||||||
|
@@ -6,7 +6,7 @@ package com.gitee.sop.gatewaycommon.result;
|
|||||||
* "result": {
|
* "result": {
|
||||||
* "code": "20000",
|
* "code": "20000",
|
||||||
* "msg": "Service Currently Unavailable",
|
* "msg": "Service Currently Unavailable",
|
||||||
* "sub_code": "isp.unknow-error",
|
* "sub_code": "isp.unknown-error",
|
||||||
* "sub_msg": "系统繁忙"
|
* "sub_msg": "系统繁忙"
|
||||||
* },
|
* },
|
||||||
* "sign": "ERITJKEIJKJHKKKKKKKHJEREEEEEEEEEEE"
|
* "sign": "ERITJKEIJKJHKKKKKKKHJEREEEEEEEEEEE"
|
||||||
|
@@ -8,7 +8,7 @@ package com.gitee.sop.gatewaycommon.result;
|
|||||||
* "alipay_trade_order_settle_response": {
|
* "alipay_trade_order_settle_response": {
|
||||||
* "code": "20000",
|
* "code": "20000",
|
||||||
* "msg": "Service Currently Unavailable",
|
* "msg": "Service Currently Unavailable",
|
||||||
* "sub_code": "isp.unknow-error",
|
* "sub_code": "isp.unknown-error",
|
||||||
* "sub_msg": "系统繁忙"
|
* "sub_msg": "系统繁忙"
|
||||||
* },
|
* },
|
||||||
* "sign": "ERITJKEIJKJHKKKKKKKHJEREEEEEEEEEEE"
|
* "sign": "ERITJKEIJKJHKKKKKKKHJEREEEEEEEEEEE"
|
||||||
|
@@ -22,7 +22,7 @@ package com.gitee.sop.gatewaycommon.result;
|
|||||||
* "alipay_trade_fastpay_refund_query_response": {
|
* "alipay_trade_fastpay_refund_query_response": {
|
||||||
* "code": "20000",
|
* "code": "20000",
|
||||||
* "msg": "Service Currently Unavailable",
|
* "msg": "Service Currently Unavailable",
|
||||||
* "sub_code": "isp.unknow-error",
|
* "sub_code": "isp.unknown-error",
|
||||||
* "sub_msg": "系统繁忙"
|
* "sub_msg": "系统繁忙"
|
||||||
* },
|
* },
|
||||||
* "sign": "ERITJKEIJKJHKKKKKKKHJEREEEEEEEEEEE"
|
* "sign": "ERITJKEIJKJHKKKKKKKHJEREEEEEEEEEEE"
|
||||||
|
@@ -36,7 +36,7 @@ public class ApiSessionManager implements SessionManager {
|
|||||||
return cache.get(sessionId);
|
return cache.get(sessionId);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
logger.error(e.getMessage(), e);
|
logger.error(e.getMessage(), e);
|
||||||
throw ErrorEnum.AOP_UNKNOW_ERROR.getErrorMeta().getException();
|
throw ErrorEnum.ISP_UNKNOWN_ERROR.getErrorMeta().getException();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -0,0 +1,72 @@
|
|||||||
|
package com.gitee.sop.gatewaycommon.zuul.loadbalancer;
|
||||||
|
|
||||||
|
import com.google.common.base.Optional;
|
||||||
|
import com.netflix.loadbalancer.ILoadBalancer;
|
||||||
|
import com.netflix.loadbalancer.Server;
|
||||||
|
import com.netflix.loadbalancer.ZoneAvoidanceRule;
|
||||||
|
import com.netflix.zuul.context.RequestContext;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.util.CollectionUtils;
|
||||||
|
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 服务实例选择器
|
||||||
|
*
|
||||||
|
* @author tanghc
|
||||||
|
*/
|
||||||
|
@Slf4j
|
||||||
|
public abstract class BaseServerChooser extends ZoneAvoidanceRule {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否匹配对应的服务器,可在此判断是否是预发布,灰度环境
|
||||||
|
*
|
||||||
|
* @param server 指定服务器
|
||||||
|
* @return 返回true:是
|
||||||
|
*/
|
||||||
|
protected abstract boolean match(Server server);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 客户端能否够访问服务器
|
||||||
|
*
|
||||||
|
* @param server 服务器实例
|
||||||
|
* @param request request
|
||||||
|
* @return 返回true:能访问
|
||||||
|
*/
|
||||||
|
protected abstract boolean canVisit(Server server, HttpServletRequest request);
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Server choose(Object key) {
|
||||||
|
ILoadBalancer lb = getLoadBalancer();
|
||||||
|
// 获取服务实例列表
|
||||||
|
List<Server> allServers = new ArrayList<>(lb.getAllServers());
|
||||||
|
int index = -1;
|
||||||
|
for (int i = 0; i < allServers.size(); i++) {
|
||||||
|
Server server = allServers.get(i);
|
||||||
|
if (match(server)) {
|
||||||
|
index = i;
|
||||||
|
if (canVisit(server, RequestContext.getCurrentContext().getRequest())) {
|
||||||
|
return server;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 调用默认的算法
|
||||||
|
// 如果选出了特殊环境服务器,需要移除命中的服务器
|
||||||
|
if (index > -1) {
|
||||||
|
allServers.remove(index);
|
||||||
|
}
|
||||||
|
if (CollectionUtils.isEmpty(allServers)) {
|
||||||
|
log.error("无可用服务实例,key:", key);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
Optional<Server> server = getPredicate().chooseRoundRobinAfterFiltering(allServers, key);
|
||||||
|
if (server.isPresent()) {
|
||||||
|
return server.get();
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -76,7 +76,7 @@ public class ZuulResultExecutor extends BaseExecutorAdapter<RequestContext, Stri
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (error == null) {
|
if (error == null) {
|
||||||
error = ErrorEnum.AOP_UNKNOW_ERROR.getErrorMeta().getError();
|
error = ErrorEnum.ISP_UNKNOWN_ERROR.getErrorMeta().getError();
|
||||||
}
|
}
|
||||||
return error;
|
return error;
|
||||||
}
|
}
|
||||||
|
@@ -6,6 +6,45 @@ import org.springframework.util.CollectionUtils;
|
|||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* {
|
||||||
|
* "instance": {
|
||||||
|
* "instanceId": "demo-order2:11101",
|
||||||
|
* "app": "demo-order2",
|
||||||
|
* "appGroutName": null,
|
||||||
|
* "ipAddr": "127.0.0.1",
|
||||||
|
* "sid": "na",
|
||||||
|
* "homePageUrl": null,
|
||||||
|
* "statusPageUrl": null,
|
||||||
|
* "healthCheckUrl": null,
|
||||||
|
* "secureHealthCheckUrl": null,
|
||||||
|
* "vipAddress": "demo-order2",
|
||||||
|
* "secureVipAddress": "demo-order2",
|
||||||
|
* "countryId": 1,
|
||||||
|
* "dataCenterInfo": {
|
||||||
|
* "@class": "com.netflix.appinfo.InstanceInfo$DefaultDataCenterInfo",
|
||||||
|
* "name": "MyOwn"
|
||||||
|
* },
|
||||||
|
* "hostName": "127.0.0.1",
|
||||||
|
* "status": "UP",
|
||||||
|
* "leaseInfo": null,
|
||||||
|
* "isCoordinatingDiscoveryServer": false,
|
||||||
|
* "lastUpdatedTimestamp": 1529391461000,
|
||||||
|
* "lastDirtyTimestamp": 1529391461000,
|
||||||
|
* "actionType": null,
|
||||||
|
* "asgName": null,
|
||||||
|
* "overridden_status": "UNKNOWN",
|
||||||
|
* "port": {
|
||||||
|
* "$": 11102,
|
||||||
|
* "@enabled": "false"
|
||||||
|
* },
|
||||||
|
* "securePort": {
|
||||||
|
* "$": 7002,
|
||||||
|
* "@enabled": "false"
|
||||||
|
* },
|
||||||
|
* "metadata": {
|
||||||
|
* "@class": "java.util.Collections$EmptyMap"
|
||||||
|
* }* }
|
||||||
|
* }
|
||||||
* @author tanghc
|
* @author tanghc
|
||||||
*/
|
*/
|
||||||
@Data
|
@Data
|
||||||
@@ -19,6 +58,8 @@ public class EurekaInstance {
|
|||||||
private String healthCheckUrl;
|
private String healthCheckUrl;
|
||||||
private String lastUpdatedTimestamp;
|
private String lastUpdatedTimestamp;
|
||||||
|
|
||||||
|
private Map<String, String> metadata;
|
||||||
|
|
||||||
public String fetchPort() {
|
public String fetchPort() {
|
||||||
if (CollectionUtils.isEmpty(port)) {
|
if (CollectionUtils.isEmpty(port)) {
|
||||||
return "";
|
return "";
|
||||||
|
@@ -16,6 +16,10 @@ public enum EurekaUri {
|
|||||||
* 查询所有实例 Query for all instances
|
* 查询所有实例 Query for all instances
|
||||||
*/
|
*/
|
||||||
QUERY_APPS("GET", "/apps"),
|
QUERY_APPS("GET", "/apps"),
|
||||||
|
/**
|
||||||
|
* 查询一个实例
|
||||||
|
*/
|
||||||
|
QUERY_INSTANCES("GET", "/instances/%s"),
|
||||||
/**
|
/**
|
||||||
* 下线 Take instance out of service
|
* 下线 Take instance out of service
|
||||||
*/
|
*/
|
||||||
@@ -24,6 +28,12 @@ public enum EurekaUri {
|
|||||||
* 上线 Move instance back into service (remove override)
|
* 上线 Move instance back into service (remove override)
|
||||||
*/
|
*/
|
||||||
ONLINE_SERVICE("DELETE", "/apps/%s/%s/status?value=UP"),
|
ONLINE_SERVICE("DELETE", "/apps/%s/%s/status?value=UP"),
|
||||||
|
/**
|
||||||
|
* 设置metadata信息
|
||||||
|
*
|
||||||
|
* /apps/{appID}/{instanceID}/metadata?key=value
|
||||||
|
*/
|
||||||
|
SET_METADATA("PUT", "/apps/%s/%s/metadata?%s=%s")
|
||||||
;
|
;
|
||||||
public static final String URL_PREFIX = "/";
|
public static final String URL_PREFIX = "/";
|
||||||
|
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
package com.gitee.sop.websiteserver.bean;
|
package com.gitee.sop.registryapi.bean;
|
||||||
|
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
@@ -110,7 +110,7 @@ public class HttpTool {
|
|||||||
* @return
|
* @return
|
||||||
* @throws IOException
|
* @throws IOException
|
||||||
*/
|
*/
|
||||||
public String request(String url, Map<String, String> form, Map<String, String> header, HTTPMethod method) throws IOException {
|
public String request(String url, Map<String, ?> form, Map<String, String> header, HTTPMethod method) throws IOException {
|
||||||
Request.Builder requestBuilder = buildRequestBuilder(url, form, method.value());
|
Request.Builder requestBuilder = buildRequestBuilder(url, form, method.value());
|
||||||
// 添加header
|
// 添加header
|
||||||
addHeader(requestBuilder, header);
|
addHeader(requestBuilder, header);
|
||||||
@@ -153,7 +153,7 @@ public class HttpTool {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Request.Builder buildRequestBuilder(String url, Map<String, String> form, String method) {
|
public static Request.Builder buildRequestBuilder(String url, Map<String, ?> form, String method) {
|
||||||
switch (method) {
|
switch (method) {
|
||||||
case "get":
|
case "get":
|
||||||
return new Request.Builder()
|
return new Request.Builder()
|
||||||
@@ -178,18 +178,18 @@ public class HttpTool {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static HttpUrl buildHttpUrl(String url, Map<String, String> form) {
|
public static HttpUrl buildHttpUrl(String url, Map<String, ?> form) {
|
||||||
HttpUrl.Builder urlBuilder = HttpUrl.parse(url).newBuilder();
|
HttpUrl.Builder urlBuilder = HttpUrl.parse(url).newBuilder();
|
||||||
for (Map.Entry<String, String> entry : form.entrySet()) {
|
for (Map.Entry<String, ?> entry : form.entrySet()) {
|
||||||
urlBuilder.addQueryParameter(entry.getKey(), entry.getValue());
|
urlBuilder.addQueryParameter(entry.getKey(), String.valueOf(entry.getValue()));
|
||||||
}
|
}
|
||||||
return urlBuilder.build();
|
return urlBuilder.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static FormBody buildFormBody(Map<String, String> form) {
|
public static FormBody buildFormBody(Map<String, ?> form) {
|
||||||
FormBody.Builder paramBuilder = new FormBody.Builder(StandardCharsets.UTF_8);
|
FormBody.Builder paramBuilder = new FormBody.Builder(StandardCharsets.UTF_8);
|
||||||
for (Map.Entry<String, String> entry : form.entrySet()) {
|
for (Map.Entry<String, ?> entry : form.entrySet()) {
|
||||||
paramBuilder.add(entry.getKey(), entry.getValue());
|
paramBuilder.add(entry.getKey(), String.valueOf(entry.getValue()));
|
||||||
}
|
}
|
||||||
return paramBuilder.build();
|
return paramBuilder.build();
|
||||||
}
|
}
|
||||||
@@ -262,6 +262,9 @@ public class HttpTool {
|
|||||||
HEAD,
|
HEAD,
|
||||||
DELETE;
|
DELETE;
|
||||||
|
|
||||||
|
private HTTPMethod() {
|
||||||
|
}
|
||||||
|
|
||||||
public String value() {
|
public String value() {
|
||||||
return this.name();
|
return this.name();
|
||||||
}
|
}
|
@@ -2,6 +2,9 @@ package com.gitee.sop.registryapi.bean;
|
|||||||
|
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author tanghc
|
* @author tanghc
|
||||||
*/
|
*/
|
||||||
@@ -37,4 +40,9 @@ public class ServiceInstance {
|
|||||||
*/
|
*/
|
||||||
private String updateTime;
|
private String updateTime;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* user extended attributes
|
||||||
|
*/
|
||||||
|
private Map<String, String> metadata = new HashMap<String, String>();
|
||||||
|
|
||||||
}
|
}
|
@@ -38,4 +38,13 @@ public interface RegistryService {
|
|||||||
void offlineInstance(ServiceInstance serviceInstance) throws Exception;
|
void offlineInstance(ServiceInstance serviceInstance) throws Exception;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置实例元数据
|
||||||
|
*
|
||||||
|
* @param serviceInstance 实例
|
||||||
|
* @param key key
|
||||||
|
* @param value 值
|
||||||
|
* @throws Exception 设置实例元数据失败抛出异常
|
||||||
|
*/
|
||||||
|
void setMetadata(ServiceInstance serviceInstance, String key, String value) throws Exception;
|
||||||
}
|
}
|
||||||
|
@@ -60,6 +60,7 @@ public class RegistryServiceEureka implements RegistryService {
|
|||||||
serviceInstance.setStatus(eurekaInstance.getStatus());
|
serviceInstance.setStatus(eurekaInstance.getStatus());
|
||||||
Date updateTime = new Date(Long.valueOf(eurekaInstance.getLastUpdatedTimestamp()));
|
Date updateTime = new Date(Long.valueOf(eurekaInstance.getLastUpdatedTimestamp()));
|
||||||
serviceInstance.setUpdateTime(DateFormatUtils.format(updateTime, TIMESTAMP_PATTERN));
|
serviceInstance.setUpdateTime(DateFormatUtils.format(updateTime, TIMESTAMP_PATTERN));
|
||||||
|
serviceInstance.setMetadata(eurekaInstance.getMetadata());
|
||||||
serviceInfo.getInstances().add(serviceInstance);
|
serviceInfo.getInstances().add(serviceInstance);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -80,6 +81,11 @@ public class RegistryServiceEureka implements RegistryService {
|
|||||||
this.requestEurekaServer(EurekaUri.OFFLINE_SERVICE, serviceInstance.getServiceId(), serviceInstance.getInstanceId());
|
this.requestEurekaServer(EurekaUri.OFFLINE_SERVICE, serviceInstance.getServiceId(), serviceInstance.getInstanceId());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setMetadata(ServiceInstance serviceInstance, String key, String value) throws Exception {
|
||||||
|
this.requestEurekaServer(EurekaUri.SET_METADATA, serviceInstance.getServiceId(), serviceInstance.getInstanceId(), key, value);
|
||||||
|
}
|
||||||
|
|
||||||
private String requestEurekaServer(EurekaUri eurekaUri, String... args) throws IOException {
|
private String requestEurekaServer(EurekaUri eurekaUri, String... args) throws IOException {
|
||||||
Request request = eurekaUri.getRequest(this.eurekaUrl, args);
|
Request request = eurekaUri.getRequest(this.eurekaUrl, args);
|
||||||
Response response = client.newCall(request).execute();
|
Response response = client.newCall(request).execute();
|
||||||
|
@@ -1,17 +1,17 @@
|
|||||||
package com.gitee.sop.registryapi.service.impl;
|
package com.gitee.sop.registryapi.service.impl;
|
||||||
|
|
||||||
|
import com.alibaba.fastjson.JSON;
|
||||||
|
import com.alibaba.fastjson.JSONObject;
|
||||||
import com.alibaba.nacos.api.exception.NacosException;
|
import com.alibaba.nacos.api.exception.NacosException;
|
||||||
import com.alibaba.nacos.api.naming.NamingFactory;
|
import com.alibaba.nacos.api.naming.NamingFactory;
|
||||||
import com.alibaba.nacos.api.naming.NamingService;
|
import com.alibaba.nacos.api.naming.NamingService;
|
||||||
import com.alibaba.nacos.api.naming.pojo.Instance;
|
import com.alibaba.nacos.api.naming.pojo.Instance;
|
||||||
import com.alibaba.nacos.api.naming.pojo.ListView;
|
import com.alibaba.nacos.api.naming.pojo.ListView;
|
||||||
|
import com.gitee.sop.registryapi.bean.HttpTool;
|
||||||
import com.gitee.sop.registryapi.bean.ServiceInfo;
|
import com.gitee.sop.registryapi.bean.ServiceInfo;
|
||||||
import com.gitee.sop.registryapi.bean.ServiceInstance;
|
import com.gitee.sop.registryapi.bean.ServiceInstance;
|
||||||
import com.gitee.sop.registryapi.service.RegistryService;
|
import com.gitee.sop.registryapi.service.RegistryService;
|
||||||
import okhttp3.FormBody;
|
|
||||||
import okhttp3.OkHttpClient;
|
import okhttp3.OkHttpClient;
|
||||||
import okhttp3.Request;
|
|
||||||
import okhttp3.Response;
|
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.springframework.beans.factory.annotation.Value;
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
import org.springframework.util.CollectionUtils;
|
import org.springframework.util.CollectionUtils;
|
||||||
@@ -24,13 +24,15 @@ import java.util.List;
|
|||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* nacos接口实现
|
* nacos接口实现, https://nacos.io/zh-cn/docs/open-api.html
|
||||||
* @author tanghc
|
* @author tanghc
|
||||||
*/
|
*/
|
||||||
public class RegistryServiceNacos implements RegistryService {
|
public class RegistryServiceNacos implements RegistryService {
|
||||||
|
|
||||||
OkHttpClient client = new OkHttpClient();
|
OkHttpClient client = new OkHttpClient();
|
||||||
|
|
||||||
|
static HttpTool httpTool = new HttpTool();
|
||||||
|
|
||||||
@Value("${registry.nacos-server-addr:}")
|
@Value("${registry.nacos-server-addr:}")
|
||||||
private String nacosAddr;
|
private String nacosAddr;
|
||||||
|
|
||||||
@@ -63,6 +65,7 @@ public class RegistryServiceNacos implements RegistryService {
|
|||||||
boolean isOnline = instance.getWeight() > 0;
|
boolean isOnline = instance.getWeight() > 0;
|
||||||
String status = isOnline ? "UP" : "OUT_OF_SERVICE";
|
String status = isOnline ? "UP" : "OUT_OF_SERVICE";
|
||||||
serviceInstance.setStatus(status);
|
serviceInstance.setStatus(status);
|
||||||
|
serviceInstance.setMetadata(instance.getMetadata());
|
||||||
serviceInfo.getInstances().add(serviceInstance);
|
serviceInfo.getInstances().add(serviceInstance);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -73,34 +76,50 @@ public class RegistryServiceNacos implements RegistryService {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onlineInstance(ServiceInstance serviceInstance) throws Exception {
|
public void onlineInstance(ServiceInstance serviceInstance) throws Exception {
|
||||||
Map<String, String> params = new HashMap<>(8);
|
// 查询实例
|
||||||
|
Instance instance = this.getInstance(serviceInstance);
|
||||||
// 上线,把权重设置成1
|
// 上线,把权重设置成1
|
||||||
params.put("weight", "1");
|
instance.setWeight(1);
|
||||||
this.updateInstance(serviceInstance, params);
|
this.updateInstance(instance);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void offlineInstance(ServiceInstance serviceInstance) throws Exception {
|
public void offlineInstance(ServiceInstance serviceInstance) throws Exception {
|
||||||
Map<String, String> params = new HashMap<>(8);
|
// 查询实例
|
||||||
|
Instance instance = this.getInstance(serviceInstance);
|
||||||
// 下线,把权重设置成0
|
// 下线,把权重设置成0
|
||||||
params.put("weight", "0");
|
instance.setWeight(0);
|
||||||
this.updateInstance(serviceInstance, params);
|
this.updateInstance(instance);
|
||||||
}
|
}
|
||||||
|
|
||||||
private Response updateInstance(ServiceInstance serviceInstance, Map<String, String> params) throws IOException {
|
@Override
|
||||||
FormBody.Builder builder = new FormBody.Builder();
|
public void setMetadata(ServiceInstance serviceInstance, String key, String value) throws Exception {
|
||||||
for (Map.Entry<String, String> entry : params.entrySet()) {
|
// 查询实例
|
||||||
builder.add(entry.getKey(), String.valueOf(entry.getValue()));
|
Instance instance = this.getInstance(serviceInstance);
|
||||||
|
// 设置metadata
|
||||||
|
Map<String, String> metadata = instance.getMetadata();
|
||||||
|
metadata.put(key, value);
|
||||||
|
this.updateInstance(instance);
|
||||||
}
|
}
|
||||||
builder.add("serviceName", serviceInstance.getServiceId())
|
|
||||||
.add("ip", serviceInstance.getIp())
|
|
||||||
.add("port", String.valueOf(serviceInstance.getPort()));
|
|
||||||
FormBody formBody = builder.build();
|
|
||||||
final Request request = new Request.Builder()
|
|
||||||
.url("http://" + nacosAddr + "/nacos/v1/ns/instance")
|
|
||||||
.put(formBody)
|
|
||||||
.build();
|
|
||||||
|
|
||||||
return client.newCall(request).execute();
|
protected void updateInstance(Instance instance) throws IOException {
|
||||||
|
String json = JSON.toJSONString(instance);
|
||||||
|
JSONObject jsonObject = JSON.parseObject(json);
|
||||||
|
httpTool.request("http://" + nacosAddr + "/nacos/v1/ns/instance", jsonObject, null, HttpTool.HTTPMethod.PUT);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询实例
|
||||||
|
* @param serviceInstance 实例信息
|
||||||
|
* @return 返回nacos实例
|
||||||
|
* @throws IOException 查询异常
|
||||||
|
*/
|
||||||
|
protected Instance getInstance(ServiceInstance serviceInstance) throws IOException {
|
||||||
|
Map<String, String> params = new HashMap<>(8);
|
||||||
|
params.put("serviceName", serviceInstance.getServiceId());
|
||||||
|
params.put("ip", serviceInstance.getIp());
|
||||||
|
params.put("port", String.valueOf(serviceInstance.getPort()));
|
||||||
|
String instanceJson = httpTool.request("http://" + nacosAddr + "/nacos/v1/ns/instance", params, null, HttpTool.HTTPMethod.GET);
|
||||||
|
return JSON.parseObject(instanceJson, Instance.class);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -5,7 +5,7 @@ package com.gitee.sop.servercommon.message;
|
|||||||
*/
|
*/
|
||||||
public enum ServiceErrorEnum {
|
public enum ServiceErrorEnum {
|
||||||
/** 系统繁忙 */
|
/** 系统繁忙 */
|
||||||
ISP_UNKNOW_ERROR("isp.unknow-error"),
|
ISP_UNKNOW_ERROR("isp.unknown-error"),
|
||||||
/** 参数错误 */
|
/** 参数错误 */
|
||||||
ISV_PARAM_ERROR("isv.invalid-parameter"),
|
ISV_PARAM_ERROR("isv.invalid-parameter"),
|
||||||
/** 通用错误 */
|
/** 通用错误 */
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
# 错误配置
|
# 错误配置
|
||||||
|
|
||||||
isp.error_isp.unknow-error=Service is temporarily unavailable
|
isp.error_isp.unknown-error=Service is temporarily unavailable
|
||||||
isp.error_isv.invalid-parameter=Invalid parameter
|
isp.error_isv.invalid-parameter=Invalid parameter
|
@@ -1,5 +1,5 @@
|
|||||||
# 错误配置
|
# 错误配置
|
||||||
|
|
||||||
isp.error_isp.unknow-error=\u670d\u52a1\u6682\u4e0d\u53ef\u7528
|
isp.error_isp.unknown-error=\u670d\u52a1\u6682\u4e0d\u53ef\u7528
|
||||||
# 参数无效
|
# 参数无效
|
||||||
isp.error_isv.invalid-parameter=\u53c2\u6570\u65e0\u6548
|
isp.error_isv.invalid-parameter=\u53c2\u6570\u65e0\u6548
|
||||||
|
@@ -6,8 +6,11 @@ package com.gitee.sop.gateway.config;
|
|||||||
* 注意:下面两个只能使用一个
|
* 注意:下面两个只能使用一个
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import com.gitee.sop.gateway.loadbalancer.SopPropertiesFactory;
|
||||||
import com.gitee.sop.gateway.manager.ManagerInitializer;
|
import com.gitee.sop.gateway.manager.ManagerInitializer;
|
||||||
import com.gitee.sop.gatewaycommon.zuul.configuration.AlipayZuulConfiguration;
|
import com.gitee.sop.gatewaycommon.zuul.configuration.AlipayZuulConfiguration;
|
||||||
|
import org.springframework.cloud.netflix.ribbon.PropertiesFactory;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -21,6 +24,11 @@ public class ZuulConfig extends AlipayZuulConfiguration {
|
|||||||
new ManagerInitializer();
|
new ManagerInitializer();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
PropertiesFactory propertiesFactory() {
|
||||||
|
return new SopPropertiesFactory();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@@ -0,0 +1,55 @@
|
|||||||
|
package com.gitee.sop.gateway.loadbalancer;
|
||||||
|
|
||||||
|
import com.gitee.sop.gatewaycommon.zuul.loadbalancer.BaseServerChooser;
|
||||||
|
import com.netflix.loadbalancer.Server;
|
||||||
|
import com.netflix.niws.loadbalancer.DiscoveryEnabledServer;
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 预发布环境选择,参考自:https://segmentfault.com/a/1190000017412946
|
||||||
|
*
|
||||||
|
* @author tanghc
|
||||||
|
*/
|
||||||
|
public class PreEnvironmentServerChooser extends BaseServerChooser {
|
||||||
|
|
||||||
|
private static final String MEDATA_KEY_ENV = "env";
|
||||||
|
private static final String ENV_PRE_VALUE = "pre";
|
||||||
|
|
||||||
|
private static final String HOST_NAME = "localhost";
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean match(Server server) {
|
||||||
|
// eureka存储的metadata
|
||||||
|
Map<String, String> metadata = ((DiscoveryEnabledServer) server).getInstanceInfo().getMetadata();
|
||||||
|
String env = metadata.get(MEDATA_KEY_ENV);
|
||||||
|
return StringUtils.isNotBlank(env);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 这里判断客户端能否访问,可以根据ip地址,域名,header内容来决定是否可以访问预发布环境
|
||||||
|
* @param server 服务器实例
|
||||||
|
* @param request request
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
protected boolean canVisit(Server server, HttpServletRequest request) {
|
||||||
|
// eureka存储的metadata
|
||||||
|
Map<String, String> metadata = ((DiscoveryEnabledServer) server).getInstanceInfo().getMetadata();
|
||||||
|
String env = metadata.get(MEDATA_KEY_ENV);
|
||||||
|
return Objects.equals(ENV_PRE_VALUE, env) && canClientVisit(request);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 通过判断hostname来确定是否是预发布请求,如果需要通过其它条件判断,修改此方法
|
||||||
|
* @param request request
|
||||||
|
* @return 返回true:可以进入到预发环境
|
||||||
|
*/
|
||||||
|
protected boolean canClientVisit(HttpServletRequest request) {
|
||||||
|
String serverName = request.getServerName();
|
||||||
|
return HOST_NAME.equals(serverName);
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,39 @@
|
|||||||
|
package com.gitee.sop.gateway.loadbalancer;
|
||||||
|
|
||||||
|
import com.netflix.loadbalancer.IRule;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.cloud.netflix.ribbon.PropertiesFactory;
|
||||||
|
import org.springframework.core.env.Environment;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 自定义PropertiesFactory,用来动态添加LoadBalance规则
|
||||||
|
* @author tanghc
|
||||||
|
*/
|
||||||
|
public class SopPropertiesFactory extends PropertiesFactory {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 可在配置文件中设置<code>zuul.custom-rule-classname=com.xx.ClassName</code>指定负载均衡规则类
|
||||||
|
* 默认使用com.gitee.sop.gateway.loadbalancer.PreEnvironmentServerChooser
|
||||||
|
*/
|
||||||
|
private static final String PROPERTIES_KEY = "zuul.custom-rule-classname";
|
||||||
|
|
||||||
|
private static final String CUSTOM_RULE_CLASSNAME = PreEnvironmentServerChooser.class.getName();
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private Environment environment;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 配置文件配置:<serviceId>.ribbon.NFLoadBalancerRuleClassName=com.gitee.sop.gateway.loadbalancer.PreEnvironmentServerChooser
|
||||||
|
* @param clazz
|
||||||
|
* @param name serviceId
|
||||||
|
* @return 返回class全限定名
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public String getClassName(Class clazz, String name) {
|
||||||
|
if (clazz == IRule.class) {
|
||||||
|
return this.environment.getProperty(PROPERTIES_KEY, CUSTOM_RULE_CLASSNAME);
|
||||||
|
} else {
|
||||||
|
return super.getClassName(clazz, name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -6,7 +6,7 @@ package com.gitee.sop.sdk.common;
|
|||||||
* "result": {
|
* "result": {
|
||||||
* "code": "20000",
|
* "code": "20000",
|
||||||
* "msg": "Service Currently Unavailable",
|
* "msg": "Service Currently Unavailable",
|
||||||
* "sub_code": "isp.unknow-error",
|
* "sub_code": "isp.unknown-error",
|
||||||
* "sub_msg": "系统繁忙"
|
* "sub_msg": "系统繁忙"
|
||||||
* },
|
* },
|
||||||
* "sign": "ERITJKEIJKJHKKKKKKKHJEREEEEEEEEEEE"
|
* "sign": "ERITJKEIJKJHKKKKKKKHJEREEEEEEEEEEE"
|
||||||
|
@@ -8,7 +8,7 @@ package com.gitee.sop.sdk.common;
|
|||||||
* "alipay_trade_order_settle_response": {
|
* "alipay_trade_order_settle_response": {
|
||||||
* "code": "20000",
|
* "code": "20000",
|
||||||
* "msg": "Service Currently Unavailable",
|
* "msg": "Service Currently Unavailable",
|
||||||
* "sub_code": "isp.unknow-error",
|
* "sub_code": "isp.unknown-error",
|
||||||
* "sub_msg": "系统繁忙"
|
* "sub_msg": "系统繁忙"
|
||||||
* },
|
* },
|
||||||
* "sign": "ERITJKEIJKJHKKKKKKKHJEREEEEEEEEEEE"
|
* "sign": "ERITJKEIJKJHKKKKKKKHJEREEEEEEEEEEE"
|
||||||
|
@@ -11,7 +11,7 @@ import lombok.Setter;
|
|||||||
* "alipay_trade_close_response": {
|
* "alipay_trade_close_response": {
|
||||||
* "code": "20000",
|
* "code": "20000",
|
||||||
* "msg": "Service Currently Unavailable",
|
* "msg": "Service Currently Unavailable",
|
||||||
* "sub_code": "isp.unknow-error",
|
* "sub_code": "isp.unknown-error",
|
||||||
* "sub_msg": "系统繁忙"
|
* "sub_msg": "系统繁忙"
|
||||||
* },
|
* },
|
||||||
* "sign": "ERITJKEIJKJHKKKKKKKHJEREEEEEEEEEEE"
|
* "sign": "ERITJKEIJKJHKKKKKKKHJEREEEEEEEEEEE"
|
||||||
|
@@ -110,7 +110,7 @@ public class HttpTool {
|
|||||||
* @return
|
* @return
|
||||||
* @throws IOException
|
* @throws IOException
|
||||||
*/
|
*/
|
||||||
public String request(String url, Map<String, String> form, Map<String, String> header, HTTPMethod method) throws IOException {
|
public String request(String url, Map<String, ?> form, Map<String, String> header, HTTPMethod method) throws IOException {
|
||||||
Request.Builder requestBuilder = buildRequestBuilder(url, form, method.value());
|
Request.Builder requestBuilder = buildRequestBuilder(url, form, method.value());
|
||||||
// 添加header
|
// 添加header
|
||||||
addHeader(requestBuilder, header);
|
addHeader(requestBuilder, header);
|
||||||
@@ -153,7 +153,7 @@ public class HttpTool {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Request.Builder buildRequestBuilder(String url, Map<String, String> form, String method) {
|
public static Request.Builder buildRequestBuilder(String url, Map<String, ?> form, String method) {
|
||||||
switch (method) {
|
switch (method) {
|
||||||
case "get":
|
case "get":
|
||||||
return new Request.Builder()
|
return new Request.Builder()
|
||||||
@@ -178,18 +178,18 @@ public class HttpTool {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static HttpUrl buildHttpUrl(String url, Map<String, String> form) {
|
public static HttpUrl buildHttpUrl(String url, Map<String, ?> form) {
|
||||||
HttpUrl.Builder urlBuilder = HttpUrl.parse(url).newBuilder();
|
HttpUrl.Builder urlBuilder = HttpUrl.parse(url).newBuilder();
|
||||||
for (Map.Entry<String, String> entry : form.entrySet()) {
|
for (Map.Entry<String, ?> entry : form.entrySet()) {
|
||||||
urlBuilder.addQueryParameter(entry.getKey(), entry.getValue());
|
urlBuilder.addQueryParameter(entry.getKey(), String.valueOf(entry.getValue()));
|
||||||
}
|
}
|
||||||
return urlBuilder.build();
|
return urlBuilder.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static FormBody buildFormBody(Map<String, String> form) {
|
public static FormBody buildFormBody(Map<String, ?> form) {
|
||||||
FormBody.Builder paramBuilder = new FormBody.Builder(StandardCharsets.UTF_8);
|
FormBody.Builder paramBuilder = new FormBody.Builder(StandardCharsets.UTF_8);
|
||||||
for (Map.Entry<String, String> entry : form.entrySet()) {
|
for (Map.Entry<String, ?> entry : form.entrySet()) {
|
||||||
paramBuilder.add(entry.getKey(), entry.getValue());
|
paramBuilder.add(entry.getKey(), String.valueOf(entry.getValue()));
|
||||||
}
|
}
|
||||||
return paramBuilder.build();
|
return paramBuilder.build();
|
||||||
}
|
}
|
||||||
|
@@ -141,7 +141,7 @@
|
|||||||
<div>服务不可用</div>
|
<div>服务不可用</div>
|
||||||
</td>
|
</td>
|
||||||
<td rowspan="1" colspan="1">
|
<td rowspan="1" colspan="1">
|
||||||
<div>isp.unknow-error</div>
|
<div>isp.unknown-error</div>
|
||||||
</td>
|
</td>
|
||||||
<td rowspan="1" colspan="1">
|
<td rowspan="1" colspan="1">
|
||||||
<div>服务暂不可用(业务系统不可用)</div>
|
<div>服务暂不可用(业务系统不可用)</div>
|
||||||
|
@@ -93,7 +93,7 @@ function createResponseCode(docItem) {
|
|||||||
' "'+method+'_response": {\n' +
|
' "'+method+'_response": {\n' +
|
||||||
' "code": "20000",\n' +
|
' "code": "20000",\n' +
|
||||||
' "msg": "Service is temporarily unavailable",\n' +
|
' "msg": "Service is temporarily unavailable",\n' +
|
||||||
' "sub_code": "isp.unknow-error",\n' +
|
' "sub_code": "isp.unknown-error",\n' +
|
||||||
' "sub_msg": "服务暂不可用"\n' +
|
' "sub_msg": "服务暂不可用"\n' +
|
||||||
' }' +
|
' }' +
|
||||||
'}';
|
'}';
|
||||||
|
@@ -1,7 +1,7 @@
|
|||||||
package com.gitee.sop.websiteserver.controller;
|
package com.gitee.sop.websiteserver.controller;
|
||||||
|
|
||||||
import com.gitee.sop.websiteserver.bean.HttpTool;
|
import com.gitee.sop.registryapi.bean.HttpTool;
|
||||||
import com.gitee.sop.websiteserver.bean.HttpTool.*;
|
import com.gitee.sop.registryapi.bean.HttpTool.*;
|
||||||
import com.gitee.sop.websiteserver.sign.AlipayApiException;
|
import com.gitee.sop.websiteserver.sign.AlipayApiException;
|
||||||
import com.gitee.sop.websiteserver.sign.AlipaySignature;
|
import com.gitee.sop.websiteserver.sign.AlipaySignature;
|
||||||
import com.gitee.sop.websiteserver.util.UploadUtil;
|
import com.gitee.sop.websiteserver.util.UploadUtil;
|
||||||
|
Reference in New Issue
Block a user