eureka
tanghc 5 years ago
commit 9830e3e17d
  1. 6
      README.md
  2. 16
      doc/docs/files/10010_快速体验.md
  3. 6
      doc/docs/files/10100_提供restful接口.md
  4. 59
      doc/docs/files/90011_原理分析之如何存储路由.md
  5. 12
      docker-build.sh
  6. 13
      docker-entrypoint.sh
  7. 16
      dockerfile
  8. 13
      pom.xml
  9. 4
      sop-admin/sop-admin-server/src/main/resources/META-INF/sop-admin.properties
  10. 2
      sop-admin/sop-admin-server/src/main/resources/public/index.html
  11. 2
      sop-admin/sop-admin-server/src/main/resources/public/static/js/chunk-25908fca.eac43cde.js
  12. 4
      sop-admin/sop-admin-vue/src/views/isv/index.vue
  13. 25
      sop-common/sop-bridge-eureka/.gitignore
  14. 25
      sop-common/sop-bridge-nacos/.gitignore
  15. 7
      sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/gateway/ServerWebExchangeUtil.java
  16. 7
      sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/gateway/filter/IndexFilter.java
  17. 17
      sop-example/sop-story/src/main/java/com/gitee/sop/storyweb/controller/Example1001_BaseController.java
  18. 3
      sop-example/sop-story/src/main/resources/application-dev.properties
  19. 10
      sop-gateway/pom.xml
  20. 54
      sop-gateway/src/main/java/com/gitee/sop/gateway/entity/ConfigRouteLimit.java
  21. 8
      sop-gateway/src/main/java/com/gitee/sop/gateway/manager/DbRouteConfigManager.java
  22. 11
      sop-gateway/src/main/java/com/gitee/sop/gateway/mapper/ConfigRouteLimitMapper.java
  23. 3
      sop-gateway/src/main/resources/META-INF/gateway.properties
  24. 24
      sop-mysql5.6以下版本.sql
  25. 24
      sop-test/src/test/java/com/gitee/sop/test/AllInOneTest.java
  26. 25
      sop.sql

