diff --git a/changelog.md b/changelog.md
index 64fde0d5..a0a8dc4b 100644
--- a/changelog.md
+++ b/changelog.md
@@ -1,5 +1,12 @@
# changelog
+## 4.4.0
+
+**【重要】:升级前请阅读 [升级到4.4.0注意事项](./升级到4.4.0注意事项.md)**
+
+- 优化异常处理
+- 优化网关多实例数据库重复保存问题
+
## 4.3.4
- 修复Request参数在第一位导致绑定失败问题
diff --git a/doc/docs/files/10011_项目接入到SOP.md b/doc/docs/files/10011_项目接入到SOP.md
index e1138abc..7f4e5e18 100644
--- a/doc/docs/files/10011_项目接入到SOP.md
+++ b/doc/docs/files/10011_项目接入到SOP.md
@@ -79,6 +79,56 @@ public class OpenServiceConfig extends AlipayServiceConfiguration {
}
```
+- 全局异常处理
+
+在微服务项目的全局异常处理中添加一句:`ExceptionHolder.hold(request, response, exception);`
+
+```java
+ @ExceptionHandler(Exception.class)
+ @ResponseBody
+ public Object exceptionHandler(HttpServletRequest request, HttpServletResponse response, Exception exception) {
+ ...
+ // 在返回前加这一句
+ ExceptionHolder.hold(request, response, exception);
+ ...
+ return ..;
+ }
+```
+
+如果没有配置全局异常,可参考下面配置
+
+```java
+@ControllerAdvice
+@Slf4j
+public class StoryGlobalExceptionHandler {
+
+
+ /**
+ * 捕获手动抛出的异常
+ *
+ * @param request request
+ * @param response response
+ * @param exception 异常信息
+ * @return 返回提示信息
+ */
+ @ExceptionHandler(Exception.class)
+ @ResponseBody
+ public Object exceptionHandler(HttpServletRequest request, HttpServletResponse response, Exception exception) {
+ // 在返回前加这一句
+ ExceptionHolder.hold(request, response, exception);
+ // 下面可以实现自己的全局异常处理
+ return new ErrorResult(500, exception.getMessage());
+ }
+
+ @Data
+ @AllArgsConstructor
+ public static class ErrorResult {
+ private int code;
+ private String msg;
+ }
+}
+```
+
到此准备工作就完成了,接下来可前往`新增接口`查看如何新增接口。
## 非Java项目接入
diff --git a/doc/pom.xml b/doc/pom.xml
index 0f98db76..e9a5fcc0 100644
--- a/doc/pom.xml
+++ b/doc/pom.xml
@@ -3,7 +3,7 @@
4.0.0
com.gitee.sop
doc
- 4.3.4-SNAPSHOT
+ 4.4.0-SNAPSHOT
diff --git a/pom.xml b/pom.xml
index 63d90efd..807bed5c 100644
--- a/pom.xml
+++ b/pom.xml
@@ -11,7 +11,7 @@
4.0.0
com.gitee.sop
sop-parent
- 4.3.4-SNAPSHOT
+ 4.4.0-SNAPSHOT
pom
一个开放平台解决方案项目,基于Spring Cloud实现,目标是能够让用户快速得搭建起自己的开放平台
diff --git a/sop-admin/pom.xml b/sop-admin/pom.xml
index 6c881c8c..25ec91df 100644
--- a/sop-admin/pom.xml
+++ b/sop-admin/pom.xml
@@ -6,7 +6,7 @@
com.gitee.sop
sop-parent
- 4.3.4-SNAPSHOT
+ 4.4.0-SNAPSHOT
../pom.xml
diff --git a/sop-admin/sop-admin-server/pom.xml b/sop-admin/sop-admin-server/pom.xml
index c34d9993..701a365d 100644
--- a/sop-admin/sop-admin-server/pom.xml
+++ b/sop-admin/sop-admin-server/pom.xml
@@ -5,13 +5,13 @@
com.gitee.sop
sop-parent
- 4.3.4-SNAPSHOT
+ 4.4.0-SNAPSHOT
../../pom.xml
4.0.0
sop-admin-server
- 4.3.4-SNAPSHOT
+ 4.4.0-SNAPSHOT
jar
diff --git a/sop-auth/pom.xml b/sop-auth/pom.xml
index 7faffb8b..b19b560a 100644
--- a/sop-auth/pom.xml
+++ b/sop-auth/pom.xml
@@ -5,7 +5,7 @@
com.gitee.sop
sop-parent
- 4.3.4-SNAPSHOT
+ 4.4.0-SNAPSHOT
../pom.xml
@@ -37,7 +37,7 @@
com.gitee.sop
sdk-java
- 4.3.4-SNAPSHOT
+ 4.4.0-SNAPSHOT
diff --git a/sop-common/pom.xml b/sop-common/pom.xml
index e18fcf21..378e02b5 100644
--- a/sop-common/pom.xml
+++ b/sop-common/pom.xml
@@ -6,7 +6,7 @@
com.gitee.sop
sop-parent
- 4.3.4-SNAPSHOT
+ 4.4.0-SNAPSHOT
../pom.xml
diff --git a/sop-common/sop-bridge-eureka/pom.xml b/sop-common/sop-bridge-eureka/pom.xml
index 908d0640..8b122f5d 100644
--- a/sop-common/sop-bridge-eureka/pom.xml
+++ b/sop-common/sop-bridge-eureka/pom.xml
@@ -5,7 +5,7 @@
com.gitee.sop
sop-common
- 4.3.4-SNAPSHOT
+ 4.4.0-SNAPSHOT
../pom.xml
4.0.0
diff --git a/sop-common/sop-bridge-nacos/pom.xml b/sop-common/sop-bridge-nacos/pom.xml
index ca0ba208..e394afa0 100644
--- a/sop-common/sop-bridge-nacos/pom.xml
+++ b/sop-common/sop-bridge-nacos/pom.xml
@@ -5,7 +5,7 @@
com.gitee.sop
sop-common
- 4.3.4-SNAPSHOT
+ 4.4.0-SNAPSHOT
../pom.xml
4.0.0
diff --git a/sop-common/sop-gateway-common/pom.xml b/sop-common/sop-gateway-common/pom.xml
index d297e88d..49acb240 100644
--- a/sop-common/sop-gateway-common/pom.xml
+++ b/sop-common/sop-gateway-common/pom.xml
@@ -5,7 +5,7 @@
com.gitee.sop
sop-common
- 4.3.4-SNAPSHOT
+ 4.4.0-SNAPSHOT
../pom.xml
@@ -100,6 +100,10 @@
javax.servlet-api
provided
+
+ org.springframework
+ spring-jdbc
+
diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/bean/ServiceRouteInfo.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/bean/ServiceRouteInfo.java
index aa851b68..d43e6a59 100644
--- a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/bean/ServiceRouteInfo.java
+++ b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/bean/ServiceRouteInfo.java
@@ -24,6 +24,8 @@ public class ServiceRouteInfo {
private List routeDefinitionList;
+ private String md5;
+
public String fetchServiceIdLowerCase() {
return serviceId.toLowerCase();
}
diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/bean/SopConstants.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/bean/SopConstants.java
index 7af00f96..470085d2 100644
--- a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/bean/SopConstants.java
+++ b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/bean/SopConstants.java
@@ -40,7 +40,10 @@ public class SopConstants {
public static final String X_SERVICE_ERROR_MESSAGE = "x-service-error-message";
+ public static final String X_SERVICE_ERROR_RESPONSE = "x-service-error-response";
+
public static final int BIZ_ERROR_STATUS = 4000;
+ public static final int UNKNOWN_ERROR_STATUS = 5050;
public static final String UNKNOWN_SERVICE= "_sop_unknown_service_";
public static final String UNKNOWN_METHOD = "_sop_unknown_method_";
diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/gateway/ServerWebExchangeUtil.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/gateway/ServerWebExchangeUtil.java
index 303650ae..4d0a99c8 100644
--- a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/gateway/ServerWebExchangeUtil.java
+++ b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/gateway/ServerWebExchangeUtil.java
@@ -19,6 +19,7 @@ import org.springframework.http.MediaType;
import org.springframework.http.codec.ServerCodecConfigurer;
import org.springframework.http.converter.FormHttpMessageConverter;
import org.springframework.http.server.reactive.ServerHttpRequest;
+import org.springframework.util.CollectionUtils;
import org.springframework.util.MultiValueMap;
import org.springframework.web.multipart.MultipartHttpServletRequest;
import org.springframework.web.multipart.commons.CommonsMultipartResolver;
@@ -33,6 +34,7 @@ import java.nio.charset.StandardCharsets;
import java.util.Collections;
import java.util.List;
import java.util.Map;
+import java.util.Optional;
import java.util.function.Consumer;
/**
@@ -198,6 +200,29 @@ public class ServerWebExchangeUtil {
.build();
}
+ /**
+ * 返回header值
+ * @param exchange ServerWebExchange
+ * @param name header名
+ * @return 返回值,没有返回null
+ */
+ public static Optional getHeaderValue(ServerWebExchange exchange, String name) {
+ List values = exchange.getResponse().getHeaders().get(name);
+ if (CollectionUtils.isEmpty(values)) {
+ return Optional.empty();
+ }
+ return values.stream().findFirst();
+ }
+
+ /**
+ * 移除header
+ * @param exchange ServerWebExchange
+ * @param name header名
+ */
+ public static void removeHeader(ServerWebExchange exchange, String name) {
+ exchange.getResponse().getHeaders().remove(name);
+ }
+
/**
* 获取一个文件上传request
*
diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/gateway/result/GatewayResultExecutor.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/gateway/result/GatewayResultExecutor.java
index 25f46393..08785bda 100644
--- a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/gateway/result/GatewayResultExecutor.java
+++ b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/gateway/result/GatewayResultExecutor.java
@@ -24,6 +24,7 @@ import org.springframework.web.util.UriUtils;
import java.nio.charset.StandardCharsets;
import java.util.List;
import java.util.Locale;
+import java.util.Optional;
/**
@@ -36,27 +37,24 @@ public class GatewayResultExecutor extends BaseExecutorAdapter errorCodeList = exchange.getResponse().getHeaders().get(SopConstants.X_SERVICE_ERROR_CODE);
- if (!CollectionUtils.isEmpty(errorCodeList)) {
- String errorCode = errorCodeList.get(0);
- responseStatus = Integer.parseInt(errorCode);
+ return ServerWebExchangeUtil.getHeaderValue(exchange, SopConstants.X_SERVICE_ERROR_CODE)
+ .map(Integer::parseInt)
+ .orElse(statusCode.value());
+ }
+
+ @Override
+ public Optional getServiceResultForError(ServerWebExchange exchange, int status) {
+ if (status == HttpStatus.OK.value()) {
+ return Optional.empty();
}
- return responseStatus;
+ return ServerWebExchangeUtil.getHeaderValue(exchange, SopConstants.X_SERVICE_ERROR_RESPONSE);
}
@Override
public String getResponseErrorMessage(ServerWebExchange exchange) {
- String errorMsg = null;
- List errorMessageList = exchange.getResponse().getHeaders().get(SopConstants.X_SERVICE_ERROR_MESSAGE);
- if (!CollectionUtils.isEmpty(errorMessageList)) {
- errorMsg = errorMessageList.get(0);
- }
- if (StringUtils.hasText(errorMsg)) {
- errorMsg = UriUtils.decode(errorMsg, StandardCharsets.UTF_8);
- }
- exchange.getResponse().getHeaders().remove(SopConstants.X_SERVICE_ERROR_MESSAGE);
- return errorMsg;
+ return ServerWebExchangeUtil.getHeaderValue(exchange, SopConstants.X_SERVICE_ERROR_MESSAGE)
+ .map(msg -> UriUtils.decode(msg, StandardCharsets.UTF_8))
+ .orElse(null);
}
@Override
@@ -71,14 +69,15 @@ public class GatewayResultExecutor extends BaseExecutorAdapter callback) {
try {
String serviceId = serviceRouteInfo.getServiceId();
- String newMd5 = buildMd5(serviceRouteInfo.getRouteDefinitionList());
+ String newMd5 = serviceRouteInfo.getMd5();
String oldMd5 = serviceIdMd5Map.get(serviceId);
if (Objects.equals(newMd5, oldMd5)) {
return;
@@ -78,20 +78,6 @@ public class GatewayRouteCache implements RouteLoader {
this.routeRepository.refresh();
}
- /**
- * 构建路由id MD5
- *
- * @param routeDefinitionList 路由列表
- * @return 返回MD5
- */
- private String buildMd5(List routeDefinitionList) {
- List routeIdList = routeDefinitionList.stream()
- .map(JSON::toJSONString)
- .sorted()
- .collect(Collectors.toList());
- String md5Source = org.apache.commons.lang3.StringUtils.join(routeIdList, "");
- return DigestUtils.md5DigestAsHex(md5Source.getBytes(StandardCharsets.UTF_8));
- }
@Override
public void remove(String serviceId) {
diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/manager/InstanceManager.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/manager/InstanceManager.java
new file mode 100644
index 00000000..1998fe6e
--- /dev/null
+++ b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/manager/InstanceManager.java
@@ -0,0 +1,15 @@
+package com.gitee.sop.gatewaycommon.manager;
+
+import com.gitee.sop.gatewaycommon.bean.InstanceDefinition;
+import com.gitee.sop.gatewaycommon.route.RegistryEvent;
+
+import java.util.List;
+
+/**
+ * @author tanghc
+ */
+public interface InstanceManager extends RegistryEvent {
+
+ List listInstance(String serviceId);
+
+}
diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/message/ErrorEnum.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/message/ErrorEnum.java
index 65e91deb..6dc4425f 100644
--- a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/message/ErrorEnum.java
+++ b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/message/ErrorEnum.java
@@ -10,6 +10,8 @@ public enum ErrorEnum {
/** 服务暂不可用 */
ISP_UNKNOWN_ERROR(Codes.CODE_UNKNOWN, "isp.unknown-error"),
+ /** 微服务未知错误 */
+ ISP_SERVICE_UNKNOWN_ERROR(Codes.CODE_UNKNOWN, "isp.service-unknown-error"),
/** 服务不可用,路由被禁用 */
ISP_API_DISABLED(Codes.CODE_UNKNOWN, "isp.service-not-available"),
/** 网关响应超时 */
diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/result/BaseExecutorAdapter.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/result/BaseExecutorAdapter.java
index 76c68aec..fa2de3a7 100644
--- a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/result/BaseExecutorAdapter.java
+++ b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/result/BaseExecutorAdapter.java
@@ -17,13 +17,17 @@ import com.gitee.sop.gatewaycommon.util.RouteInterceptorUtil;
import com.gitee.sop.gatewaycommon.validate.alipay.AlipaySignature;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
+import org.springframework.cloud.client.ServiceInstance;
import org.springframework.http.HttpStatus;
import org.springframework.util.CollectionUtils;
+import org.springframework.web.util.UriUtils;
+import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Locale;
import java.util.Map;
+import java.util.Optional;
/**
* 处理微服务返回结果
@@ -44,6 +48,7 @@ public abstract class BaseExecutorAdapter implements ResultExecutor
static {
HTTP_STATUS_ERROR_ENUM_MAP.put(HttpStatus.OK.value(), ErrorEnum.SUCCESS);
HTTP_STATUS_ERROR_ENUM_MAP.put(SopConstants.BIZ_ERROR_STATUS, ErrorEnum.BIZ_ERROR);
+ HTTP_STATUS_ERROR_ENUM_MAP.put(SopConstants.UNKNOWN_ERROR_STATUS, ErrorEnum.ISP_SERVICE_UNKNOWN_ERROR);
HTTP_STATUS_ERROR_ENUM_MAP.put(HttpStatus.NOT_FOUND.value(), ErrorEnum.ISV_INVALID_METHOD);
}
@@ -64,6 +69,14 @@ public abstract class BaseExecutorAdapter implements ResultExecutor
*/
public abstract String getResponseErrorMessage(T t);
+ /**
+ * 业务返回的错误结果
+ * @param t request
+ * @param status status
+ * @return 业务返回结果
+ */
+ public abstract Optional getServiceResultForError(T t, int status);
+
/**
* 返回Api参数
*
@@ -93,6 +106,10 @@ public abstract class BaseExecutorAdapter implements ResultExecutor
serviceResult = formatResult(serviceResult);
boolean isMergeResult = this.isMergeResult(request);
int responseStatus = this.getResponseStatus(request);
+ Optional serviceResultForError = this.getServiceResultForError(request, responseStatus);
+ if (serviceResultForError.isPresent()) {
+ serviceResult = UriUtils.decode(serviceResultForError.get(), StandardCharsets.UTF_8);
+ }
this.doAfterRoute(serviceResult, responseStatus, request);
String finalResult;
if (isMergeResult) {
@@ -124,6 +141,8 @@ public abstract class BaseExecutorAdapter implements ResultExecutor
if (StringUtils.isEmpty(responseErrorMessage)) {
responseErrorMessage = serviceResult;
}
+ ServiceInstance serviceInstance = defaultRouteInterceptorContext.getServiceInstance();
+ log.error("微服务端报错,instance:{}:{}, errorMsg:{}", serviceInstance.getHost(), serviceInstance.getPort(), responseErrorMessage);
defaultRouteInterceptorContext.setServiceErrorMsg(responseErrorMessage);
}
}
diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/route/ServiceRouteListener.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/route/ServiceRouteListener.java
index c029a1cc..9a574856 100644
--- a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/route/ServiceRouteListener.java
+++ b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/route/ServiceRouteListener.java
@@ -2,17 +2,24 @@ package com.gitee.sop.gatewaycommon.route;
import com.alibaba.fastjson.JSON;
import com.gitee.sop.gatewaycommon.bean.InstanceDefinition;
+import com.gitee.sop.gatewaycommon.bean.RouteDefinition;
+import com.gitee.sop.gatewaycommon.bean.ServiceBeanInitializer;
import com.gitee.sop.gatewaycommon.bean.ServiceRouteInfo;
import com.gitee.sop.gatewaycommon.gateway.route.GatewayRouteCache;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.ApplicationContext;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
+import org.springframework.util.DigestUtils;
+import java.nio.charset.StandardCharsets;
+import java.util.List;
import java.util.Map;
+import java.util.stream.Collectors;
/**
* @author tanghc
@@ -30,6 +37,9 @@ public class ServiceRouteListener extends BaseServiceListener {
@Autowired
private RoutesProcessor routesProcessor;
+ @Autowired
+ private ApplicationContext applicationContext;
+
@Override
public void onRemoveService(String serviceId) {
log.info("服务下线,删除路由配置,serviceId: {}", serviceId);
@@ -39,9 +49,9 @@ public class ServiceRouteListener extends BaseServiceListener {
@Override
public void onAddInstance(InstanceDefinition instance) {
- String serviceName = instance.getServiceId();
+ String serviceId = instance.getServiceId();
String url = getRouteRequestUrl(instance);
- log.info("拉取路由配置,serviceId: {}, url: {}", serviceName, url);
+ log.info("拉取路由配置,serviceId: {}, url: {}", serviceId, url);
ResponseEntity responseEntity = getRestTemplate().getForEntity(url, String.class);
if (responseEntity.getStatusCode() == HttpStatus.OK) {
String body = responseEntity.getBody();
@@ -53,17 +63,41 @@ public class ServiceRouteListener extends BaseServiceListener {
return;
}
gatewayRouteCache.load(serviceRouteInfo, callback -> routesProcessor.saveRoutes(serviceRouteInfo, instance));
+ this.initServiceBeanInitializer(serviceId);
} else {
log.error("拉取路由配置异常,url: {}, status: {}, body: {}", url, responseEntity.getStatusCodeValue(), responseEntity.getBody());
}
}
+ private void initServiceBeanInitializer(String serviceId) {
+ Map serviceBeanInitializerMap = applicationContext.getBeansOfType(ServiceBeanInitializer.class);
+ serviceBeanInitializerMap.values().forEach(serviceBeanInitializer -> serviceBeanInitializer.load(serviceId));
+ }
+
private ServiceRouteInfo parseServiceRouteInfo(String body) {
ServiceRouteInfo serviceRouteInfo = JSON.parseObject(body, ServiceRouteInfo.class);
serviceRouteInfo.setServiceId(serviceRouteInfo.getServiceId().toLowerCase());
+ List routeDefinitionList = serviceRouteInfo.getRouteDefinitionList();
+ String md5 = buildMd5(routeDefinitionList);
+ serviceRouteInfo.setMd5(md5);
return serviceRouteInfo;
}
+ /**
+ * 构建路由id MD5
+ *
+ * @param routeDefinitionList 路由列表
+ * @return 返回MD5
+ */
+ private String buildMd5(List routeDefinitionList) {
+ List routeIdList = routeDefinitionList.stream()
+ .map(JSON::toJSONString)
+ .sorted()
+ .collect(Collectors.toList());
+ String md5Source = org.apache.commons.lang3.StringUtils.join(routeIdList, "");
+ return DigestUtils.md5DigestAsHex(md5Source.getBytes(StandardCharsets.UTF_8));
+ }
+
protected HttpEntity getHttpEntity() {
HttpHeaders headers = new HttpHeaders();
return new HttpEntity<>(headers);
diff --git a/sop-common/sop-gateway-common/src/main/resources/i18n/open/error_en.properties b/sop-common/sop-gateway-common/src/main/resources/i18n/open/error_en.properties
index 7f414951..db6a2c38 100644
--- a/sop-common/sop-gateway-common/src/main/resources/i18n/open/error_en.properties
+++ b/sop-common/sop-gateway-common/src/main/resources/i18n/open/error_en.properties
@@ -6,6 +6,7 @@ open.error_10000=Success
# open.error_\uFF08\u524D\u7F00\uFF0920000\uFF08\u7F51\u5173\u9519\u8BEF\u7801\uFF09_isp.unknow-error\uFF08\u5B50\u9519\u8BEF\u7801\uFF09
open.error_20000=Service is temporarily unavailable
open.error_20000_isp.unknown-error=Service is temporarily unavailable
+open.error_20000_isp.service-unknown-error=Service not available
open.error_20000_aop.unknown-error=Service is temporarily unavailable
open.error_20000_isp.service-not-available=Service is temporarily unavailable
open.error_20000_isp.gateway-response-timeout=Gateway response timeout
diff --git a/sop-common/sop-gateway-common/src/main/resources/i18n/open/error_zh_CN.properties b/sop-common/sop-gateway-common/src/main/resources/i18n/open/error_zh_CN.properties
index 5fa2aef1..4ddfdf79 100644
--- a/sop-common/sop-gateway-common/src/main/resources/i18n/open/error_zh_CN.properties
+++ b/sop-common/sop-gateway-common/src/main/resources/i18n/open/error_zh_CN.properties
@@ -62,6 +62,7 @@ open.error_10000=Success
# open.error_\uFF08\u524D\u7F00\uFF0920000\uFF08\u7F51\u5173\u9519\u8BEF\u7801\uFF09_isp.unknow-error\uFF08\u5B50\u9519\u8BEF\u7801\uFF09
open.error_20000=\u670D\u52A1\u4E0D\u53EF\u7528
open.error_20000_isp.unknown-error=\u670D\u52A1\u6682\u4E0D\u53EF\u7528
+open.error_20000_isp.service-unknown-error=\u670D\u52A1\u4E0D\u53EF\u7528
open.error_20000_aop.unknown-error=\u670D\u52A1\u6682\u4E0D\u53EF\u7528
open.error_20000_isp.service-not-available=\u670D\u52A1\u6682\u4E0D\u53EF\u7528
open.error_20000_isp.gateway-response-timeout=\u7F51\u5173\u54CD\u5E94\u8D85\u65F6
diff --git a/sop-common/sop-service-common/pom.xml b/sop-common/sop-service-common/pom.xml
index 20f0c533..23850b13 100644
--- a/sop-common/sop-service-common/pom.xml
+++ b/sop-common/sop-service-common/pom.xml
@@ -6,13 +6,13 @@
com.gitee.sop
sop-common
- 4.3.4-SNAPSHOT
+ 4.4.0-SNAPSHOT
../pom.xml
4.0.0
sop-service-common
- 4.3.4-SNAPSHOT
+ 4.4.0-SNAPSHOT
jar
diff --git a/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/configuration/GlobalExceptionHandler.java b/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/configuration/GlobalExceptionHandler.java
index a5c3b3fc..c1e54c27 100644
--- a/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/configuration/GlobalExceptionHandler.java
+++ b/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/configuration/GlobalExceptionHandler.java
@@ -14,10 +14,13 @@ import javax.servlet.http.HttpServletResponse;
import java.nio.charset.StandardCharsets;
/**
- * 全局异常处理
+ * 全局异常处理,不在使用,目的是不与原有的全局异常冲突。替代方案见sop-story中的
+ *
+ * com.gitee.sop.storyweb.StoryGlobalExceptionHandler
*
* @author tanghc
*/
+@Deprecated
@ControllerAdvice
@Slf4j
public class GlobalExceptionHandler {
diff --git a/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/configuration/SpringmvcConfiguration.java b/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/configuration/SpringmvcConfiguration.java
index a76fbbc2..e98ce121 100644
--- a/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/configuration/SpringmvcConfiguration.java
+++ b/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/configuration/SpringmvcConfiguration.java
@@ -76,12 +76,6 @@ public class SpringmvcConfiguration implements WebMvcConfigurer, BeanPostProcess
registry.addInterceptor(new ServiceContextInterceptor());
}
- @Bean
- @ConditionalOnMissingBean
- GlobalExceptionHandler globalExceptionHandler() {
- return new GlobalExceptionHandler();
- }
-
@Bean
@ConditionalOnMissingBean
ServiceRouteController serviceRouteInfoHandler() {
diff --git a/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/exception/ExceptionHolder.java b/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/exception/ExceptionHolder.java
new file mode 100644
index 00000000..833e90a6
--- /dev/null
+++ b/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/exception/ExceptionHolder.java
@@ -0,0 +1,86 @@
+package com.gitee.sop.servercommon.exception;
+
+import com.alibaba.fastjson.JSON;
+import com.gitee.sop.servercommon.bean.ServiceConfig;
+import com.gitee.sop.servercommon.result.ServiceResultBuilder;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.web.util.UriUtils;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.nio.charset.StandardCharsets;
+
+/**
+ * @author tanghc
+ */
+@Slf4j
+public class ExceptionHolder {
+ /**
+ * 与网关约定好的状态码,表示业务出错
+ */
+ private static final int BIZ_ERROR_CODE = 4000;
+
+ /**
+ * 与网关约定好的系统错误状态码
+ */
+ private static final int SYSTEM_ERROR_CODE = 5050;
+
+ /**
+ * header中的错误code
+ */
+ private static final String X_SERVICE_ERROR_HEADER_NAME = "x-service-error-code";
+
+ /**
+ * header中的错误信息
+ */
+ private static final String X_SERVICE_ERROR_MESSAGE = "x-service-error-message";
+
+ /**
+ * header中的返回信息
+ */
+ private static final String X_SERVICE_ERROR_RESPONSE = "x-service-error-response";
+
+ /**
+ * 处理微服务异常信息,做到不与原系统的错误处理相冲突
+ * @param request request
+ * @param response response
+ * @param exception exception
+ */
+ public static void hold(HttpServletRequest request, HttpServletResponse response, Exception exception) {
+ log.error("系统错误", exception);
+ int code = exception instanceof ServiceException ? BIZ_ERROR_CODE : SYSTEM_ERROR_CODE;
+ // 需要设置两个值,这样网关会收到错误信息
+ // 并且会统计到监控当中
+ response.setHeader(X_SERVICE_ERROR_HEADER_NAME, String.valueOf(code));
+ String responseBody = buildResponse(request, response, exception);
+ response.setHeader(X_SERVICE_ERROR_RESPONSE, UriUtils.encode(responseBody, StandardCharsets.UTF_8));
+
+ // 如果是未知错误,还需要收集异常信息
+ if (code == SYSTEM_ERROR_CODE) {
+ StringBuilder msg = new StringBuilder();
+ msg.append(exception.getMessage());
+ StackTraceElement[] stackTrace = exception.getStackTrace();
+ // 取5行错误内容
+ int lineCount = 5;
+ for (int i = 0; i < stackTrace.length && i < lineCount; i++) {
+ StackTraceElement stackTraceElement = stackTrace[i];
+ msg.append("\n at ").append(stackTraceElement.toString());
+ }
+ response.setHeader(X_SERVICE_ERROR_MESSAGE, UriUtils.encode(msg.toString(), StandardCharsets.UTF_8));
+ }
+ }
+
+ /**
+ * 处理异常
+ *
+ * @param request request
+ * @param response response
+ * @param exception 异常信息
+ * @return 返回最终结果
+ */
+ private static String buildResponse(HttpServletRequest request, HttpServletResponse response, Exception exception) {
+ ServiceResultBuilder serviceResultBuilder = ServiceConfig.getInstance().getServiceResultBuilder();
+ Object result = serviceResultBuilder.buildError(request, response, exception);
+ return JSON.toJSONString(result);
+ }
+}
diff --git a/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/message/ServiceErrorEnum.java b/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/message/ServiceErrorEnum.java
index 7bd28618..f0319bf5 100644
--- a/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/message/ServiceErrorEnum.java
+++ b/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/message/ServiceErrorEnum.java
@@ -4,8 +4,8 @@ package com.gitee.sop.servercommon.message;
* @author tanghc
*/
public enum ServiceErrorEnum {
- /** 系统繁忙 */
- ISP_UNKNOW_ERROR("isp.unknown-error"),
+ /** 未知错误 */
+ ISP_UNKNOWN_ERROR("isp.service-unknown-error"),
/** 参数错误 */
ISV_PARAM_ERROR("isv.invalid-parameter"),
/** 通用错误 */
diff --git a/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/result/DefaultServiceResultBuilder.java b/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/result/DefaultServiceResultBuilder.java
index a353f5fd..4ec7f7a6 100644
--- a/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/result/DefaultServiceResultBuilder.java
+++ b/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/result/DefaultServiceResultBuilder.java
@@ -1,6 +1,10 @@
package com.gitee.sop.servercommon.result;
+import com.alibaba.fastjson.annotation.JSONField;
import com.gitee.sop.servercommon.exception.ServiceException;
+import com.gitee.sop.servercommon.message.ServiceError;
+import com.gitee.sop.servercommon.message.ServiceErrorEnum;
+import com.gitee.sop.servercommon.message.ServiceErrorMeta;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
@@ -15,20 +19,17 @@ import javax.servlet.http.HttpServletResponse;
@Slf4j
public class DefaultServiceResultBuilder implements ServiceResultBuilder {
- public static final String ISP_UNKNOWN_ERROR = "isp.unknown-error";
-
@Override
public Object buildError(HttpServletRequest request, HttpServletResponse response, Throwable throwable) {
- String subCode, subMsg;
+ ServiceError error;
if (throwable instanceof ServiceException) {
- ServiceException ex = (ServiceException) throwable;
- subCode = ex.getError().getSub_code();
- subMsg = ex.getError().getSub_msg();
+ ServiceException serviceException = (ServiceException) throwable;
+ error = serviceException.getError();
} else {
- subCode = ISP_UNKNOWN_ERROR;
- subMsg = throwable.getMessage();
+ ServiceErrorMeta errorMeta = ServiceErrorEnum.ISP_UNKNOWN_ERROR.getErrorMeta();
+ error = errorMeta.getError();
}
- return this.buildError(subCode, subMsg);
+ return this.buildError(error.getSub_code(), error.getSub_msg());
}
@Override
@@ -41,7 +42,9 @@ public class DefaultServiceResultBuilder implements ServiceResultBuilder {
@Data
public static class AlipayResult {
+ @JSONField(ordinal = 1)
private String sub_code;
+ @JSONField(ordinal = 2)
private String sub_msg;
}
}
diff --git a/sop-common/sop-service-common/src/main/resources/i18n/isp/error_en.properties b/sop-common/sop-service-common/src/main/resources/i18n/isp/error_en.properties
index 9b36e636..9034e15d 100644
--- a/sop-common/sop-service-common/src/main/resources/i18n/isp/error_en.properties
+++ b/sop-common/sop-service-common/src/main/resources/i18n/isp/error_en.properties
@@ -1,4 +1,5 @@
-# 错误配置
+# \u9519\u8BEF\u914D\u7F6E
-isp.error_isp.unknown-error=Service is temporarily unavailable
-isp.error_isv.invalid-parameter=Invalid parameter
\ No newline at end of file
+isp.error_isp.service-unknown-error=Service not unavailable
+isp.error_isv.invalid-parameter=Invalid parameter
+isp.error_isv.common-error=Service error
\ No newline at end of file
diff --git a/sop-common/sop-service-common/src/main/resources/i18n/isp/error_zh_CN.properties b/sop-common/sop-service-common/src/main/resources/i18n/isp/error_zh_CN.properties
index 48fc185c..ceb09ae9 100644
--- a/sop-common/sop-service-common/src/main/resources/i18n/isp/error_zh_CN.properties
+++ b/sop-common/sop-service-common/src/main/resources/i18n/isp/error_zh_CN.properties
@@ -1,5 +1,6 @@
-# 错误配置
+# \u9519\u8BEF\u914D\u7F6E
-isp.error_isp.unknown-error=\u670d\u52a1\u6682\u4e0d\u53ef\u7528
-# 参数无效
-isp.error_isv.invalid-parameter=\u53c2\u6570\u65e0\u6548
+isp.error_isp.service-unknown-error=\u672A\u77E5\u9519\u8BEF
+# \u53C2\u6570\u65E0\u6548
+isp.error_isv.invalid-parameter=\u53C2\u6570\u65E0\u6548
+isp.error_isv.common-error=\u7CFB\u7EDF\u9519\u8BEF
\ No newline at end of file
diff --git a/sop-example/pom.xml b/sop-example/pom.xml
index 3060f4a6..d3cf02b6 100644
--- a/sop-example/pom.xml
+++ b/sop-example/pom.xml
@@ -5,7 +5,7 @@
com.gitee.sop
sop-parent
- 4.3.4-SNAPSHOT
+ 4.4.0-SNAPSHOT
../pom.xml
diff --git a/sop-example/sop-springmvc/pom.xml b/sop-example/sop-springmvc/pom.xml
index 56b153fa..d045afac 100644
--- a/sop-example/sop-springmvc/pom.xml
+++ b/sop-example/sop-springmvc/pom.xml
@@ -5,7 +5,7 @@
com.gitee.sop
sop-parent
- 4.3.4-SNAPSHOT
+ 4.4.0-SNAPSHOT
../../pom.xml
diff --git a/sop-example/sop-story/pom.xml b/sop-example/sop-story/pom.xml
index 001f141e..f7829a43 100644
--- a/sop-example/sop-story/pom.xml
+++ b/sop-example/sop-story/pom.xml
@@ -4,7 +4,7 @@
com.gitee.sop
sop-parent
- 4.3.4-SNAPSHOT
+ 4.4.0-SNAPSHOT
../../pom.xml
diff --git a/sop-example/sop-story/src/main/java/com/gitee/sop/storyweb/StoryGlobalExceptionHandler.java b/sop-example/sop-story/src/main/java/com/gitee/sop/storyweb/StoryGlobalExceptionHandler.java
new file mode 100644
index 00000000..78451b3e
--- /dev/null
+++ b/sop-example/sop-story/src/main/java/com/gitee/sop/storyweb/StoryGlobalExceptionHandler.java
@@ -0,0 +1,48 @@
+package com.gitee.sop.storyweb;
+
+import com.gitee.sop.servercommon.exception.ExceptionHolder;
+import com.gitee.sop.servercommon.exception.ServiceException;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.web.bind.annotation.ControllerAdvice;
+import org.springframework.web.bind.annotation.ExceptionHandler;
+import org.springframework.web.bind.annotation.ResponseBody;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+/**
+ * 全局异常处理
+ *
+ * @author tanghc
+ */
+@ControllerAdvice
+@Slf4j
+public class StoryGlobalExceptionHandler {
+
+
+ /**
+ * 捕获手动抛出的异常
+ *
+ * @param request request
+ * @param response response
+ * @param exception 异常信息
+ * @return 返回提示信息
+ */
+ @ExceptionHandler(Exception.class)
+ @ResponseBody
+ public Object exceptionHandler(HttpServletRequest request, HttpServletResponse response, Exception exception) {
+ // 在返回前加这一句
+ ExceptionHolder.hold(request, response, exception);
+ // 下面可以实现自己的全局异常处理
+ return new ErrorResult(500, exception.getMessage());
+ }
+
+ @Data
+ @AllArgsConstructor
+ public static class ErrorResult {
+ private int code;
+ private String msg;
+ }
+}
diff --git a/sop-example/sop-story/src/main/java/com/gitee/sop/storyweb/controller/Example1005_ThrowExceptionController.java b/sop-example/sop-story/src/main/java/com/gitee/sop/storyweb/controller/Example1005_ThrowExceptionController.java
index 1f58d8e6..7fc5fb11 100644
--- a/sop-example/sop-story/src/main/java/com/gitee/sop/storyweb/controller/Example1005_ThrowExceptionController.java
+++ b/sop-example/sop-story/src/main/java/com/gitee/sop/storyweb/controller/Example1005_ThrowExceptionController.java
@@ -32,4 +32,11 @@ public class Example1005_ThrowExceptionController {
}
return param;
}
+
+ @Open("goods.update2")
+ @RequestMapping("ex2")
+ public Object updateGoods2(GoodsUpdateParam param) {
+ int i = 1/0;
+ return param;
+ }
}
diff --git a/sop-gateway/pom.xml b/sop-gateway/pom.xml
index 4263ec25..856953d2 100644
--- a/sop-gateway/pom.xml
+++ b/sop-gateway/pom.xml
@@ -4,7 +4,7 @@
com.gitee.sop
sop-parent
- 4.3.4-SNAPSHOT
+ 4.4.0-SNAPSHOT
../pom.xml
diff --git a/sop-gateway/src/main/java/com/gitee/sop/gateway/manager/DbLimitConfigManager.java b/sop-gateway/src/main/java/com/gitee/sop/gateway/manager/DbLimitConfigManager.java
index bb536e1b..e1d38613 100644
--- a/sop-gateway/src/main/java/com/gitee/sop/gateway/manager/DbLimitConfigManager.java
+++ b/sop-gateway/src/main/java/com/gitee/sop/gateway/manager/DbLimitConfigManager.java
@@ -10,7 +10,6 @@ import com.gitee.sop.gatewaycommon.util.MyBeanUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.core.env.Environment;
import org.springframework.stereotype.Service;
/**
@@ -24,9 +23,6 @@ public class DbLimitConfigManager extends DefaultLimitConfigManager {
@Autowired
ConfigLimitMapper configLimitMapper;
- @Autowired
- Environment environment;
-
@Override
public void load(String serviceId) {
Query query = new Query();
diff --git a/sop-gateway/src/main/java/com/gitee/sop/gateway/manager/DbRoutesProcessor.java b/sop-gateway/src/main/java/com/gitee/sop/gateway/manager/DbRoutesProcessor.java
index 6740f2d8..34677e53 100644
--- a/sop-gateway/src/main/java/com/gitee/sop/gateway/manager/DbRoutesProcessor.java
+++ b/sop-gateway/src/main/java/com/gitee/sop/gateway/manager/DbRoutesProcessor.java
@@ -4,19 +4,19 @@ import com.alibaba.fastjson.JSON;
import com.gitee.fastmybatis.core.query.Query;
import com.gitee.sop.gateway.entity.ConfigServiceRoute;
import com.gitee.sop.gateway.mapper.ConfigServiceRouteMapper;
-import com.gitee.sop.gatewaycommon.bean.BeanInitializer;
+import com.gitee.sop.gateway.mapper.SystemLockMapper;
import com.gitee.sop.gatewaycommon.bean.InstanceDefinition;
-import com.gitee.sop.gatewaycommon.bean.ServiceBeanInitializer;
import com.gitee.sop.gatewaycommon.bean.ServiceRouteInfo;
import com.gitee.sop.gatewaycommon.route.RoutesProcessor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Component;
+import org.springframework.transaction.annotation.Transactional;
+import java.time.LocalDateTime;
+import java.time.format.DateTimeFormatter;
import java.util.List;
-import java.util.Map;
import java.util.stream.Collectors;
/**
@@ -30,7 +30,7 @@ public class DbRoutesProcessor implements RoutesProcessor {
private ConfigServiceRouteMapper configServiceRouteMapper;
@Autowired
- private ApplicationContext applicationContext;
+ private SystemLockMapper systemLockMapper;
@Override
public void removeAllRoutes(String serviceId) {
@@ -39,8 +39,17 @@ public class DbRoutesProcessor implements RoutesProcessor {
configServiceRouteMapper.deleteByQuery(delServiceQuery);
}
+ @Transactional(rollbackFor = Exception.class)
@Override
public synchronized void saveRoutes(ServiceRouteInfo serviceRouteInfo, InstanceDefinition instance) {
+ // 抢锁,没抢到阻塞在这里
+ systemLockMapper.lock();
+ String time = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMddHHmm"));
+ int result = systemLockMapper.insert(time + serviceRouteInfo.getMd5());
+ // 抢到锁,插入失败,表示其它实例已经处理完毕,这里直接返回
+ if (result == 0) {
+ return;
+ }
log.info("保存路由信息到数据库,instance: {}", instance);
String serviceId = serviceRouteInfo.getServiceId();
List configServiceRoutes = serviceRouteInfo
@@ -72,13 +81,6 @@ public class DbRoutesProcessor implements RoutesProcessor {
if (CollectionUtils.isNotEmpty(configServiceRoutes)) {
// 批量保存
configServiceRouteMapper.saveBatch(configServiceRoutes);
- // 后续处理操作
- this.initServiceBeanInitializer(serviceId);
}
}
-
- private void initServiceBeanInitializer(String serviceId) {
- Map serviceBeanInitializerMap = applicationContext.getBeansOfType(ServiceBeanInitializer.class);
- serviceBeanInitializerMap.values().forEach(serviceBeanInitializer -> serviceBeanInitializer.load(serviceId));
- }
}
diff --git a/sop-gateway/src/main/java/com/gitee/sop/gateway/mapper/SystemLockMapper.java b/sop-gateway/src/main/java/com/gitee/sop/gateway/mapper/SystemLockMapper.java
new file mode 100644
index 00000000..9698cea4
--- /dev/null
+++ b/sop-gateway/src/main/java/com/gitee/sop/gateway/mapper/SystemLockMapper.java
@@ -0,0 +1,27 @@
+package com.gitee.sop.gateway.mapper;
+
+import org.apache.ibatis.annotations.Insert;
+import org.apache.ibatis.annotations.Mapper;
+import org.apache.ibatis.annotations.Param;
+import org.apache.ibatis.annotations.ResultType;
+import org.apache.ibatis.annotations.Select;
+
+/**
+ * @author tanghc
+ */
+@Mapper
+public interface SystemLockMapper {
+
+ /**
+ * 插入唯一值
+ * @param content 唯一值
+ * @return 1:返回成功,0:插入失败
+ */
+ @Insert("INSERT IGNORE INTO system_lock(content) VALUES (#{content})")
+ @ResultType(int.class)
+ int insert(@Param("content") String content);
+
+ @Select("SELECT id FROM system_lock WHERE id=1 FOR UPDATE")
+ @ResultType(long.class)
+ long lock();
+}
diff --git a/sop-mysql5.6以下版本.sql b/sop-mysql5.6以下版本.sql
index a608fa18..2b76307c 100644
--- a/sop-mysql5.6以下版本.sql
+++ b/sop-mysql5.6以下版本.sql
@@ -21,6 +21,14 @@ DROP TABLE IF EXISTS `monitor_info`;
DROP TABLE IF EXISTS `monitor_info_error`;
DROP TABLE IF EXISTS `user_account`;
DROP TABLE IF EXISTS `isp_resource`;
+DROP TABLE IF EXISTS `system_lock`;
+
+CREATE TABLE `system_lock` (
+ `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
+ `content` varchar(64) NOT NULL DEFAULT '',
+ PRIMARY KEY (`id`),
+ UNIQUE KEY `uk_content` (`content`) USING BTREE
+) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE `admin_user_info` (
`id` BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT,
@@ -347,3 +355,5 @@ INSERT INTO `isp_resource` (`id`, `name`, `content`, `ext_content`, `version`, `
(3,'Python','http://www.bilibili.com','```python\n # 创建请求\n request = MemberInfoGetRequest()\n # 请求参数\n model = MemberInfoGetModel()\n model.age = 22\n model.name = \'jim\'\n model.address = \'xx\'\n # 添加请求参数\n request.biz_model = model\n\n # 添加上传文件\n # files = {\n # \'file1\': open(\'aa.txt\', \'rb\'),\n # \'file2\': open(\'bb.txt\', \'rb\')\n # }\n # request.files = files\n\n # 调用请求\n response = self.client.execute(request)\n\n if response.is_success():\n print \'response: \', response\n print \'is_vip:\', response.get(\'member_info\').get(\'is_vip\', 0)\n else:\n print \'请求失败,code:%s, msg:%s, sub_code:%s, sub_msg:%s\' % \\\n (response.code, response.msg, response.sub_code, response.sub_msg)\n```','1.0',0,0,'2020-11-07 14:30:16','2020-11-07 14:31:41'),
(4,'Go','http://www.baidu.com','```go\n\n// 应用ID\nconst appId string = "xx"\n// 应用私钥\nconst privateKey string = "xx"\n// 请求地址\nconst url string = "http://localhost:7071/prod/gw68uy85"\n\n// 请求客户端\nvar openClient = common.OpenClient{AppId: appId, PrivateKey: privateKey, Url: url}\n\nfunc main() {\n // 创建请求\n memberInfoGetRequest := request.MemberInfoGetRequest{}\n // 请求参数\n memberInfoGetRequest.BizModel = model.MemberInfoGetModel{Name: "jim", Age: 22, Address: "xx"}\n \n // 添加上传文件\n //path, _ := os.Getwd()\n //files := []common.UploadFile{\n // {Name:"file1", Filepath:path + "/test/aa.txt"},\n // {Name:"file2", Filepath:path + "/test/bb.txt"},\n //}\n //memberInfoGetRequest.Files = files\n \n // 发送请求,返回json bytes\n var jsonBytes = openClient.Execute(memberInfoGetRequest)\n fmt.Printf("data:%s\\n", string(jsonBytes))\n // 转换结果\n var memberInfoGetResponse response.MemberInfoGetResponse\n response.ConvertResponse(jsonBytes, &memberInfoGetResponse)\n\n if memberInfoGetResponse.IsSuccess() {\n fmt.Printf("is_vip:%d, vip_endtime:%s\\n", memberInfoGetResponse.MemberInfo.IsVip, memberInfoGetResponse.MemberInfo.VipEndtime)\n } else {\n fmt.Printf("code:%s, msg:%s, subCode:%s, subMsg:%s\\n",\n memberInfoGetResponse.Code, memberInfoGetResponse.Msg, memberInfoGetResponse.SubCode, memberInfoGetResponse.SubMsg)\n }\n}\n```','1.0',0,0,'2020-11-07 14:31:21','2020-11-07 14:31:21'),
(5,'C++','http://pan.baidu.com','#include \n\n#include "common/OpenClient.h"\n#include "request/BaseRequest.h"\n#include "request/MemberInfoGetRequest.hpp"\n\n// 应用ID\nstring appId = "2020051325943082302177280";\n// 存放私钥的文件路径\nstring privateKeyFile = "/Users/thc/IdeaProjects/opc/opc-sdk/sdk-c++/privateEx.pem";\n// 请求接口\nstring url = "http://localhost:7071/prod/gw68uy85";\n\nOpenClient openClient(appId, privateKeyFile, url);\n\nint main() {\n // 创建请求\n MemberInfoGetRequest request;\n\n // 业务参数\n map bizModel;\n bizModel["name"] = "jim";\n bizModel["age"] = "22";\n bizModel["address"] = "xx";\n\n request.bizModel = bizModel;\n\n // 添加上传文件\n// request->setFiles({\n// FileInfo{"aa", "/Users/thc/IdeaProjects/opc/opc-sdk/sdk-c++/aa.txt"},\n// FileInfo{"bb", "/Users/thc/IdeaProjects/opc/opc-sdk/sdk-c++/bb.txt"}\n// });\n\n // 发送请求\n neb::CJsonObject jsonObj = openClient.execute(&request);\n std::cout << jsonObj.ToString() << std::endl;\n std::cout << "id:" << jsonObj["id"].ToString() << std::endl;\n std::cout << "is_vip:" << jsonObj["member_info"]["is_vip"].ToString() << std::endl;\n return 0;\n}\n\n','1.0',0,0,'2020-11-07 14:32:55','2020-11-07 14:32:55');
+
+INSERT INTO `system_lock` (`id`, `content`) VALUES (1,'lock');
\ No newline at end of file
diff --git a/sop-sdk/pom.xml b/sop-sdk/pom.xml
index 390ad294..ffb39022 100644
--- a/sop-sdk/pom.xml
+++ b/sop-sdk/pom.xml
@@ -6,7 +6,7 @@
com.gitee.sop
sop-parent
- 4.3.4-SNAPSHOT
+ 4.4.0-SNAPSHOT
../pom.xml
diff --git a/sop-sdk/sdk-java/pom.xml b/sop-sdk/sdk-java/pom.xml
index 51fb751d..01f148f0 100644
--- a/sop-sdk/sdk-java/pom.xml
+++ b/sop-sdk/sdk-java/pom.xml
@@ -4,13 +4,13 @@
com.gitee.sop
sop-parent
- 4.3.4-SNAPSHOT
+ 4.4.0-SNAPSHOT
../../pom.xml
4.0.0
sdk-java
- 4.3.4-SNAPSHOT
+ 4.4.0-SNAPSHOT
diff --git a/sop-test/pom.xml b/sop-test/pom.xml
index 82e06234..db45c02e 100644
--- a/sop-test/pom.xml
+++ b/sop-test/pom.xml
@@ -5,7 +5,7 @@
com.gitee.sop
sop-parent
- 4.3.4-SNAPSHOT
+ 4.4.0-SNAPSHOT
../pom.xml
diff --git a/sop-test/src/test/java/com/gitee/sop/test/AllInOneTest.java b/sop-test/src/test/java/com/gitee/sop/test/AllInOneTest.java
index 23a56d2e..0e935a0e 100644
--- a/sop-test/src/test/java/com/gitee/sop/test/AllInOneTest.java
+++ b/sop-test/src/test/java/com/gitee/sop/test/AllInOneTest.java
@@ -422,6 +422,25 @@ public class AllInOneTest extends TestBase {
client.execute(requestBuilder);
}
+ /**
+ * 未知异常
+ */
+ public void testException() {
+ Client.RequestBuilder requestBuilder = new Client.RequestBuilder()
+ .method("goods.update2")
+ .version("1.0")
+ .bizContent(new BizContent().add("goods_name", "Apple"))
+ .httpMethod(HttpTool.HTTPMethod.POST)
+ .callback((requestInfo, responseData) -> {
+ System.out.println(responseData);
+ String node = requestInfo.getDataNode();
+ JSONObject jsonObject = JSON.parseObject(responseData).getJSONObject(node);
+ Assert.assertEquals("isp.service-unknown-error", jsonObject.getString("sub_code"));
+ });
+
+ client.execute(requestBuilder);
+ }
+
static class BizContent extends HashMap {
public BizContent add(String key, Object value) {
this.put(key, value);
diff --git a/sop-website/pom.xml b/sop-website/pom.xml
index efb588c5..b0f9bc3f 100644
--- a/sop-website/pom.xml
+++ b/sop-website/pom.xml
@@ -6,7 +6,7 @@
com.gitee.sop
sop-parent
- 4.3.4-SNAPSHOT
+ 4.4.0-SNAPSHOT
../pom.xml
diff --git a/sop-website/sop-website-server/pom.xml b/sop-website/sop-website-server/pom.xml
index c740f11f..c61693a0 100644
--- a/sop-website/sop-website-server/pom.xml
+++ b/sop-website/sop-website-server/pom.xml
@@ -5,7 +5,7 @@
com.gitee.sop
sop-website
- 4.3.4-SNAPSHOT
+ 4.4.0-SNAPSHOT
../pom.xml
diff --git a/sop-website/sop-website-vue/public/static/code.json b/sop-website/sop-website-vue/public/static/code.json
index 8ac28678..b48cf3cd 100644
--- a/sop-website/sop-website-vue/public/static/code.json
+++ b/sop-website/sop-website-vue/public/static/code.json
@@ -50,6 +50,12 @@
"sub_code": "isp.exec-gzip-error",
"sub_msg": "处理gzip异常",
"solution": "联系客服排查日志"
+ },
+ {
+ "id": 1208,
+ "sub_code": "isp.service-unknown-error",
+ "sub_msg": "服务不可用",
+ "solution": "联系客服排查日志"
}
]
},
diff --git a/sop.sql b/sop.sql
index d296cdeb..3f7ef0e3 100644
--- a/sop.sql
+++ b/sop.sql
@@ -23,6 +23,7 @@ DROP TABLE IF EXISTS `monitor_info`;
DROP TABLE IF EXISTS `monitor_info_error`;
DROP TABLE IF EXISTS `user_account`;
DROP TABLE IF EXISTS `isp_resource`;
+DROP TABLE IF EXISTS `system_lock`;
CREATE TABLE `admin_user_info` (
@@ -280,6 +281,13 @@ CREATE TABLE `isp_resource` (
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='ISP资源表';
+CREATE TABLE `system_lock` (
+ `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
+ `content` varchar(64) NOT NULL DEFAULT '',
+ PRIMARY KEY (`id`),
+ UNIQUE KEY `uk_content` (`content`) USING BTREE
+) ENGINE=InnoDB DEFAULT CHARSET=utf8;
+
INSERT INTO `admin_user_info` (`id`, `username`, `password`, `status`, `gmt_create`, `gmt_modified`) VALUES
(1,'admin','a62cd510fb9a8a557a27ef279569091f',1,'2019-04-02 19:55:26','2019-04-02 19:55:26');
@@ -350,3 +358,5 @@ INSERT INTO `isp_resource` (`id`, `name`, `content`, `ext_content`, `version`, `
(3,'Python','http://www.bilibili.com','```python\n # 创建请求\n request = MemberInfoGetRequest()\n # 请求参数\n model = MemberInfoGetModel()\n model.age = 22\n model.name = \'jim\'\n model.address = \'xx\'\n # 添加请求参数\n request.biz_model = model\n\n # 添加上传文件\n # files = {\n # \'file1\': open(\'aa.txt\', \'rb\'),\n # \'file2\': open(\'bb.txt\', \'rb\')\n # }\n # request.files = files\n\n # 调用请求\n response = self.client.execute(request)\n\n if response.is_success():\n print \'response: \', response\n print \'is_vip:\', response.get(\'member_info\').get(\'is_vip\', 0)\n else:\n print \'请求失败,code:%s, msg:%s, sub_code:%s, sub_msg:%s\' % \\\n (response.code, response.msg, response.sub_code, response.sub_msg)\n```','1.0',0,0,'2020-11-07 14:30:16','2020-11-07 14:31:41'),
(4,'Go','http://www.baidu.com','```go\n\n// 应用ID\nconst appId string = "xx"\n// 应用私钥\nconst privateKey string = "xx"\n// 请求地址\nconst url string = "http://localhost:7071/prod/gw68uy85"\n\n// 请求客户端\nvar openClient = common.OpenClient{AppId: appId, PrivateKey: privateKey, Url: url}\n\nfunc main() {\n // 创建请求\n memberInfoGetRequest := request.MemberInfoGetRequest{}\n // 请求参数\n memberInfoGetRequest.BizModel = model.MemberInfoGetModel{Name: "jim", Age: 22, Address: "xx"}\n \n // 添加上传文件\n //path, _ := os.Getwd()\n //files := []common.UploadFile{\n // {Name:"file1", Filepath:path + "/test/aa.txt"},\n // {Name:"file2", Filepath:path + "/test/bb.txt"},\n //}\n //memberInfoGetRequest.Files = files\n \n // 发送请求,返回json bytes\n var jsonBytes = openClient.Execute(memberInfoGetRequest)\n fmt.Printf("data:%s\\n", string(jsonBytes))\n // 转换结果\n var memberInfoGetResponse response.MemberInfoGetResponse\n response.ConvertResponse(jsonBytes, &memberInfoGetResponse)\n\n if memberInfoGetResponse.IsSuccess() {\n fmt.Printf("is_vip:%d, vip_endtime:%s\\n", memberInfoGetResponse.MemberInfo.IsVip, memberInfoGetResponse.MemberInfo.VipEndtime)\n } else {\n fmt.Printf("code:%s, msg:%s, subCode:%s, subMsg:%s\\n",\n memberInfoGetResponse.Code, memberInfoGetResponse.Msg, memberInfoGetResponse.SubCode, memberInfoGetResponse.SubMsg)\n }\n}\n```','1.0',0,0,'2020-11-07 14:31:21','2020-11-07 14:31:21'),
(5,'C++','http://pan.baidu.com','#include \n\n#include "common/OpenClient.h"\n#include "request/BaseRequest.h"\n#include "request/MemberInfoGetRequest.hpp"\n\n// 应用ID\nstring appId = "2020051325943082302177280";\n// 存放私钥的文件路径\nstring privateKeyFile = "/Users/thc/IdeaProjects/opc/opc-sdk/sdk-c++/privateEx.pem";\n// 请求接口\nstring url = "http://localhost:7071/prod/gw68uy85";\n\nOpenClient openClient(appId, privateKeyFile, url);\n\nint main() {\n // 创建请求\n MemberInfoGetRequest request;\n\n // 业务参数\n map bizModel;\n bizModel["name"] = "jim";\n bizModel["age"] = "22";\n bizModel["address"] = "xx";\n\n request.bizModel = bizModel;\n\n // 添加上传文件\n// request->setFiles({\n// FileInfo{"aa", "/Users/thc/IdeaProjects/opc/opc-sdk/sdk-c++/aa.txt"},\n// FileInfo{"bb", "/Users/thc/IdeaProjects/opc/opc-sdk/sdk-c++/bb.txt"}\n// });\n\n // 发送请求\n neb::CJsonObject jsonObj = openClient.execute(&request);\n std::cout << jsonObj.ToString() << std::endl;\n std::cout << "id:" << jsonObj["id"].ToString() << std::endl;\n std::cout << "is_vip:" << jsonObj["member_info"]["is_vip"].ToString() << std::endl;\n return 0;\n}\n\n','1.0',0,0,'2020-11-07 14:32:55','2020-11-07 14:32:55');
+
+INSERT INTO `system_lock` (`id`, `content`) VALUES (1,'lock');
diff --git a/升级到4.4.0注意事项.md b/升级到4.4.0注意事项.md
new file mode 100644
index 00000000..e65f15d0
--- /dev/null
+++ b/升级到4.4.0注意事项.md
@@ -0,0 +1,19 @@
+从之前版本升级到4.4.0必须看,如果直接从4.4.0开始使用,不用看。
+
+- 首先执行升级脚本
+
+```sql
+use sop;
+
+CREATE TABLE `system_lock` (
+ `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
+ `content` varchar(64) NOT NULL DEFAULT '',
+ PRIMARY KEY (`id`),
+ UNIQUE KEY `uk_content` (`content`) USING BTREE
+) ENGINE=InnoDB DEFAULT CHARSET=utf8;
+
+INSERT INTO `system_lock` (`id`, `content`) VALUES (1,'lock');
+```
+
+类`com.gitee.sop.servercommon.configuration.GlobalExceptionHandler`已被废弃,替代方案改用自己的全局异常处理
+参考:`com.gitee.sop.storyweb.StoryGlobalExceptionHandler`。这样改的目的是对于老项目的全局异常无入侵。
\ No newline at end of file