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