@ -103,11 +103,11 @@ System.out.println(responseData);
## 界面预览
![服务列表](https://images.gitee.com/uploads/images/2019/0711/174825_2856281f_332975.png "服务列表")
![服务列表](https://images.gitee.com/uploads/images/2020/1016/134354_c1915902_332975.png "service.png")
![路由管理](https://images.gitee.com/uploads/images/2019/0711/174843_1648591c_332975.png "路由管理")
![路由管理](https://images.gitee.com/uploads/images/2020/1016/134039_bed1608d_332975.png "route.png")
![限流管理](https://images.gitee.com/uploads/images/2019/0711/174900_9315cc8e_332975.png "限流管理")
![限流管理](https://images.gitee.com/uploads/images/2020/1016/134102_f2dcfb25_332975.png "limit.png")
![秘钥信息](https://images.gitee.com/uploads/images/2019/0711/174921_bd817533_332975.png "秘钥信息")

@ -1,5 +1,7 @@
# 快速体验
## 方式1
> 运行环境:JDK8,Maven3,[Nacos](https://nacos.io/zh-cn/docs/what-is-nacos.html),Mysql
- 安装并启动Nacos,[安装教程](https://nacos.io/zh-cn/docs/quick-start.html)
@ -14,6 +16,20 @@
2. 运行`SopStoryApplication.java`
- 找到sop-test,运行`com.gitee.sop.test.AlipayClientPostTest.testGet`进行接口调用测试
## 方式2(docker)
> 前提:安装好docker
- 安装并启动Nacos,[安装教程](https://nacos.io/zh-cn/docs/quick-start.html)
- 执行Mysql脚本`sop.sql`(Mysql版本5.6+),5.6以下运行`sop-mysql5.6以下版本.sql`
- 打开`docker-entrypoint.sh`,修改mysql,nacos配置
- 执行`docker-build.sh`
- 找到sop-test,运行`com.gitee.sop.test.AlipayClientPostTest.testGet`进行接口调用测试
> - admin地址:http://ip:8082 登录账号:admin/123456
> - 文档地址:http://ip:8083/
## 使用admin
- 找到`sop-admin/sop-admin-server`工程,打开sop-admin-server下的`application-dev.properties`,修改相关配置

@ -6,8 +6,6 @@
`http://ip:port/rest/服务id/your_path`,其中`http://ip:port/rest/`为固定部分,后面跟微服务请求路径。
> 可在微服务端指定一个配置:`sop.restful.prefix=xxx`。请求路径将变成:`http://ip:port/rest/xxx/your_path`
下面是一个微服务的接口例子
```java
@ -36,6 +34,10 @@ public class TraditionalWebappController {
微服务之间的调用可以使用dubbo或feign,有了版本号的管理,可以做到服务的平滑升级,对用户来说都是无感知的。结合SOP-Admin提供的上下线功能,
可实现预发布环境功能。
默认情况下,`http://ip:port/rest/`为固定部分,如果想要更改`rest`,可在网关配置文件指定:`sop.restful.path=/aaa`
请求前缀将变成:`http://ip:port/aaa/`
- 封装请求工具【可选】
封装请求,方便调用,针对vue的封装如下:

@ -1,52 +1,37 @@
# 原理分析之如何存储路由
SOP基于spring cloud,因此会涉及到网关路由。但是开发者不用去配置文件定义路由的隐射关系,SOP自动帮你解决了这个问题。
SOP基于spring cloud,因此会涉及到网关路由。但是开发者不用去配置文件定义路由的隐射关系,SOP帮你解决了这个问题。
## 获取路由信息
首先明确一点,路由信息由各微服务提供,因此网关需要从注册中心获取各微服务实例,这个通过nacos提供的`watch`来实现。
当有新的微服务加入时,Nacos会触发一个事件推送,详见`NacosWatch.java`类
网关启动成后会触发一个事件,代码见:`com.gitee.sop.gatewaycommon.config.AbstractConfiguration.listenEvent`
```java
this.publisher.publishEvent(new HeartbeatEvent(this, this.nacosWatchIndex.getAndIncrement()));
```
这个事件会取拉取微服务中提供的路由信息
因此,只要注册了`HeartbeatEvent`事件就能随时感知到微服务实例的变化。如何注册呢,config类实现`ApplicationListener`接口即可。
下面以nacos为例,介绍拉取路由过程
```java
public class AbstractConfiguration implements ApplicationContextAware, ApplicationListener<HeartbeatEvent> {
@EventListener(classes = HeartbeatEvent.class)
public void listenEvent(ApplicationEvent heartbeatEvent) {
// 有服务注册进来
}
}
```
1.从nacos中获取微服务实例
然后,每个微服务提供一个restful接口,接口地址为:`http://ip:port/sop/routes`,用来返回自身路由信息。网关请求这个接口用来获取路由信息。
入口代码:`com.gitee.sop.bridge.route.NacosRegistryListener.onEvent`
加载路由伪代码如下:
2.拿到微服务信息,调用微服务提供的接口拉取路由数据
```java
@Override
public void onAddInstance(InstanceDefinition instance) {
String serviceName = instance.getServiceId();
String url = getRouteRequestUrl(instance);
log.info("拉取路由配置,serviceId: {}, url: {}", serviceName, url);
ResponseEntity<String> responseEntity = getRestTemplate().getForEntity(url, String.class);
if (responseEntity.getStatusCode() == HttpStatus.OK) {
String body = responseEntity.getBody();
ServiceRouteInfo serviceRouteInfo = JSON.parseObject(body, ServiceRouteInfo.class);
baseRouteCache.load(serviceRouteInfo, callback -> routesProcessor.saveRoutes(serviceRouteInfo, instance));
} else {
log.error("拉取路由配置异常,url: {}, status: {}, body: {}", url, responseEntity.getStatusCodeValue(), responseEntity.getBody());
}
}
```
入口代码:`com.gitee.sop.gatewaycommon.route.BaseRegistryListener.pullRoutes`
最终落实到:`com.gitee.sop.gatewaycommon.route.ServiceRouteListener.onAddInstance`
微服务提供一个url:`http://ip:port/sop/routes`,对应Controller在:`com.gitee.sop.servercommon.route.ServiceRouteController.listRoutes`
微服务找到被`@Open`注解的方法,然后封装成一个路由对象,放到List中,最后返回给网关。
3.网关拿到路由信息,经过处理,转化成网关路由配置
关联方法:
`com.gitee.sop.gatewaycommon.gateway.route.GatewayRouteCache.add`
参考代码:
`com.gitee.sop.gatewaycommon.gateway.route.GatewayRouteCache.refresh`
- `com.gitee.sop.gatewaycommon.route.RegistryListener`以及实现类
- `com.gitee.sop.gatewaycommon.route.ServiceRouteListener`
路由的存储方式是一个Map,key为路由id,即接口名+版本号。
@ -57,5 +42,5 @@ public void onAddInstance(InstanceDefinition instance) {
private static final Map<String, GatewayTargetRoute> routes = synchronizedMap(new LinkedHashMap<>());
```
因为客户端调用接口都会传递一个接口名和版本号,因此通过这两个字段能够很快查询出路由信息,进行路由转发操作。
因为客户端调用接口都会传递一个接口名和版本号,因此通过这两个字段能够很快查询出路由信息,然后进行路由转发操作。

@ -0,0 +1,12 @@
#!/bin/bash
# 打包
mvn clean package
# 创建镜像
docker build -t gitee.com/sop .
# 获取镜像id
image_id=`docker images gitee.com/sop --format "{{.ID}}" | awk '{print $1}'`
# 运行镜像
docker run --name sop -p 8081:8081 -p 8082:8082 -p 8083:8083 -p 8087:8087 -p 2222:2222 -d $image_id

@ -0,0 +1,13 @@
#!/bin/bash
JAVA_OPTS="-Xms128m -Xmx128m"
# mysql, nacos配置
args="--mysql.host=10.1.30.110:3306 --mysql.username=root --mysql.password=root --register.url=10.1.30.110:8848"
java $JAVA_OPTS -Djava.security.egd=file:/dev/./urandom -jar /sop/sop-gateway/sop-gateway.jar $args --logging.file.path=/sop/sop-gateway/log &
java $JAVA_OPTS -Djava.security.egd=file:/dev/./urandom -jar /sop/sop-admin/sop-admin.jar $args --logging.file.path=/sop/sop-admin/log &
java $JAVA_OPTS -Djava.security.egd=file:/dev/./urandom -jar /sop/sop-website/sop-website.jar $args --logging.file.path=/sop/sop-website/log &
java $JAVA_OPTS -Djava.security.egd=file:/dev/./urandom -jar /sop/sop-auth/sop-auth.jar $args --logging.file.path=/sop/sop-auth/log &
# 最后一条没有&
java $JAVA_OPTS -Djava.security.egd=file:/dev/./urandom -jar /sop/sop-story/sop-story.jar $args --logging.file.path=/sop/sop-story/log

@ -0,0 +1,16 @@
FROM java:8
VOLUME /tmp
VOLUME /sop
# 将所有应用放到一个镜像当中
ADD sop-gateway/target/*.jar sop/sop-gateway/sop-gateway.jar
ADD sop-admin/sop-admin-server/target/*.jar sop/sop-admin/sop-admin.jar
ADD sop-website/target/*.jar sop/sop-website/sop-website.jar
ADD sop-auth/target/*.jar sop/sop-auth/sop-auth.jar
ADD sop-example/sop-story/target/*.jar sop/sop-story/sop-story.jar
# 拷贝启动脚本
COPY docker-entrypoint.sh /usr/local/bin/
RUN chmod +x /usr/local/bin/docker-entrypoint.sh
ENTRYPOINT ["docker-entrypoint.sh"]

@ -237,19 +237,6 @@
<encoding>UTF-8</encoding>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-source-plugin</artifactId>
<version>2.2.1</version>
<executions>
<execution>
<id>attach-sources</id>
<goals>
<goal>jar-no-fork</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>

@ -27,6 +27,10 @@ spring.datasource.url=jdbc:mysql://${mysql.host}/sop?useUnicode=true&characterEn
spring.datasource.username=${mysql.username}
spring.datasource.password=${mysql.password}
#连接池
spring.datasource.hikari.pool-name=DatebookHikariCP
spring.datasource.hikari.max-lifetime=500000
# 固定不用改
mybatis.mapper-locations=classpath:mybatis/mapper/*.xml
easyopen.show-doc=false

@ -1 +1 @@
<!DOCTYPE html><html><head><meta charset=utf-8><meta http-equiv=X-UA-Compatible content="IE=edge,chrome=1"><meta name=viewport content="width=device-width,initial-scale=1,maximum-scale=1,user-scalable=no"><link rel=icon href=favicon.ico><title>SOP Admin</title><link href=static/css/chunk-elementUI.81cf475c.css rel=stylesheet><link href=static/css/chunk-libs.3dfb7769.css rel=stylesheet><link href=static/css/app.5c572c03.css rel=stylesheet></head><body><noscript><strong>We're sorry but SOP Admin doesn't work properly without JavaScript enabled. Please enable it to continue.</strong></noscript><div id=app></div><script>(function(e){function n(n){for(var r,c,a=n[0],f=n[1],i=n[2],d=0,l=[];d<a.length;d++)c=a[d],u[c]&&l.push(u[c][0]),u[c]=0;for(r in f)Object.prototype.hasOwnProperty.call(f,r)&&(e[r]=f[r]);h&&h(n);while(l.length)l.shift()();return o.push.apply(o,i||[]),t()}function t(){for(var e,n=0;n<o.length;n++){for(var t=o[n],r=!0,c=1;c<t.length;c++){var a=t[c];0!==u[a]&&(r=!1)}r&&(o.splice(n--,1),e=f(f.s=t[0]))}return e}var r={},c={runtime:0},u={runtime:0},o=[];function a(e){return f.p+"static/js/"+({}[e]||e)+"."+{"chunk-25908fca":"00a89f91","chunk-2c1f2e8f":"f092c0a0","chunk-2d0d32e7":"e7c489be","chunk-2d2085ef":"91d75f3c","chunk-2d221c34":"20057287","chunk-30c6c34f":"b288bbf5","chunk-4de1c2b6":"e74e3d03","chunk-73b2dcec":"60c5d8e9","chunk-9b31c83a":"494fc338","chunk-9f479afe":"2093f9d0","chunk-c3ce42fe":"9517b588"}[e]+".js"}function f(n){if(r[n])return r[n].exports;var t=r[n]={i:n,l:!1,exports:{}};return e[n].call(t.exports,t,t.exports,f),t.l=!0,t.exports}f.e=function(e){var n=[],t={"chunk-25908fca":1,"chunk-2c1f2e8f":1,"chunk-30c6c34f":1,"chunk-4de1c2b6":1,"chunk-73b2dcec":1,"chunk-9b31c83a":1,"chunk-c3ce42fe":1};c[e]?n.push(c[e]):0!==c[e]&&t[e]&&n.push(c[e]=new Promise(function(n,t){for(var r="static/css/"+({}[e]||e)+"."+{"chunk-25908fca":"a66354ec","chunk-2c1f2e8f":"0314067f","chunk-2d0d32e7":"31d6cfe0","chunk-2d2085ef":"31d6cfe0","chunk-2d221c34":"31d6cfe0","chunk-30c6c34f":"3b12267b","chunk-4de1c2b6":"a37cd815","chunk-73b2dcec":"ed391cc5","chunk-9b31c83a":"c4612b4a","chunk-9f479afe":"31d6cfe0","chunk-c3ce42fe":"6b789903"}[e]+".css",u=f.p+r,o=document.getElementsByTagName("link"),a=0;a<o.length;a++){var i=o[a],d=i.getAttribute("data-href")||i.getAttribute("href");if("stylesheet"===i.rel&&(d===r||d===u))return n()}var l=document.getElementsByTagName("style");for(a=0;a<l.length;a++){i=l[a],d=i.getAttribute("data-href");if(d===r||d===u)return n()}var h=document.createElement("link");h.rel="stylesheet",h.type="text/css",h.onload=n,h.onerror=function(n){var r=n&&n.target&&n.target.src||u,o=new Error("Loading CSS chunk "+e+" failed.\n("+r+")");o.code="CSS_CHUNK_LOAD_FAILED",o.request=r,delete c[e],h.parentNode.removeChild(h),t(o)},h.href=u;var s=document.getElementsByTagName("head")[0];s.appendChild(h)}).then(function(){c[e]=0}));var r=u[e];if(0!==r)if(r)n.push(r[2]);else{var o=new Promise(function(n,t){r=u[e]=[n,t]});n.push(r[2]=o);var i,d=document.createElement("script");d.charset="utf-8",d.timeout=120,f.nc&&d.setAttribute("nonce",f.nc),d.src=a(e),i=function(n){d.onerror=d.onload=null,clearTimeout(l);var t=u[e];if(0!==t){if(t){var r=n&&("load"===n.type?"missing":n.type),c=n&&n.target&&n.target.src,o=new Error("Loading chunk "+e+" failed.\n("+r+": "+c+")");o.type=r,o.request=c,t[1](o)}u[e]=void 0}};var l=setTimeout(function(){i({type:"timeout",target:d})},12e4);d.onerror=d.onload=i,document.head.appendChild(d)}return Promise.all(n)},f.m=e,f.c=r,f.d=function(e,n,t){f.o(e,n)||Object.defineProperty(e,n,{enumerable:!0,get:t})},f.r=function(e){"undefined"!==typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},f.t=function(e,n){if(1&n&&(e=f(e)),8&n)return e;if(4&n&&"object"===typeof e&&e&&e.__esModule)return e;var t=Object.create(null);if(f.r(t),Object.defineProperty(t,"default",{enumerable:!0,value:e}),2&n&&"string"!=typeof e)for(var r in e)f.d(t,r,function(n){return e[n]}.bind(null,r));return t},f.n=function(e){var n=e&&e.__esModule?function(){return e["default"]}:function(){return e};return f.d(n,"a",n),n},f.o=function(e,n){return Object.prototype.hasOwnProperty.call(e,n)},f.p="",f.oe=function(e){throw console.error(e),e};var i=window["webpackJsonp"]=window["webpackJsonp"]||[],d=i.push.bind(i);i.push=n,i=i.slice();for(var l=0;l<i.length;l++)n(i[l]);var h=d;t()})([]);</script><script src=static/js/chunk-elementUI.298ac98c.js></script><script src=static/js/chunk-libs.75deb05f.js></script><script src=static/js/app.f323bdd7.js></script></body></html>
<!DOCTYPE html><html><head><meta charset=utf-8><meta http-equiv=X-UA-Compatible content="IE=edge,chrome=1"><meta name=viewport content="width=device-width,initial-scale=1,maximum-scale=1,user-scalable=no"><link rel=icon href=favicon.ico><title>SOP Admin</title><link href=static/css/chunk-elementUI.81cf475c.css rel=stylesheet><link href=static/css/chunk-libs.3dfb7769.css rel=stylesheet><link href=static/css/app.5c572c03.css rel=stylesheet></head><body><noscript><strong>We're sorry but SOP Admin doesn't work properly without JavaScript enabled. Please enable it to continue.</strong></noscript><div id=app></div><script>(function(e){function n(n){for(var r,c,a=n[0],f=n[1],i=n[2],d=0,l=[];d<a.length;d++)c=a[d],u[c]&&l.push(u[c][0]),u[c]=0;for(r in f)Object.prototype.hasOwnProperty.call(f,r)&&(e[r]=f[r]);h&&h(n);while(l.length)l.shift()();return o.push.apply(o,i||[]),t()}function t(){for(var e,n=0;n<o.length;n++){for(var t=o[n],r=!0,c=1;c<t.length;c++){var a=t[c];0!==u[a]&&(r=!1)}r&&(o.splice(n--,1),e=f(f.s=t[0]))}return e}var r={},c={runtime:0},u={runtime:0},o=[];function a(e){return f.p+"static/js/"+({}[e]||e)+"."+{"chunk-25908fca":"eac43cde","chunk-2c1f2e8f":"f092c0a0","chunk-2d0d32e7":"e7c489be","chunk-2d2085ef":"91d75f3c","chunk-2d221c34":"20057287","chunk-30c6c34f":"b288bbf5","chunk-4de1c2b6":"e74e3d03","chunk-73b2dcec":"60c5d8e9","chunk-9b31c83a":"494fc338","chunk-9f479afe":"2093f9d0","chunk-c3ce42fe":"9517b588"}[e]+".js"}function f(n){if(r[n])return r[n].exports;var t=r[n]={i:n,l:!1,exports:{}};return e[n].call(t.exports,t,t.exports,f),t.l=!0,t.exports}f.e=function(e){var n=[],t={"chunk-25908fca":1,"chunk-2c1f2e8f":1,"chunk-30c6c34f":1,"chunk-4de1c2b6":1,"chunk-73b2dcec":1,"chunk-9b31c83a":1,"chunk-c3ce42fe":1};c[e]?n.push(c[e]):0!==c[e]&&t[e]&&n.push(c[e]=new Promise(function(n,t){for(var r="static/css/"+({}[e]||e)+"."+{"chunk-25908fca":"a66354ec","chunk-2c1f2e8f":"0314067f","chunk-2d0d32e7":"31d6cfe0","chunk-2d2085ef":"31d6cfe0","chunk-2d221c34":"31d6cfe0","chunk-30c6c34f":"3b12267b","chunk-4de1c2b6":"a37cd815","chunk-73b2dcec":"ed391cc5","chunk-9b31c83a":"c4612b4a","chunk-9f479afe":"31d6cfe0","chunk-c3ce42fe":"6b789903"}[e]+".css",u=f.p+r,o=document.getElementsByTagName("link"),a=0;a<o.length;a++){var i=o[a],d=i.getAttribute("data-href")||i.getAttribute("href");if("stylesheet"===i.rel&&(d===r||d===u))return n()}var l=document.getElementsByTagName("style");for(a=0;a<l.length;a++){i=l[a],d=i.getAttribute("data-href");if(d===r||d===u)return n()}var h=document.createElement("link");h.rel="stylesheet",h.type="text/css",h.onload=n,h.onerror=function(n){var r=n&&n.target&&n.target.src||u,o=new Error("Loading CSS chunk "+e+" failed.\n("+r+")");o.code="CSS_CHUNK_LOAD_FAILED",o.request=r,delete c[e],h.parentNode.removeChild(h),t(o)},h.href=u;var s=document.getElementsByTagName("head")[0];s.appendChild(h)}).then(function(){c[e]=0}));var r=u[e];if(0!==r)if(r)n.push(r[2]);else{var o=new Promise(function(n,t){r=u[e]=[n,t]});n.push(r[2]=o);var i,d=document.createElement("script");d.charset="utf-8",d.timeout=120,f.nc&&d.setAttribute("nonce",f.nc),d.src=a(e),i=function(n){d.onerror=d.onload=null,clearTimeout(l);var t=u[e];if(0!==t){if(t){var r=n&&("load"===n.type?"missing":n.type),c=n&&n.target&&n.target.src,o=new Error("Loading chunk "+e+" failed.\n("+r+": "+c+")");o.type=r,o.request=c,t[1](o)}u[e]=void 0}};var l=setTimeout(function(){i({type:"timeout",target:d})},12e4);d.onerror=d.onload=i,document.head.appendChild(d)}return Promise.all(n)},f.m=e,f.c=r,f.d=function(e,n,t){f.o(e,n)||Object.defineProperty(e,n,{enumerable:!0,get:t})},f.r=function(e){"undefined"!==typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},f.t=function(e,n){if(1&n&&(e=f(e)),8&n)return e;if(4&n&&"object"===typeof e&&e&&e.__esModule)return e;var t=Object.create(null);if(f.r(t),Object.defineProperty(t,"default",{enumerable:!0,value:e}),2&n&&"string"!=typeof e)for(var r in e)f.d(t,r,function(n){return e[n]}.bind(null,r));return t},f.n=function(e){var n=e&&e.__esModule?function(){return e["default"]}:function(){return e};return f.d(n,"a",n),n},f.o=function(e,n){return Object.prototype.hasOwnProperty.call(e,n)},f.p="",f.oe=function(e){throw console.error(e),e};var i=window["webpackJsonp"]=window["webpackJsonp"]||[],d=i.push.bind(i);i.push=n,i=i.slice();for(var l=0;l<i.length;l++)n(i[l]);var h=d;t()})([]);</script><script src=static/js/chunk-elementUI.298ac98c.js></script><script src=static/js/chunk-libs.75deb05f.js></script><script src=static/js/app.f323bdd7.js></script></body></html>

@ -1,8 +1,8 @@
<template>
<div class="app-container">
<el-form :inline="true" :model="searchFormData" class="demo-form-inline" size="mini">
<el-form-item label="appId">
<el-input v-model="searchFormData.appKey" :clearable="true" placeholder="appId" style="width: 250px;" />
<el-form-item label="AppId">
<el-input v-model="searchFormData.appKey" :clearable="true" placeholder="AppId" style="width: 250px;" />
</el-form-item>
<el-form-item>
<el-button type="primary" icon="el-icon-search" @click="onSearchTable">查询</el-button>

@ -0,0 +1,25 @@
/target/
!.mvn/wrapper/maven-wrapper.jar
### STS ###
.apt_generated
.classpath
.factorypath
.project
.settings
.springBeans
.sts4-cache
### IntelliJ IDEA ###
.idea
*.iws
*.iml
*.ipr
### NetBeans ###
/nbproject/private/
/nbbuild/
/dist/
/nbdist/
/.nb-gradle/
/build/

@ -0,0 +1,25 @@
/target/
!.mvn/wrapper/maven-wrapper.jar
### STS ###
.apt_generated
.classpath
.factorypath
.project
.settings
.springBeans
.sts4-cache
### IntelliJ IDEA ###
.idea
*.iws
*.iml
*.ipr
### NetBeans ###
/nbproject/private/
/nbbuild/
/dist/
/nbdist/
/.nb-gradle/
/build/

@ -44,7 +44,6 @@ public class ServerWebExchangeUtil {
private static final String THROWABLE_KEY = "sop.throwable";
private static final String UNKNOWN_PATH = "/sop/unknown";
private static final String REST_PATH = "/rest";
private static final FormHttpMessageConverter formHttpMessageConverter = new FormHttpMessageConverter();
@ -77,10 +76,10 @@ public class ServerWebExchangeUtil {
return ServerRequest.create(exchange, messageReaders);
}
public static String getRestfulPath(String path) {
int index = path.indexOf(REST_PATH);
public static String getRestfulPath(String path, String prefix) {
int index = path.indexOf(prefix);
// 取"/rest"的后面部分
return path.substring(index + REST_PATH.length());
return path.substring(index + prefix.length());
}
/**

@ -5,6 +5,7 @@ import com.gitee.sop.gatewaycommon.bean.SopConstants;
import com.gitee.sop.gatewaycommon.exception.ApiException;
import com.gitee.sop.gatewaycommon.gateway.ServerWebExchangeUtil;
import com.gitee.sop.gatewaycommon.gateway.route.GatewayForwardChooser;
import com.gitee.sop.gatewaycommon.manager.EnvironmentKeys;
import com.gitee.sop.gatewaycommon.param.ApiParam;
import com.gitee.sop.gatewaycommon.route.ForwardInfo;
import com.gitee.sop.gatewaycommon.util.RouteInterceptorUtil;
@ -44,8 +45,6 @@ import java.util.Objects;
@Order(Ordered.HIGHEST_PRECEDENCE)
public class IndexFilter implements WebFilter {
private static final String REST_PATH_PREFIX = "/rest";
/** 路径白名单 */
private static final List<String> PATH_WHITE_LIST = Arrays.asList(
"/sop", "/actuator"
@ -69,9 +68,9 @@ public class IndexFilter implements WebFilter {
return chain.filter(exchange);
}
// 如果是restful请求,直接转发
if (path.startsWith(REST_PATH_PREFIX)) {
if (path.startsWith(EnvironmentKeys.SOP_RESTFUL_PATH.getValue())) {
exchange.getAttributes().put(SopConstants.RESTFUL_REQUEST, true);
String restfulPath = ServerWebExchangeUtil.getRestfulPath(path);
String restfulPath = ServerWebExchangeUtil.getRestfulPath(path, EnvironmentKeys.SOP_RESTFUL_PATH.getValue());
ServerWebExchange newExchange = ServerWebExchangeUtil.getForwardExchange(exchange, restfulPath);
return chain.filter(newExchange);
}

@ -22,9 +22,11 @@ import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.servlet.http.HttpServletRequest;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Arrays;
@ -135,6 +137,19 @@ public class Example1001_BaseController {
return testResult;
}
// 获取header
@Open(value = "test.head",version = "1.0")
@GetMapping("/get/header/v1")
public StoryResult header(@RequestBody StoryParam story, HttpServletRequest request) {
HttpServletRequest servletRequest = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
StoryResult storyResult = new StoryResult();
storyResult.setId(1L);
storyResult.setName(story.getName()
+ ", token1:" + request.getHeader("token")
+ ", token2:" + servletRequest.getHeader("token"));
return storyResult;
}
// 返回数组结果
@ApiOperation(value = "返回数组结果(第二)", notes = "返回数组结果", position = -99)
@Open("story.list")

@ -1,5 +1,6 @@
server.port=2222
spring.application.name=story-service
register.url=http://localhost:1111/eureka/
# 注册中心
eureka.client.serviceUrl.defaultZone=http://localhost:1111/eureka/
eureka.client.serviceUrl.defaultZone=${register.url}

@ -59,6 +59,10 @@
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
</dependency>
<!-- test -->
<dependency>
@ -73,12 +77,6 @@
<version>1.18.4</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
<scope>provided</scope>
</dependency>
</dependencies>

@ -1,54 +0,0 @@
package com.gitee.sop.gateway.entity;
import lombok.Data;
import javax.persistence.Column;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;
import java.util.Date;
/**
* 表名config_route_limit
* 备注路由限流配置
*
* @author tanghc
*/
@Table(name = "config_route_limit")
@Data
public class ConfigRouteLimit {
@Id
@Column(name = "id")
@GeneratedValue(strategy = GenerationType.IDENTITY)
/** 数据库字段:id */
private Integer id;
/** 路由id, 数据库字段:route_id */
private String routeId;
/** 限流策略,1:窗口策略,2:令牌桶策略, 数据库字段:limit_type */
private Byte limitType;
/** 每秒可处理请求数, 数据库字段:exec_count_per_second */
private Integer execCountPerSecond;
/** 返回的错误码, 数据库字段:limit_code */
private String limitCode;
/** 返回的错误信息, 数据库字段:limit_msg */
private String limitMsg;
/** 令牌桶容量, 数据库字段:token_bucket_count */
private Integer tokenBucketCount;
/** 1:开启,0关闭, 数据库字段:limit_status */
private Byte limitStatus;
/** 数据库字段:gmt_create */
private Date gmtCreate;
/** 数据库字段:gmt_modified */
private Date gmtModified;
}

@ -1,6 +1,5 @@
package com.gitee.sop.gateway.manager;
import com.gitee.sop.gateway.mapper.ConfigRouteLimitMapper;
import com.gitee.sop.gateway.mapper.ConfigRouteMapper;
import com.gitee.sop.gatewaycommon.bean.ChannelMsg;
import com.gitee.sop.gatewaycommon.bean.RouteConfig;
@ -21,13 +20,10 @@ import java.util.List;
public class DbRouteConfigManager extends DefaultRouteConfigManager {
@Autowired
ConfigRouteLimitMapper configRouteLimitMapper;
private ConfigRouteMapper configRouteMapper;
@Autowired
ConfigRouteMapper configRouteMapper;
@Autowired
Environment environment;
private Environment environment;
@Override
public void load(String serviceId) {

@ -1,11 +0,0 @@
package com.gitee.sop.gateway.mapper;
import com.gitee.fastmybatis.core.mapper.CrudMapper;
import com.gitee.sop.gateway.entity.ConfigRouteLimit;
/**
* @author tanghc
*/
public interface ConfigRouteLimitMapper extends CrudMapper<ConfigRouteLimit, Integer> {
}

@ -18,6 +18,9 @@ spring.datasource.url=jdbc:mysql://${mysql.host}/sop?useUnicode=true&characterEn
spring.datasource.username=${mysql.username}
spring.datasource.password=${mysql.password}
#连接池
spring.datasource.hikari.pool-name=DatebookHikariCP
spring.datasource.hikari.max-lifetime=500000
spring.cloud.gateway.discovery.locator.lower-case-service-id=true
spring.cloud.gateway.discovery.locator.enabled=true

@ -9,7 +9,6 @@ DROP TABLE IF EXISTS `perm_role`;
DROP TABLE IF EXISTS `perm_isv_role`;
DROP TABLE IF EXISTS `isv_keys`;
DROP TABLE IF EXISTS `isv_info`;
DROP TABLE IF EXISTS `config_route_limit`;
DROP TABLE IF EXISTS `config_route_base`;
DROP TABLE IF EXISTS `config_limit`;
DROP TABLE IF EXISTS `config_ip_blacklist`;
@ -113,22 +112,6 @@ CREATE TABLE `config_route_base` (
) ENGINE=INNODB AUTO_INCREMENT=9 DEFAULT CHARSET=utf8 COMMENT='路由配置表';
CREATE TABLE `config_route_limit` (
`id` INT(11) UNSIGNED NOT NULL AUTO_INCREMENT,
`route_id` VARCHAR(64) NOT NULL COMMENT '路由id',
`service_id` VARCHAR(64) NOT NULL DEFAULT '',
`limit_type` TINYINT(4) NOT NULL DEFAULT '1' COMMENT '限流策略,1:窗口策略,2:令牌桶策略',
`exec_count_per_second` INT(11) DEFAULT NULL COMMENT '每秒可处理请求数',
`limit_code` VARCHAR(64) DEFAULT NULL COMMENT '返回的错误码',
`limit_msg` VARCHAR(100) DEFAULT NULL COMMENT '返回的错误信息',
`token_bucket_count` INT(11) DEFAULT NULL COMMENT '令牌桶容量',
`limit_status` TINYINT(4) NOT NULL DEFAULT '0' COMMENT '限流开启状态,1:开启,0关闭',
`gmt_create` DATETIME DEFAULT NULL,
`gmt_modified` DATETIME DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `uk_routeid` (`route_id`) USING BTREE
) ENGINE=INNODB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8 COMMENT='路由限流配置';
CREATE TABLE `isv_info` (
`id` BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT,
@ -241,13 +224,6 @@ INSERT INTO `config_route_base` (`id`, `route_id`, `status`, `gmt_create`, `gmt_
(8,'goods.add1.0',1,'2019-05-13 17:23:00','2019-05-13 17:23:11');
INSERT INTO `config_route_limit` (`id`, `route_id`, `service_id`, `limit_type`, `exec_count_per_second`, `limit_code`, `limit_msg`, `token_bucket_count`, `limit_status`, `gmt_create`, `gmt_modified`) VALUES
(1,'alipay.story.find1.0','story-service',1,10,'isp.service-busy','服务器正忙',NULL,1,'2019-04-10 20:55:08','2019-04-10 21:04:35'),
(2,'alipay.story.get1.0','story-service',2,5,'isp.service-busy','服务器正忙',5,0,'2019-04-10 21:05:51','2019-04-11 18:18:03'),
(3,'alipay.story.get1.2','story-service',2,10,'isp.service-busy','服务器正忙',3,1,'2019-04-11 17:26:04','2019-04-11 18:19:33'),
(4,'alipay.category.get1.0','story-service',1,5,'error-code','服务器忙',5,1,'2019-05-07 12:22:04','2019-05-07 12:25:06');
INSERT INTO `isv_info` (`id`, `app_key`, `status`, `sign_type`, `remark`, `gmt_create`, `gmt_modified`) VALUES
(1,'2019032617262200001',1,1,NULL,'2019-03-27 10:10:34','2019-05-09 11:10:38'),
(3,'asdfasdf',2,1,NULL,'2019-03-27 11:01:11','2019-05-11 10:45:01'),

@ -112,10 +112,16 @@ public class AllInOneTest extends TestBase {
*/
public void testJSR303() {
Client client = new Client(url, appId, privateKey);
Map<String, String> headers = new HashMap<>(8);
headers.put("accept-language", "zh-CN");
Client.RequestBuilder requestBuilder = new Client.RequestBuilder()
.method("goods.add")
.version("1.0")
.bizContent(new BizContent().add("goods_name", "iphone6").add("goods_remark", "iphone6").add("goods_comment", "1"))
.header(headers)
.bizContent(new BizContent()
.add("goods_name", "iphone6")
.add("goods_remark", "iphone6")
.add("goods_comment", "1"))
.httpMethod(HttpTool.HTTPMethod.POST)
.callback((requestInfo, responseData) -> {
System.out.println(responseData);
@ -218,6 +224,22 @@ public class AllInOneTest extends TestBase {
client.execute(requestBuilder);
}
/**
* 传递header
*/
public void testHeader() {
Map<String, String> header = new HashMap<>(8);
header.put("token", "aaaabbbb");
Client.RequestBuilder requestBuilder = new Client.RequestBuilder()
.method("test.head")
.version("1.0")
.header(header)
.bizContent(new BizContent().add("id", "1").add("name", "葫芦娃"))
.httpMethod(HttpTool.HTTPMethod.GET);
client.execute(requestBuilder);
}
/**
* 测试post提交大文本内容
* @throws IOException

@ -12,7 +12,6 @@ DROP TABLE IF EXISTS `perm_isv_role`;
DROP TABLE IF EXISTS `isv_keys`;
DROP TABLE IF EXISTS `isv_info`;
DROP TABLE IF EXISTS `config_service_route`;
DROP TABLE IF EXISTS `config_route_limit`;
DROP TABLE IF EXISTS `config_route_base`;
DROP TABLE IF EXISTS `config_limit`;
DROP TABLE IF EXISTS `config_ip_blacklist`;
@ -115,23 +114,6 @@ CREATE TABLE `config_route_base` (
) ENGINE=InnoDB AUTO_INCREMENT=9 DEFAULT CHARSET=utf8 COMMENT='路由配置表';
CREATE TABLE `config_route_limit` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`route_id` varchar(64) NOT NULL COMMENT '路由id',
`service_id` varchar(64) NOT NULL DEFAULT '',
`limit_type` tinyint(4) NOT NULL DEFAULT '1' COMMENT '限流策略,1:窗口策略,2:令牌桶策略',
`exec_count_per_second` int(11) DEFAULT NULL COMMENT '每秒可处理请求数',
`limit_code` varchar(64) DEFAULT NULL COMMENT '返回的错误码',
`limit_msg` varchar(100) DEFAULT NULL COMMENT '返回的错误信息',
`token_bucket_count` int(11) DEFAULT NULL COMMENT '令牌桶容量',
`limit_status` tinyint(4) NOT NULL DEFAULT '0' COMMENT '限流开启状态,1:开启,0关闭',
`gmt_create` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
`gmt_modified` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
UNIQUE KEY `uk_routeid` (`route_id`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8 COMMENT='路由限流配置';
CREATE TABLE `config_service_route` (
`id` varchar(128) NOT NULL DEFAULT '' COMMENT '路由id',
`service_id` varchar(128) NOT NULL DEFAULT '',
@ -244,13 +226,6 @@ INSERT INTO `config_route_base` (`id`, `route_id`, `status`, `gmt_create`, `gmt_
(8,'goods.add1.0',1,'2019-05-13 17:23:00','2019-05-13 17:23:11');
INSERT INTO `config_route_limit` (`id`, `route_id`, `service_id`, `limit_type`, `exec_count_per_second`, `limit_code`, `limit_msg`, `token_bucket_count`, `limit_status`, `gmt_create`, `gmt_modified`) VALUES
(1,'alipay.story.find1.0','story-service',1,10,'isp.service-busy','服务器正忙',NULL,1,'2019-04-10 20:55:08','2019-04-10 21:04:35'),
(2,'alipay.story.get1.0','story-service',2,5,'isp.service-busy','服务器正忙',5,0,'2019-04-10 21:05:51','2019-04-11 18:18:03'),
(3,'alipay.story.get1.2','story-service',2,10,'isp.service-busy','服务器正忙',3,1,'2019-04-11 17:26:04','2019-04-11 18:19:33'),
(4,'alipay.category.get1.0','story-service',1,5,'error-code','服务器忙',5,1,'2019-05-07 12:22:04','2019-05-07 12:25:06');
INSERT INTO `isv_info` (`id`, `app_key`, `status`, `sign_type`, `remark`, `gmt_create`, `gmt_modified`) VALUES
(1,'2019032617262200001',1,1,NULL,'2019-03-27 10:10:34','2019-05-09 11:10:38'),
(3,'asdfasdf',2,1,NULL,'2019-03-27 11:01:11','2019-05-11 10:45:01'),

Loading…
Cancel
Save