全面使用nacos,舍弃zookeeper

This commit is contained in:
tanghc
2019-08-21 16:04:32 +08:00
parent 90d6823693
commit 6e2a0e6c13
36 changed files with 179 additions and 658 deletions

View File

@@ -62,7 +62,7 @@ SOP封装了开放平台大部分功能包括签名验证、统一异常处
## 工程说明
> 运行环境JDK8Maven3Zookeeper
> 运行环境JDK8Maven3[Nacos](https://nacos.io/zh-cn/docs/what-is-nacos.html)
- doc开发文档
- sop-admin后台管理
@@ -76,9 +76,9 @@ SOP封装了开放平台大部分功能包括签名验证、统一异常处
## 分支说明
- master发版分支
- spring-cloud-gatewaySpring Cloud Gateway作为网关
- develop日常开发分支
- registry-nacosnacos作为注册中心
- SpringCloudGatewaySpringCloudGateway作为网关
## 相关文档

View File

@@ -1,22 +1,20 @@
# 快速体验
> 运行环境JDK8Maven3ZookeeperMysql
> 运行环境JDK8Maven3[Nacos](https://nacos.io/zh-cn/docs/what-is-nacos.html)Mysql
- 安装并启动zookeeper[安装教程](http://zookeeper.apache.org/doc/r3.4.13/zookeeperStarted.html)
- 安装并启动Nacos[安装教程](https://nacos.io/zh-cn/docs/quick-start.html)
- 执行Mysql脚本`sop.sql`
- IDE安装lombok插件然后打开项目(IDEA下可以打开根pom.xml然后open as project)
- 启动注册中心sop-registry运行SopRegistryApplication.java
- 启动网关打开sop-gateway下的`application-dev.properties`
1. 修改数据库`username/password`
2. 指定zookeeper地址如果zookeeper安装在本机则不用改
2. 指定nacos地址如果nacos安装在本机则不用改
3. 运行`SopGatewayApplication.java`
- 启动微服务打开sop-story-web下的`application-dev.properties`文件
1. 指定zookeeper地址如果zookeeper安装在本机则不用改
1. 指定nacos地址如果nacos安装在本机则不用改
2. 运行`SopStoryApplication.java`
- 找到sop-test打开`AllInOneTest.java`进行接口调用测试
确保注册中心先启动
## 使用admin
- 找到`sop-admin/sop-admin-server`工程打开sop-admin-server下的`application-dev.properties`,修改相关配置

View File

@@ -39,10 +39,8 @@
server.port=2222
# 服务名称
spring.application.name=story-service
# eureka注册中心
eureka.client.serviceUrl.defaultZone=http://localhost:1111/eureka/
# zookeeper配置
spring.cloud.zookeeper.connect-string=localhost:2181
# nacos注册中心
spring.cloud.nacos.discovery.server-addr=127.0.0.1:8848
```
- 在springboot启动类上添加`@EnableDiscoveryClient`

View File

@@ -7,8 +7,8 @@
SOP也是采用这种方式实现大致步骤如下
- 每个服务注册到注册中心在启动的时候把自己的路由信息上传到zookeeper并且保证每一个接口都能对应到哪个服务。
- 网关启动时同样注册到注册中心获取zookeeper上的接口信息保存到本地并监听zookeeper上的接口信息一旦接口信息有修改网关这边能及时进行更新。
- 每个服务注册到nacos
- 网关启动时同样注册到nacos然后从各服务中拉取路由信息
- 网关收到客户端请求后,先进行签名校验,通过之后根据接口信息找到对应的服务,然后进行路由
- 网关对返回结果进行处理(或不处理),返回给客户端。
@@ -16,10 +16,10 @@ SOP也是采用这种方式实现大致步骤如下
在网关定义一个`Map<String, RouteInfo> routeMap = ...`key为接口名+版本号。
网关启动时,从zookeeper中获取路由信息并保存到routeMap中
网关启动时,从各微服务中获取路由信息并保存到routeMap中
```java
routeMap = buildFromZookeeper();
routeMap = requestFormServices();
```
接口请求进来后,根据`方法名+版本号`获取路由信息,然后进行路由转发。
@@ -33,7 +33,7 @@ RouteInfo routeInfo = routeMap.get(method + version);
doRoute(routeInfo);
```
因为有多个服务路由信息注册到zookeeper我们要确保接口名唯一,即`method`全局唯一。
因为nacos需要拉取各个微服务路由信息,接口名有可能会冲突,因此需要确保接口名唯一,即`method`全局唯一。
我们推荐接口名的命名规则应该是:`服务模块.业务模块.功能模块.行为`,如:

View File

@@ -1,8 +1,6 @@
# 使用SpringCloudGateway
SOP默认网关是使用Spring Cloud Zuul您也可以切换成Spring Cloud Gateway完整代码见`SpringCloudGateway`分支。
**注:**SOP对Spring Cloud Gateway的支持目前处于beta阶段推荐使用zuul。
SOP默认网关是使用Spring Cloud Zuul您也可以切换成Spring Cloud Gateway完整代码见`spring-cloud-gateway`分支。
步骤如下:

View File

@@ -1,90 +0,0 @@
# nacos注册中心
使用nacos作为注册中心源码在`registry-nacos`分支
这里演示如何将默认的eureka注册中心替换成nacos步骤如下
- 准备工作
1.安装nacos前往[最新稳定版本](https://github.com/alibaba/nacos/releases)下载最新版nacos
2.启动nacos服务器cd nacos/bin
Linux/Unix/Mac启动命令(standalone代表着单机模式运行非集群模式):
`sh startup.sh -m standalone`
Windows启动命令
`cmd startup.cmd`
或者双击startup.cmd运行文件。
更多访问https://nacos.io/zh-cn/docs/quick-start.html
- 微服务端修改
1.修改微服务应用pom打开`sop-example/sop-story/sop-story-web/pom.xml`注释eureka服务发现依赖添加nacos服务发现依赖
```xml
<!-- 注册中心【只能用一个,不用的注释掉】 -->
<!-- 使用eureka注册中心
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
-->
<!-- 使用nacos注册中心
版本 0.2.x.RELEASE 对应的是 Spring Boot 2.x 版本,版本 0.1.x.RELEASE 对应的是 Spring Boot 1.x 版本。
https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-starter-alibaba-nacos-discovery
-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
<version>0.2.2.RELEASE</version>
</dependency>
<!-- 注册中心end -->
```
2.yml文件新增nacos配置并注释掉eureka相关配置
```yaml
spring:
cloud:
# nacos注册中心和eureka只能用一个
nacos:
discovery:
server-addr: 127.0.0.1:8848
```
- 网关修改
找到`sop-gateway`工程,步骤同上
- SOP-admin修改
修改yml文件设置nacos服务器地址`registry.name`填nacos
```yaml
# 注册中心地址,根据实际情况改,这里只是参数,并不会去注册
registry:
eureka-server-addr: http://localhost:1111/eureka/
# nacos服务器地址
nacos-server-addr: 127.0.0.1:8848
# 使用eurekaeureka使用nacos填nacos
name: nacos
```
- website-server修改
步骤同`SOP-admin修改`
如果要改成consul注册中心可参照以上步骤。
- 参考资料
1.[nacos介绍及安装](https://nacos.io/zh-cn/docs/quick-start.html)
2.[nacos spring cloud注册发现](https://nacos.io/zh-cn/docs/quick-start-spring-cloud.html)

View File

@@ -1,112 +0,0 @@
# 扩展其它注册中心
**注: nacos注册中心已经实现本篇以nacos为例介绍如何扩展如果要改成consul可按照此方式进行修改**
SOP默认使用eureka注册中心如果要换成nacos注册中心步骤如下
- 实现`com.gitee.sop.registryapi.service.RegistryService`接口
1.找到`SOP/sop-common/sop-registry-api`工程在service.impl包下新建一个类实现RegistryService接口
```java
public class RegistryServiceNacos implements RegistryService {
@Override
public List<ServiceInfo> listAllService(int pageNo, int pageSize) throws Exception {
// TODO: 返回服务实例
}
@Override
public void onlineInstance(ServiceInstance serviceInstance) throws Exception {
// TODO: 实例上线
}
@Override
public void offlineInstance(ServiceInstance serviceInstance) throws Exception {
// TODO: 实例下线
}
}
```
2.在`com.gitee.sop.registryapi.config.BaseRegistryConfig`中新增
```java
/**
* 当配置了registry.name=nacos生效
*
* @return
*/
@Bean
@ConditionalOnProperty(prefix = "registry", name = "name", havingValue = "nacos")
RegistryService registryServiceNacos() {
return new RegistryServiceNacos();
}
```
其中`@ConditionalOnProperty(prefix = "registry", name = "name", havingValue = "nacos")`
表示`application.properties`配置了`registry.name=nacos`参数才能生效registry.name=nacos下文会讲到。
- 微服务端修改
1.修改微服务应用pom.xml注释eureka服务发现依赖添加nacos服务发现依赖
```xml
<!-- 注册中心【只能用一个,不用的注释掉】 -->
<!-- 使用eureka注册中心
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
-->
<!-- 使用nacos注册中心
版本 0.2.x.RELEASE 对应的是 Spring Boot 2.x 版本,版本 0.1.x.RELEASE 对应的是 Spring Boot 1.x 版本。
https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-starter-alibaba-nacos-discovery
-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
<version>0.2.2.RELEASE</version>
</dependency>
<!-- 注册中心end -->
```
2.yml文件新增nacos配置并注释掉eureka相关配置
```yaml
spring:
cloud:
# nacos注册中心和eureka只能用一个
nacos:
discovery:
server-addr: 127.0.0.1:8848
```
- 网关修改
找到`sop-gateway`工程,步骤同上
- SOP-admin修改
修改yml文件新增nacos服务器地址`registry.name`填nacos
```yaml
# 注册中心地址,根据实际情况改,这里只是参数,并不会去注册
registry:
# 使用eurekaeureka使用nacos填nacos
name: nacos
eureka-server-addr: http://localhost:1111/eureka/
nacos-server-addr: 127.0.0.1:8848
```
- website-server修改
步骤同`SOP-admin修改`
- 参考资料
1.[nacos介绍及安装](https://nacos.io/zh-cn/docs/quick-start.html)
2.[nacos spring cloud注册发现](https://nacos.io/zh-cn/docs/quick-start-spring-cloud.html)

View File

@@ -1,25 +0,0 @@
# 原理分析之路由存储
SOP将路由信息存到了zookeeper当中服务在启动时将自己的路由信息上传到zookeeper中。
网关监听存放路由的节点,动态更新到本地。
zookeeper存储路由的结构如下
```xml
/com.gitee.sop.route 根节点
/serviceId 服务节点,名字为服务名
/route1 路由节点名字为name+version存放路由信息
/route2
/...
```
服务启动时,创建`/serviceId`节点,然后遍历创建`/routeN`节点
同时,网关监听`服务节点``路由节点`,当有新服务加入时,网关会获取到新加入的路由节点信息,
同时路由节点下面的子节点也会被监听到。后续子节点的增删改都会被网关监听到,然后更新到本地。
服务上传路由相关代码在`com.gitee.sop.servercommon.manager.ServiceZookeeperApiMetaManager`类中
网关监听相关代码在`com.gitee.sop.gatewaycommon.manager.BaseRouteManager`

View File

@@ -19,16 +19,6 @@ OpenContext openContext = ServiceContext.getCurrentContext().getOpenContext();
Story bizObject = openContext.getBizObject(Story.class);
```
## Socket error occurred: `localhost/0:0:0:0:0:0:0:1:2181`: Connection refused
检查本地zookeeper有没启动如果zookeeper在其他机器上修改application-dev.yml
```yaml
cloud:
zookeeper:
connect-string: ip:2181
```
## 如何关闭签名验证
- 针对某一个接口关闭签名验证

View File

@@ -21,8 +21,6 @@
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
<zookeeper.version>3.4.12</zookeeper.version>
<curator-recipes.version>4.0.1</curator-recipes.version>
<okhttp.version>3.11.0</okhttp.version>
</properties>
@@ -30,12 +28,12 @@
<dependency>
<groupId>com.alibaba.boot</groupId>
<artifactId>nacos-config-spring-boot-starter</artifactId>
<version>0.2.2</version>
<version>0.2.3</version>
</dependency>
<dependency>
<groupId>com.alibaba.boot</groupId>
<artifactId>nacos-discovery-spring-boot-starter</artifactId>
<version>0.2.2</version>
<version>0.2.3</version>
</dependency>
<dependency>

View File

@@ -292,10 +292,10 @@ public class IsvApi {
}
try {
routePermissionService.sendIsvRolePermissionToZookeeper(isvInfo.getAppKey(), roleCodeList);
routePermissionService.sendIsvRolePermissionMsg(isvInfo.getAppKey(), roleCodeList);
} catch (Exception e) {
log.error("保存到zookeeper中失败isvInfo:{}, roleCodeList:{}", isvInfo, roleCodeList);
throw new BizException("同步zookeeper失败,请查看网关日志");
log.error("同步角色失败isvInfo:{}, roleCodeList:{}", isvInfo, roleCodeList);
throw new BizException("同步角色失败,请查看网关日志");
}
}
}

View File

@@ -4,7 +4,6 @@ import com.gitee.easyopen.annotation.Api;
import com.gitee.easyopen.annotation.ApiService;
import com.gitee.easyopen.doc.annotation.ApiDoc;
import com.gitee.easyopen.doc.annotation.ApiDocMethod;
import com.gitee.easyopen.util.CopyUtil;
import com.gitee.sop.adminserver.api.isv.result.RoleVO;
import com.gitee.sop.adminserver.api.service.param.RouteAddParam;
import com.gitee.sop.adminserver.api.service.param.RouteDeleteParam;
@@ -95,46 +94,19 @@ public class RouteApi {
@Api(name = "route.add")
@ApiDocMethod(description = "新增路由")
void addRoute(RouteAddParam param) {
// TODO:新增路由
String id = param.getName() + param.getVersion();
// String routePath = ZookeeperContext.buildRoutePath(param.getServiceId(), id);
GatewayRouteDefinition routeDefinition = new GatewayRouteDefinition();
CopyUtil.copyPropertiesIgnoreNull(param, routeDefinition);
routeDefinition.setId(id);
routeDefinition.setCustom(1);
// try {
// ZookeeperContext.addPath(routePath, JSON.toJSONString(routeDefinition));
// } catch (ZookeeperPathExistException e) {
// throw new BizException("路由已存在");
// }
this.updateRouteConfig(routeDefinition);
// TODO: 新增路由
}
@Api(name = "route.update")
@ApiDocMethod(description = "修改路由")
void updateRoute(RouteUpdateParam param) {
// TODO:修改路由
// String routePath = ZookeeperContext.buildRoutePath(param.getServiceId(), param.getId());
// GatewayRouteDefinition routeDefinition = this.getGatewayRouteDefinition(routePath);
// CopyUtil.copyPropertiesIgnoreNull(param, routeDefinition);
// try {
// ZookeeperContext.updatePathData(routePath, JSON.toJSONString(routeDefinition));
// } catch (ZookeeperPathNotExistException e) {
// throw new BizException("路由不存在");
// }
// this.updateRouteConfig(routeDefinition);
// TODO: 修改路由
}
@Api(name = "route.del")
@ApiDocMethod(description = "删除路由")
void delRoute(RouteDeleteParam param) {
// TODO:删除路由
/*String routePath = ZookeeperContext.buildRoutePath(param.getServiceId(), param.getId());
GatewayRouteDefinition routeDefinition = this.getGatewayRouteDefinition(routePath);
if (!BooleanUtils.toBoolean(routeDefinition.getCustom())) {
throw new BizException("非自定义路由,无法删除");
}
ZookeeperContext.deletePathDeep(routePath);*/
// TODO: 删除路由
}

View File

@@ -1,19 +0,0 @@
package com.gitee.sop.adminserver.bean;
/**
* @author tanghc
*/
public class SopAdminConstants {
/**
* zookeeper存放接口路由信息的根目录
*/
public static final String SOP_SERVICE_ROUTE_PATH = "/com.gitee.sop.route";
/**
* 消息监听路径
*/
public static final String SOP_MSG_CHANNEL_PATH = "/com.gitee.sop.channel";
public static final String RELOAD_ROUTE_PERMISSION_PATH = "/com.gitee.sop.route.permission.reload";
}

View File

@@ -58,12 +58,12 @@ public class RoutePermissionService {
}
/**
* 推送isv路由权限到zookeeper
* 推送isv路由权限
*
* @param appKey
* @param roleCodeList
*/
public void sendIsvRolePermissionToZookeeper(String appKey, List<String> roleCodeList) throws Exception {
public void sendIsvRolePermissionMsg(String appKey, List<String> roleCodeList) throws Exception {
Collections.sort(roleCodeList);
List<String> routeIdList = this.getRouteIdList(roleCodeList);
String roleCodeListMd5 = DigestUtils.md5Hex(JSON.toJSONString(routeIdList));
@@ -95,7 +95,7 @@ public class RoutePermissionService {
}
/**
* 推送所有路由权限到zookeeper
* 推送所有路由权限
*/
public void sendRoutePermissionReloadMsg(RoutePermissionParam oldRoutePermission) throws Exception {
IsvRoutePermission isvRoutePermission = new IsvRoutePermission();

View File

@@ -42,17 +42,6 @@ public class SopConstants {
public static final int BIZ_ERROR_STATUS = 4000;
/**
* zookeeper存放接口路由信息的根目录
*/
public static final String SOP_SERVICE_ROUTE_PATH = "/com.gitee.sop.route";
/**
* 消息监听路径
*/
public static final String SOP_MSG_CHANNEL_PATH = "/com.gitee.sop.channel";
public static final String UNKNOWN_SERVICE= "_sop_unknown_service_";
public static final String UNKNOWN_METHOD = "_sop_unknown_method_";
public static final String UNKNOWN_VERSION = "_sop_unknown_version_";

View File

@@ -1,7 +1,5 @@
package com.gitee.sop.servercommon.bean;
import com.gitee.sop.servercommon.configuration.DefaultGlobalExceptionHandler;
import com.gitee.sop.servercommon.configuration.GlobalExceptionHandler;
import com.gitee.sop.servercommon.param.ApiArgumentResolver;
import com.gitee.sop.servercommon.param.SopHandlerMethodArgumentResolver;
import com.gitee.sop.servercommon.result.DefaultServiceResultBuilder;
@@ -45,11 +43,6 @@ public class ServiceConfig {
*/
private ServiceResultBuilder serviceResultBuilder = new DefaultServiceResultBuilder();
/**
* 全局异常处理
*/
private GlobalExceptionHandler globalExceptionHandler = new DefaultGlobalExceptionHandler();
/**
* 默认版本号
*/

View File

@@ -1,16 +0,0 @@
package com.gitee.sop.servercommon.bean;
/**
* @author tanghc
*/
public class ServiceConstants {
/**
* zookeeper存放接口路由信息的根目录
*/
public static final String SOP_SERVICE_ROUTE_PATH = "/com.gitee.sop.route";
/**
* 服务临时节点
*/
public static final String SOP_SERVICE_TEMP_PATH = "/com.gitee.sop.service.tmp";
}

View File

@@ -8,6 +8,7 @@ import com.gitee.sop.servercommon.message.ServiceErrorFactory;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.StringHttpMessageConverter;
@@ -70,12 +71,20 @@ public class BaseServiceConfiguration extends WebMvcConfigurationSupport
}
@Bean
@ConditionalOnMissingBean
GlobalExceptionHandler globalExceptionHandler() {
return ServiceConfig.getInstance().getGlobalExceptionHandler();
return new GlobalExceptionHandler();
}
@Bean
ServiceRouteController serviceRouteController() {
@ConditionalOnMissingBean
ErrorController errorController() {
return new ErrorController();
}
@Bean
@ConditionalOnMissingBean
ServiceRouteController serviceRouteInfoHandler() {
return new ServiceRouteController();
}

View File

@@ -1,92 +0,0 @@
package com.gitee.sop.servercommon.configuration;
import com.gitee.sop.servercommon.bean.ServiceConfig;
import com.gitee.sop.servercommon.exception.ServiceException;
import com.gitee.sop.servercommon.result.ServiceResultBuilder;
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.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* 全局异常处理
* @author tanghc
*/
@ControllerAdvice
@Slf4j
public class DefaultGlobalExceptionHandler implements GlobalExceptionHandler {
/**
* 与网关约定好的状态码,表示业务出错
*/
public static final int BIZ_ERROR_CODE = 4000;
/**
* 系统错误
*/
public static final int SYSTEM_ERROR_CODE = 5050;
public static final String X_SERVICE_ERROR_CODE = "x-service-error-code";
@RequestMapping("/error")
@ResponseBody
public Object error(HttpServletRequest request, HttpServletResponse response) {
ServiceResultBuilder serviceResultBuilder = ServiceConfig.getInstance().getServiceResultBuilder();
return serviceResultBuilder.buildError(request, response, new ServiceException("系统繁忙"));
}
/**
* 捕获手动抛出的异常
* @param request
* @param response
* @param exception
* @return
*/
@ExceptionHandler(ServiceException.class)
@ResponseBody
public Object serviceExceptionHandler(HttpServletRequest request, HttpServletResponse response, Exception exception) {
response.addHeader(X_SERVICE_ERROR_CODE, String.valueOf(BIZ_ERROR_CODE));
return this.processError(request, response, exception);
}
/**
* 捕获未知异常
* @param request
* @param response
* @param exception
* @return
*/
@ExceptionHandler(Exception.class)
@ResponseBody
public Object exceptionHandler(HttpServletRequest request, HttpServletResponse response, Exception exception) {
response.addHeader(X_SERVICE_ERROR_CODE, String.valueOf(SYSTEM_ERROR_CODE));
log.error("系统错误", exception);
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("<br> at ").append(stackTraceElement.toString());
}
response.setHeader("x-service-error-message", msg.toString());
return this.processError(request, response, new ServiceException("系统繁忙"));
}
/**
* 处理异常
*
* @param request
* @param response
* @param exception
* @return 返回最终结果
*/
protected Object processError(HttpServletRequest request, HttpServletResponse response, Exception exception) {
ServiceResultBuilder serviceResultBuilder = ServiceConfig.getInstance().getServiceResultBuilder();
return serviceResultBuilder.buildError(request, response, exception);
}
}

View File

@@ -0,0 +1,27 @@
package com.gitee.sop.servercommon.configuration;
import com.gitee.sop.servercommon.bean.ServiceConfig;
import com.gitee.sop.servercommon.exception.ServiceException;
import com.gitee.sop.servercommon.result.ServiceResultBuilder;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* @author tanghc
*/
@RestController
public class ErrorController {
@RequestMapping("/error")
public Object error(HttpServletRequest request, HttpServletResponse response) {
return getResult(request, response);
}
protected Object getResult(HttpServletRequest request, HttpServletResponse response) {
ServiceResultBuilder serviceResultBuilder = ServiceConfig.getInstance().getServiceResultBuilder();
return serviceResultBuilder.buildError(request, response, new ServiceException("系统繁忙"));
}
}

View File

@@ -1,7 +1,92 @@
package com.gitee.sop.servercommon.configuration;
import com.gitee.sop.servercommon.bean.ServiceConfig;
import com.gitee.sop.servercommon.exception.ServiceException;
import com.gitee.sop.servercommon.result.ServiceResultBuilder;
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.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* 全局异常处理
* @author tanghc
*/
public interface GlobalExceptionHandler {
@ControllerAdvice
@Slf4j
public class GlobalExceptionHandler {
/**
* 与网关约定好的状态码,表示业务出错
*/
public static final int BIZ_ERROR_CODE = 4000;
/**
* 系统错误
*/
public static final int SYSTEM_ERROR_CODE = 5050;
public static final String X_SERVICE_ERROR_CODE = "x-service-error-code";
@RequestMapping("/error")
@ResponseBody
public Object error(HttpServletRequest request, HttpServletResponse response) {
ServiceResultBuilder serviceResultBuilder = ServiceConfig.getInstance().getServiceResultBuilder();
return serviceResultBuilder.buildError(request, response, new ServiceException("系统繁忙"));
}
/**
* 捕获手动抛出的异常
* @param request
* @param response
* @param exception
* @return
*/
@ExceptionHandler(ServiceException.class)
@ResponseBody
public Object serviceExceptionHandler(HttpServletRequest request, HttpServletResponse response, Exception exception) {
response.addHeader(X_SERVICE_ERROR_CODE, String.valueOf(BIZ_ERROR_CODE));
return this.processError(request, response, exception);
}
/**
* 捕获未知异常
* @param request
* @param response
* @param exception
* @return
*/
@ExceptionHandler(Exception.class)
@ResponseBody
public Object exceptionHandler(HttpServletRequest request, HttpServletResponse response, Exception exception) {
response.addHeader(X_SERVICE_ERROR_CODE, String.valueOf(SYSTEM_ERROR_CODE));
log.error("系统错误", exception);
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("<br> at ").append(stackTraceElement.toString());
}
response.setHeader("x-service-error-message", msg.toString());
return this.processError(request, response, new ServiceException("系统繁忙"));
}
/**
* 处理异常
*
* @param request
* @param response
* @param exception
* @return 返回最终结果
*/
protected Object processError(HttpServletRequest request, HttpServletResponse response, Exception exception) {
ServiceResultBuilder serviceResultBuilder = ServiceConfig.getInstance().getServiceResultBuilder();
return serviceResultBuilder.buildError(request, response, exception);
}
}

View File

@@ -37,7 +37,7 @@ public class SpringMvcServiceConfiguration {
@Bean
GlobalExceptionHandler globalExceptionHandler() {
return ServiceConfig.getInstance().getGlobalExceptionHandler();
return new GlobalExceptionHandler();
}
@PostConstruct

View File

@@ -3,6 +3,7 @@ package com.gitee.sop.servercommon.manager;
import com.gitee.sop.servercommon.bean.ServiceApiInfo;
import com.gitee.sop.servercommon.route.ServiceRouteInfo;
import com.gitee.sop.servercommon.util.OpenUtil;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.env.Environment;
@@ -17,6 +18,7 @@ import javax.servlet.http.HttpServletResponse;
* @author tanghc
*/
@Slf4j
@Getter
@RestController
public class ServiceRouteController {
@@ -35,6 +37,10 @@ public class ServiceRouteController {
log.error("签名验证失败, params:{}", request.getQueryString());
return null;
}
return getServiceRouteInfo(request, response);
}
protected ServiceRouteInfo getServiceRouteInfo(HttpServletRequest request, HttpServletResponse response) {
String serviceId = environment.getProperty("spring.application.name");
ApiMetaBuilder apiMetaBuilder = new ApiMetaBuilder();
ServiceApiInfo serviceApiInfo = apiMetaBuilder.getServiceApiInfo(serviceId, requestMappingHandlerMapping);
@@ -42,5 +48,4 @@ public class ServiceRouteController {
return serviceRouteInfoBuilder.build(serviceApiInfo);
}
}

View File

@@ -0,0 +1,9 @@
package com.gitee.sop.servercommon.manager;
import org.springframework.web.bind.annotation.RequestMapping;
/**
* @author tanghc
*/
public interface ServiceRouteInfoHandler {
}

View File

@@ -1,7 +1,6 @@
package com.gitee.sop.servercommon.route;
import com.alibaba.fastjson.annotation.JSONField;
import com.gitee.sop.servercommon.bean.ServiceConstants;
import lombok.Data;
import java.util.Date;
@@ -12,10 +11,10 @@ import java.util.List;
*/
@Data
public class ServiceRouteInfo {
private static final String SOP_SERVICE_ROUTE_PATH = ServiceConstants.SOP_SERVICE_ROUTE_PATH;
private static final String SOP_SERVICE_TEMP_PATH = ServiceConstants.SOP_SERVICE_TEMP_PATH;
/** 服务名称对应spring.application.name */
/**
* 服务名称对应spring.application.name
*/
private String serviceId;
@JSONField(format = "yyyy-MM-dd HH:mm:ss")
@@ -26,36 +25,12 @@ public class ServiceRouteInfo {
private String description;
/** 路由信息md5md5(sort(routeIdList)) */
/**
* 路由信息md5md5(sort(routeIdList))
*/
private String md5;
@JSONField(serialize = false)
private List<GatewayRouteDefinition> routeDefinitionList;
/**
* 返回zookeeper路径
* @return 返回zookeeper路径
*/
@JSONField(serialize = false)
public String getZookeeperPath() {
return SOP_SERVICE_ROUTE_PATH + '/' + serviceId;
}
/**
* 返回zookeeper路径
* @return 返回zookeeper路径
*/
@JSONField(serialize = false)
public String getZookeeperTempServiceIdPath() {
return SOP_SERVICE_TEMP_PATH + '/' + serviceId;
}
/**
* 返回zookeeper路径
* @return 返回zookeeper路径
*/
@JSONField(serialize = false)
public String getZookeeperTempServiceIdChildPath() {
return getZookeeperTempServiceIdPath() + "/" + serviceId;
}
}

View File

@@ -1,17 +1,13 @@
server.port=3333
spring.application.name=book-service
spring.application.description=book服务
# nacos注册中心
spring.cloud.nacos.discovery.server-addr=127.0.0.1:8848
# zookeeper配置
spring.cloud.zookeeper.connect-string=localhost:2181
# zipkin服务跟踪
spring.zipkin.base-url=http://127.0.0.1:9411/
# 设置sleuth收集信息的比率默认0.1最大是1数字越大越耗性能
spring.sleuth.sampler.probability=1
# dubbo使用zipkin过滤器
dubbo.provider.filter=tracing
dubbo.consumer.filter=tracing
#spring.zipkin.base-url=http://127.0.0.1:9411/
## 设置sleuth收集信息的比率默认0.1最大是1数字越大越耗性能
#spring.sleuth.sampler.probability=1
## dubbo使用zipkin过滤器
#dubbo.provider.filter=tracing
#dubbo.consumer.filter=tracing

View File

@@ -1,13 +1,9 @@
server.port=2222
spring.application.name=story-service
spring.application.description=story服务
# nacos注册中心
spring.cloud.nacos.discovery.server-addr=127.0.0.1:8848
# zookeeper配置
spring.cloud.zookeeper.connect-string=localhost:2181
# dubbo配置
dubbo.protocol.name=dubbo
dubbo.protocol.port=12345

View File

@@ -100,31 +100,6 @@ public class DbEnvGrayManager extends DefaultEnvGrayManager {
@PostConstruct
protected void after() throws Exception {
/*ZookeeperContext.setEnvironment(environment);
String isvChannelPath = ZookeeperContext.getServiceGrayChannelPath();
ZookeeperContext.listenPath(isvChannelPath, nodeCache -> {
String nodeData = new String(nodeCache.getCurrentData().getData());
ChannelMsg channelMsg = JSON.parseObject(nodeData, ChannelMsg.class);
String data = channelMsg.getData();
ServiceGrayDefinition userKeyDefinition = JSON.parseObject(data, ServiceGrayDefinition.class);
String serviceId = userKeyDefinition.getServiceId();
switch (channelMsg.getOperation()) {
case "set":
ConfigGray configGray = configGrayMapper.getByColumn("service_id", serviceId);
this.setServiceGrayConfig(configGray);
break;
case "open":
openGray(userKeyDefinition.getInstanceId(), serviceId);
break;
case "close":
closeGray(userKeyDefinition.getInstanceId());
break;
default:
}
});*/
// nacos
ConfigService configService = nacosConfigProperties.configServiceInstance();
configService.addListener(NacosConfigs.DATA_ID_GRAY, NacosConfigs.GROUP_CHANNEL, new AbstractListener() {
@Override

View File

@@ -41,26 +41,6 @@ public class DbIPBlacklistManager extends DefaultIPBlacklistManager {
@PostConstruct
protected void after() throws Exception {
/*ZookeeperContext.setEnvironment(environment);
String path = ZookeeperContext.getIpBlacklistChannelPath();
ZookeeperContext.listenPath(path, nodeCache -> {
String nodeData = new String(nodeCache.getCurrentData().getData());
ChannelMsg channelMsg = JSON.parseObject(nodeData, ChannelMsg.class);
final IPDto ipDto = JSON.parseObject(channelMsg.getData(), IPDto.class);
String ip = ipDto.getIp();
switch (channelMsg.getOperation()) {
case "add":
log.info("添加IP黑名单ip:{}", ip);
add(ip);
break;
case "delete":
log.info("移除IP黑名单ip:{}", ip);
remove(ip);
break;
default:
}
});*/
// nacos
ConfigService configService = nacosConfigProperties.configServiceInstance();
configService.addListener(NacosConfigs.DATA_ID_IP_BLACKLIST, NacosConfigs.GROUP_CHANNEL, new AbstractListener() {
@Override

View File

@@ -46,25 +46,6 @@ public class DbIsvManager extends CacheIsvManager {
@PostConstruct
protected void after() throws Exception {
ApiConfig.getInstance().setIsvManager(this);
/*ZookeeperContext.setEnvironment(environment);
String isvChannelPath = ZookeeperContext.getIsvInfoChannelPath();
ZookeeperContext.listenPath(isvChannelPath, nodeCache -> {
String nodeData = new String(nodeCache.getCurrentData().getData());
ChannelMsg channelMsg = JSON.parseObject(nodeData, ChannelMsg.class);
final IsvDefinition isvDefinition = JSON.parseObject(channelMsg.getData(), IsvDefinition.class);
switch (channelMsg.getOperation()) {
case "update":
log.info("更新ISV信息isvDefinition:{}", isvDefinition);
update(isvDefinition);
break;
case "remove":
log.info("删除ISVisvDefinition:{}", isvDefinition);
remove(isvDefinition.getAppKey());
break;
default:
}
});*/
ConfigService configService = nacosConfigProperties.configServiceInstance();
configService.addListener(NacosConfigs.DATA_ID_ISV, NacosConfigs.GROUP_CHANNEL, new AbstractListener() {

View File

@@ -127,42 +127,6 @@ public class DbIsvRoutePermissionManager extends DefaultIsvRoutePermissionManage
@PostConstruct
protected void after() throws Exception {
/*ZookeeperContext.setEnvironment(environment);
String isvChannelPath = ZookeeperContext.getIsvRoutePermissionChannelPath();
ZookeeperContext.listenPath(isvChannelPath, nodeCache -> {
String nodeData = new String(nodeCache.getCurrentData().getData());
ChannelMsg channelMsg = JSON.parseObject(nodeData, ChannelMsg.class);
final IsvRoutePermission isvRoutePermission = JSON.parseObject(channelMsg.getData(), IsvRoutePermission.class);
switch (channelMsg.getOperation()) {
case "reload":
log.info("重新加载路由权限信息isvRoutePermission:{}", isvRoutePermission);
String listenPath = isvRoutePermission.getListenPath();
String code = "";
try {
load();
} catch (Exception e) {
log.error("重新加载路由权限失败, channelMsg:{}", channelMsg, e);
code = e.getMessage();
}
try {
ZookeeperContext.updatePath(listenPath, code);
} catch (Exception e1) {
log.error("重新加载路由权限信息, zookeeper操作失败 path: {}", listenPath, e1);
}
break;
case "update":
log.info("更新ISV路由权限信息isvRoutePermission:{}", isvRoutePermission);
update(isvRoutePermission);
break;
case "remove":
log.info("删除ISV路由权限信息isvRoutePermission:{}", isvRoutePermission);
remove(isvRoutePermission.getAppKey());
break;
default:
}
});*/
ConfigService configService = nacosConfigProperties.configServiceInstance();
configService.addListener(NacosConfigs.DATA_ID_ROUTE_PERMISSION, NacosConfigs.GROUP_CHANNEL, new AbstractListener() {
@Override

View File

@@ -53,25 +53,6 @@ public class DbLimitConfigManager extends DefaultLimitConfigManager {
@PostConstruct
protected void after() throws Exception {
/*ZookeeperContext.setEnvironment(environment);
String path = ZookeeperContext.getLimitConfigChannelPath();
ZookeeperContext.listenPath(path, nodeCache -> {
String nodeData = new String(nodeCache.getCurrentData().getData());
ChannelMsg channelMsg = JSON.parseObject(nodeData, ChannelMsg.class);
final ConfigLimitDto configLimitDto = JSON.parseObject(channelMsg.getData(), ConfigLimitDto.class);
switch (channelMsg.getOperation()) {
case "reload":
log.info("重新加载限流配置信息configLimitDto:{}", configLimitDto);
load();
break;
case "update":
log.info("更新限流配置信息configLimitDto:{}", configLimitDto);
update(configLimitDto);
break;
default:
}
});*/
ConfigService configService = nacosConfigProperties.configServiceInstance();
configService.addListener(NacosConfigs.DATA_ID_LIMIT_CONFIG, NacosConfigs.GROUP_CHANNEL, new AbstractListener() {
@Override

View File

@@ -77,25 +77,6 @@ public class DbRouteConfigManager extends DefaultRouteConfigManager {
@PostConstruct
protected void after() throws Exception {
/*ZookeeperContext.setEnvironment(environment);
String path = ZookeeperContext.getRouteConfigChannelPath();
ZookeeperContext.listenPath(path, nodeCache -> {
String nodeData = new String(nodeCache.getCurrentData().getData());
ChannelMsg channelMsg = JSON.parseObject(nodeData, ChannelMsg.class);
final RouteConfig routeConfig = JSON.parseObject(channelMsg.getData(), RouteConfig.class);
switch (channelMsg.getOperation()) {
case "reload":
log.info("重新加载路由配置信息routeConfigDto:{}", routeConfig);
load();
break;
case "update":
log.info("更新路由配置信息routeConfigDto:{}", routeConfig);
update(routeConfig);
break;
default:
}
});*/
ConfigService configService = nacosConfigProperties.configServiceInstance();
configService.addListener(NacosConfigs.DATA_ID_ROUTE_CONFIG, NacosConfigs.GROUP_CHANNEL, new AbstractListener() {
@Override

View File

@@ -6,12 +6,9 @@ spring.application.name=api-gateway
mysql.username=root
mysql.password=root
# eureka注册中心地址
eureka.url=http://localhost:1111/eureka/
# zookeeper地址
zookeeper.url=localhost:2181
# nacos地址
nacos.url=127.0.0.1:8848
# zipkin服务监控地址没有开启不用改
zipkin.url=http://127.0.0.1:9411/
@@ -40,9 +37,6 @@ ribbon.OkToRetryOnAllOperations=false
# nacos cloud配置
spring.cloud.nacos.discovery.server-addr=${nacos.url}
# zookeeper配置
spring.cloud.zookeeper.connect-string=${zookeeper.url}
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/sop?useUnicode=true&characterEncoding=utf-8&zeroDateTimeBehavior=convertToNull
spring.datasource.username=${mysql.username}

View File

@@ -1,16 +0,0 @@
package com.gitee.sop.websiteserver.bean;
/**
* @author tanghc
*/
public class WebsiteConstants {
/**
* zookeeper存放接口路由信息的根目录
*/
public static final String SOP_SERVICE_ROUTE_PATH = "/com.gitee.sop.route";
/**
* 服务临时节点
*/
public static final String SOP_SERVICE_TEMP_PATH = "/com.gitee.sop.service.tmp";
}

View File

@@ -2,11 +2,8 @@ server.port=8083
spring.application.name=website-server
# ------- 需要改的配置 -------
# zookeeper地址
zookeeper.url=localhost:2181
# nacos地址
nacos.url=127.0.0.1:8848
# ------- 需要改的配置end -------
# nacos cloud配置