This commit is contained in:
tanghc
2020-01-21 14:03:27 +08:00
138 changed files with 2969 additions and 1529 deletions

View File

@@ -3,7 +3,7 @@
> 运行环境JDK8Maven3[Nacos](https://nacos.io/zh-cn/docs/what-is-nacos.html)Mysql
- 安装并启动Nacos[安装教程](https://nacos.io/zh-cn/docs/quick-start.html)
- 执行Mysql脚本`sop.sql`
- 执行Mysql脚本`sop.sql`(Mysql版本5.6+)5.6以下运行`sop-mysql5.6以下版本.sql`
- IDE安装lombok插件然后打开项目(IDEA下可以打开根pom.xml然后open as project)
- 启动网关打开sop-gateway下的`application-dev.properties`
1. 修改数据库`username/password`
@@ -22,4 +22,28 @@
登录账号admin/123456
## 启动文档中心
文档中心代码在sop-website工程中
- 确保注册中心、网关、微服务正常启动
- 修改sop-website下的application-dev.properties相关配置
- 运行WebsiteServerApplication.java
- 访问http://localhost:8083
## 基本配置
`sop-gateway`下的application-dev.properties配置各项配置说明如下
```properties
# 忽略验证设置true则所有接口不会进行签名校验默认false
sop.api-config.ignore-validate=false
# 是否对结果进行合并默认true
sop.api-config.merge-result=true
# 显示返回sign默认true
sop.api-config.show-return-sign=true
# 是否开启限流功能默认true
sop.api-config.open-limit=true
# 请求超时时间默认5分钟即允许在5分钟内重复请求默认300
sop.api-config.timeout-seconds=300
```

View File

@@ -1,92 +1,16 @@
# 使用SpringCloudGateway
SOP默认网关是使用Spring Cloud Zuul您也可以切换成Spring Cloud Gateway完整代码见`spring-cloud-gateway`分支。
如果您的系统并发量不大建议使用zuul因为zuul的功能更全面有新功能会优先实现在zuul上。
- SOP中 Spring Cloud Zuul 和 Spring Cloud Gateway功能对比
| 功能 | Spring Cloud Zuul | Spring Cloud Gateway |
| ----- | ---- | ----------------------- |
| 签名验证|√ | √ |
| 统一异常处理|√ | √ |
| 统一返回内容|√ | √ |
| session管理|√ | √ |
| 秘钥管理|√ | √ |
| 微服务端自动验证JSR-303|√ | √ |
| 文件上传|√ | √ |
| 文件下载|√ | √ |
| 接口限流|√ | √ |
| 文档整合|√ | √ |
| 应用授权|√ | √ |
| 监控日志|√ | √ |
| 支持nacos|√ | √ |
| 网关动态修改参数|√ | √ |
使用Spring Cloud Gateway步骤如下
- 打开sop-gateway/pom.xml注释spring cloud zuul依赖打开Spring Cloud Gateway依赖
修改`sop-gateway/pom.xml`配置artifactId部分改成`sop-bridge-gateway`即可
```xml
<!-- ↓↓↓ 使用spring cloud zuul ↓↓↓
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-zuul</artifactId>
<groupId>com.gitee.sop</groupId>
<!-- 使用zuul作为网关 -->
<!--<artifactId>sop-bridge-zuul</artifactId>-->
<!-- 使用spring cloud gateway作为网关 -->
<artifactId>sop-bridge-gateway</artifactId>
<version>对应版本</version>
</dependency>
-->
<!-- ↑↑↑ 使用spring cloud zuul ↑↑↑ -->
<!-- ↓↓↓ 使用spring cloud gateway处于beta阶段推荐使用zuul ↓↓↓ -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<!-- ↑↑↑ 使用spring cloud gateway ↑↑↑ -->
```
- 打开启动类`SopGatewayApplication.java`, 注释zuul相关注解
```java
package com.gitee.sop.gateway;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
//import org.springframework.cloud.netflix.zuul.EnableZuulProxy;
// 开启网关功能
//@EnableZuulProxy
@SpringBootApplication
public class SopGatewayApplication {
public static void main(String[] args) {
SpringApplication.run(SopGatewayApplication.class, args);
}
}
```
- 禁用ZuulConfig类注释掉@Configuration注解即可
```java
//@Configuration
public class ZuulConfig extends AlipayZuulConfiguration {...}
```
- 启用GatewayConfig类打开@Configuration注释
```java
@Configuration
public class GatewayConfig extends AlipayGatewayConfiguration {...}
```
修改完毕重启sop-gateway

View File

@@ -4,43 +4,60 @@
## 使用预发布
假设网关工程在阿里云负载均衡有两台服务器,域名分别为
SOP中预发布的思路如下
假设网关工程sop-gateway在阿里云负载均衡有两台服务器域名分别为
|域名|说明|
|:---- |:---- |
|open1.domain.com |网关服务器1 |
|openpre.domain.com | 网关服务器2作为预发布请求入口|
线上网关入口为`http://open.domain.com/api`,请求网关`http://open.domain.com/api`会负载均衡到这两台服务器
SLB对外域名为`open.domain.com`,即开放平台入口为:`http://open.domain.com`
访问`open.domain.com`会负载均衡到`open1.domain.com``openpre.domain.com`这两台实例
如果单独从`openpre.domain.com`访问,则会访问到预发布微服务。
SOP开启预发布步骤如下
修改网关工程配置文件,指定预发布域名
```properties
# 预发布网关域名
# 预发布网关域名,多个用英文逗号(,)隔开
pre.domain=openpre.domain.com
```
重启网关
微服务启动参数添加:`--spring.cloud.nacos.discovery.metadata.env=pre`eureka下是`--eureka.instance.metadata-map.env=pre`)。
建议线上配两套启动脚本,其中预发布启动脚本添加启动参数`--eureka.instance.metadata-map.env=pre`
登录SOP-Admin在服务列表中点击预发布然后预发布请求地址变成`http://openpre.domain.com/api`
`openpre.domain.com`请求进来的用户都会进预发布服务器,其它情况都走非预发布服务器。
微服务启动参数添加:`--spring.cloud.nacos.discovery.metadata.env=pre`eureka下是`--eureka.instance.metadata-map.env=pre`
建议线上配两套启动脚本,其中预发布启动脚本添加启动参数`--spring.cloud.nacos.discovery.metadata.env=pre`
登录SOP-Admin在服务列表中点击预发布。
`openpre.domain.com`请求进来的用户都会进预发布服务器从SLB域名进来请求路由到非预发服务器
## 使用灰度发布
灰度发布可允许指定的用户访问灰度服务器,其它用户还是走正常流程。
微服务启动参数添加:`--spring.cloud.nacos.discovery.metadata.env=gray`eureka下是`--eureka.instance.metadata-map.env=gray`
登录SOP-Admin前往`服务列表`
登录SOP-Admin前往服务列表。
- 先设置灰度参数,指定灰度用户和灰度接口
- 先设置灰度参数指定灰度appId和灰度接口
- 服务器实例开启灰度
参考类:
- PreEnvGrayFilter.java
- EnvironmentServerChooser.java
- LoadBalanceServerChooser.java 预发布/灰度发布服务实例选择
### 自定义判断灰度用户
默认根据`appId``IP`来判断灰度用户如果要通过其它维度来判断是否是灰度用户可实现GrayUserBuilder接口
然后在springboot main方法中调用如下方法
```java
ApiConfig.getInstance().addGrayUserBuilder(new XXGrayUserBuilder());
```
参考com.gitee.sop.gatewaycommon.loadbalancer.builder.AppIdGrayUserBuilder.java

View File

@@ -0,0 +1,92 @@
# 原理分析之预发布灰度发布
SOP网关采用`自定义负载均衡策略`来实现对预发布/灰度发布服务器实例的选择。
spring cloud gateway默认的负载均衡实现类在:`org.springframework.cloud.gateway.filter.LoadBalancerClientFilter.java`
这个类主要做了几件事情:
1. 解析出请求路径中的scheme
2. 如果scheme不是以`lb`协议开头直接跳过
3. 如果scheme以`lb`协议开头,则说明需要进行负载均衡,选出一台微服务实例
4.`lb`协议解析成`http://ip:port`,继续向下请求
其中第4步是由`org.springframework.cloud.netflix.ribbon.RibbonLoadBalancerClient`来完成的,
我们只要分别继承`LoadBalancerClientFilter``RibbonLoadBalancerClient`,然后重写其中的方法就能完成自定义负载均衡。
SOP中的重写类是`SopLoadBalancerClientFilter``SopLoadBalancerClient`,核心代码委托给了`LoadBalanceServerChooser`处理,核心代码如下:
```java
/**
* 选择服务器
*
* @param serviceId serviceId仅gateway网关有作用
* @param exchange 请求上下文
* @param loadBalancer loadBalancer
* @param superChooser 父类默认的选择
* @param serverChooserFunction 执行选择操作
* @return 返回服务器实例没有选到则返回null
*/
public R choose(
String serviceId
, T exchange
, ILoadBalancer loadBalancer
, Supplier<R> superChooser
, Function<List<Server>, R> serverChooserFunction) {
// 获取所有服务实例
List<Server> servers = loadBalancer.getReachableServers();
// 存放预发服务器
List<Server> preServers = new ArrayList<>(4);
// 存放灰度发布服务器
List<Server> grayServers = new ArrayList<>(4);
// 存放非预发服务器
List<Server> notPreServers = new ArrayList<>(4);
for (Server server : servers) {
// 获取实例metadata
Map<String, String> metadata = getMetadata(serviceId, server);
// 是否开启了预发模式
if (this.isPreServer(metadata)) {
preServers.add(server);
} else if (this.isGrayServer(metadata)) {
grayServers.add(server);
} else {
notPreServers.add(server);
}
}
notPreServers.addAll(grayServers);
// 如果没有开启预发布服务和灰度发布,直接用默认的方式
if (preServers.isEmpty() && grayServers.isEmpty()) {
return superChooser.get();
}
// 如果是从预发布域名访问过来,则认为是预发布请求,选出预发服务器
if (this.isRequestFromPreDomain(exchange)) {
return serverChooserFunction.apply(preServers);
}
// 如果是灰度请求,则认为是灰度用户,选出灰度服务器
if (this.isRequestGrayServer(exchange)) {
return serverChooserFunction.apply(grayServers);
}
// 到这里说明不能访问预发/灰度服务器,则需要路由到非预发服务器
// 注意这里允许走灰度服务器如果不允许走注释notPreServers.addAll(grayServers);这行
return serverChooserFunction.apply(notPreServers);
}
```
其业务逻辑如下:
1. 选出`serviceId`对应的所有服务器实例
2. 将服务器实例进行分类,分别放进`预发布List``灰度List``非预发布List`
3. 如果`预发布List``灰度List`都为空,表示没有开启任何预发/灰度服务,直接使用父类的负载均衡策略
4. 如果是从预发布域名访问过来,则认为是预发布请求,选出预发服务器
5. 如果是灰度请求,则认为是灰度用户,选出灰度服务器
6. 最后剩下的是正常用户,正常用户不能走预发环境
## 参考类
- com.gitee.sop.gatewaycommon.gateway.filter.SopLoadBalancerClientFilter
- com.gitee.sop.gatewaycommon.gateway.loadbalancer.SopLoadBalancerClient
- com.gitee.sop.gatewaycommon.gateway.loadbalancer.GatewayLoadBalanceServerChooser

View File

@@ -0,0 +1,23 @@
# 2.x升3.x注意事项
升级到3.x后`本地访问接口`方式会有不同。
```java
@ApiMapping(value = "alipay.story.get")
public StoryResult getStory(StoryParam param) {
StoryResult story = new StoryResult();
story.setId(1L);
story.setName("海底小纵队(alipay.story.get1.0), port:" + environment.getProperty("server.port") + ", param:" + param);
return story;
}
```
- 2.x版本访问方式
`http://localhost:2222/alipay.story.get/?name=Jim&version=1.0`
- 3.x版本访问方式
`http://localhost:2222/alipay.story.get/1.0/?name=Jim`
3.x版本中把版本号融合在了url中如果这个功能没有用到可以放心升级。

View File

@@ -0,0 +1,112 @@
# 网关性能测试
**测试环境**
> 注意:记得关闭限流功能
- 测试工具:[wrk](https://github.com/wg/wrk)[安装教程](https://www.cnblogs.com/quanxiaoha/p/10661650.html)
- 服务器CentOS7虚拟机宿主机macbookpro内存2GCPU:1核数2核
- 运行环境Java8、Mysql-5.7、Nacos-1.1.3
- 运行参数:`-verbose:gc -XX:+PrintGCDetails -XX:+PrintHeapAtGC -Xloggc:gc-zuul.log \
-XX:MetaspaceSize=128m -XX:MaxMetaspaceSize=128m -Xms1024m -Xmx1024m -Xmn512m -Xss256k -XX:SurvivorRatio=8\
-XX:+UseConcMarkSweepGC`
- zuul配置
```properties
# 不校验时间,这样一个链接可以一直进行测试
sop.api-config.timeout-seconds=0
sop.restful.enable=true
logging.level.com.gitee=info
# zuul调优
zuul.host.max-per-route-connections=5000
zuul.host.max-total-connections=5000
zuul.semaphore.max-semaphores=5000
ribbon.ReadTimeout=5000
hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds=13000
logging.file=sop-gateway.log
```
以上配置仅针对zuulSpring Cloud Gateway没有做优化配置
CentOS允许最大连接数
```
$ ulimit -n
65535
```
## 调用开放接口
- wrk命令
```
wrk -t8 -c200 -d30s "http://10.1.31.227:8081/?charset=utf-8&biz_content=%7B%22name%22%3A%22%E8%91%AB%E8%8A%A6%E5%A8%83%22%2C%22id%22%3A%221%22%7D&method=alipay.story.get&format=json&sign=RjK%2FThnzAJQOw%2BfoVLS18PgWZAR%2B25SI2XdONFhS%2BmS8vsv2jNT3rygFoh%2ByX1AJbMgIEfcBzkQyqrs29jjd5dcwHVkcf7vxXshyfcEgl0fbMF6Ihycnz7rqSqkW3lzAWx4NuWUfkPnTX8Ffuf%2BhYRaI0NCpNv%2FV300HvsvmUjS6ZzS4YHaT1peSq0agfUhwRPd97aYMnUwRZDzxNfc5wuXA7OQ1o%2FPYIuIb%2FajVfwNP5ysitc%2FKtYEqt9rNAuzkcFmsw71d2RRnrPLsDN%2BuBXnIEh482f%2FbMj2Rj4%2FMq%2B0PEtlTRbg3rYnxyfowymfX%2BNmI4gNRUt70D4a%2FL3Qiug%3D%3D&app_id=2019032617262200001&sign_type=RSA2&version=1.0&timestamp=2020-01-19+13%3A34%3A12"
```
- Spring Cloud Gateway测试结果
```
8 threads and 200 connections
Thread Stats Avg Stdev Max +/- Stdev
Latency 139.74ms 69.39ms 617.14ms 69.82%
Req/Sec 182.12 55.74 343.00 66.24%
43391 requests in 30.09s, 11.96MB read
Requests/sec: 1441.96
Transfer/sec: 406.96KB
```
- Spring Cloud Zuul测试结果
```
8 threads and 200 connections
Thread Stats Avg Stdev Max +/- Stdev
Latency 230.14ms 331.27ms 2.00s 86.98%
Req/Sec 141.69 51.04 323.00 66.99%
33945 requests in 30.09s, 9.88MB read
Socket errors: connect 0, read 0, write 0, timeout 385
Requests/sec: 1128.05
Transfer/sec: 336.15KB
```
## 调用restful请求
- wrk命令
```
wrk -t8 -c200 -d30s "http://10.1.31.227:8081/rest/story-service/food/getFoodById?id=2"
```
线程数为 8模拟 200 个并发请求,持续 30 秒
- Spring Cloud Gateway测试结果
```
8 threads and 200 connections
Thread Stats Avg Stdev Max +/- Stdev
Latency 120.14ms 58.30ms 513.85ms 67.47%
Req/Sec 210.47 54.26 770.00 69.37%
50301 requests in 30.10s, 7.53MB read
Requests/sec: 1670.97
Transfer/sec: 256.21KB
```
- Spring Cloud Zuul测试结果
```
8 threads and 200 connections
Thread Stats Avg Stdev Max +/- Stdev
Latency 185.86ms 285.65ms 1.99s 88.55%
Req/Sec 167.75 55.60 460.00 68.05%
40070 requests in 30.09s, 6.65MB read
Socket errors: connect 0, read 0, write 0, timeout 466
Requests/sec: 1331.81
Transfer/sec: 226.50KB
```
综上所述Spring Cloud Gateway在没有优化的情况下压测表现比zuul好但zuul的数据表现也不差但是出现超时现象总的来说还是Spring Cloud Gateway具有优势。