diff --git a/changelog.md b/changelog.md index de11209d..e81dc56c 100644 --- a/changelog.md +++ b/changelog.md @@ -7,6 +7,7 @@ - zuul和gateway网关二合一,可随意切换 - 精简配置文件 - 优化文档中心页面 +- 优化接口限流 ## 2.5.10 diff --git a/doc/docs/_sidebar.md b/doc/docs/_sidebar.md index b48ca6d2..c1076802 100644 --- a/doc/docs/_sidebar.md +++ b/doc/docs/_sidebar.md @@ -1,37 +1,38 @@ -* [首页](/?t=1579169719974) +* [首页](/?t=1579512230184) * 开发文档 - * [快速体验](files/10010_快速体验.md?t=1579169719976) - * [项目接入到SOP](files/10011_项目接入到SOP.md?t=1579169719993) - * [新增接口](files/10020_新增接口.md?t=1579169719993) - * [开发流程](files/10021_开发流程.md?t=1579169719993) - * [业务参数校验](files/10030_业务参数校验.md?t=1579169719993) - * [错误处理](files/10040_错误处理.md?t=1579169719993) - * [编写文档](files/10041_编写文档.md?t=1579169719993) - * [接口交互详解](files/10050_接口交互详解.md?t=1579169719993) - * [easyopen支持](files/10070_easyopen支持.md?t=1579169719993) - * [使用签名校验工具](files/10080_使用签名校验工具.md?t=1579169719993) - * [ISV管理](files/10085_ISV管理.md?t=1579169719994) - * [自定义返回结果](files/10087_自定义返回结果.md?t=1579169719994) - * [自定义过滤器](files/10088_自定义过滤器.md?t=1579169719994) - * [自定义校验token](files/10089_自定义校验token.md?t=1579169719994) - * [路由授权](files/10090_路由授权.md?t=1579169719994) - * [接口限流](files/10092_接口限流.md?t=1579169719994) - * [监控日志](files/10093_监控日志.md?t=1579169719994) - * [SDK开发](files/10095_SDK开发.md?t=1579169719994) - * [使用SpringCloudGateway](files/10096_使用SpringCloudGateway.md?t=1579169719994) - * [应用授权](files/10097_应用授权.md?t=1579169719994) - * [提供restful接口](files/10100_提供restful接口.md?t=1579169719995) - * [文件上传](files/10104_文件上传.md?t=1579169719995) - * [配置Sleuth链路追踪](files/10109_配置Sleuth链路追踪.md?t=1579169719995) - * [预发布灰度发布](files/10110_预发布灰度发布.md?t=1579169719995) - * [动态修改请求参数](files/10111_动态修改请求参数.md?t=1579169719995) - * [使用eureka](files/10112_使用eureka.md?t=1579169719995) - * [扩展其它注册中心](files/10113_扩展其它注册中心.md?t=1579169719995) + * [快速体验](files/10010_快速体验.md?t=1579512230186) + * [项目接入到SOP](files/10011_项目接入到SOP.md?t=1579512230207) + * [新增接口](files/10020_新增接口.md?t=1579512230208) + * [开发流程](files/10021_开发流程.md?t=1579512230208) + * [业务参数校验](files/10030_业务参数校验.md?t=1579512230208) + * [错误处理](files/10040_错误处理.md?t=1579512230208) + * [编写文档](files/10041_编写文档.md?t=1579512230208) + * [接口交互详解](files/10050_接口交互详解.md?t=1579512230208) + * [easyopen支持](files/10070_easyopen支持.md?t=1579512230208) + * [使用签名校验工具](files/10080_使用签名校验工具.md?t=1579512230209) + * [ISV管理](files/10085_ISV管理.md?t=1579512230209) + * [自定义返回结果](files/10087_自定义返回结果.md?t=1579512230209) + * [自定义过滤器](files/10088_自定义过滤器.md?t=1579512230209) + * [自定义校验token](files/10089_自定义校验token.md?t=1579512230209) + * [路由授权](files/10090_路由授权.md?t=1579512230209) + * [接口限流](files/10092_接口限流.md?t=1579512230209) + * [监控日志](files/10093_监控日志.md?t=1579512230209) + * [SDK开发](files/10095_SDK开发.md?t=1579512230210) + * [使用SpringCloudGateway](files/10096_使用SpringCloudGateway.md?t=1579512230210) + * [应用授权](files/10097_应用授权.md?t=1579512230210) + * [提供restful接口](files/10100_提供restful接口.md?t=1579512230210) + * [文件上传](files/10104_文件上传.md?t=1579512230210) + * [配置Sleuth链路追踪](files/10109_配置Sleuth链路追踪.md?t=1579512230210) + * [预发布灰度发布](files/10110_预发布灰度发布.md?t=1579512230211) + * [动态修改请求参数](files/10111_动态修改请求参数.md?t=1579512230211) + * [使用eureka](files/10112_使用eureka.md?t=1579512230211) + * [扩展其它注册中心](files/10113_扩展其它注册中心.md?t=1579512230211) * 原理分析 - * [原理分析之@ApiMapping](files/90010_原理分析之@ApiMapping.md?t=1579169719995) - * [原理分析之如何存储路由](files/90011_原理分析之如何存储路由.md?t=1579169719995) - * [原理分析之如何路由](files/90012_原理分析之如何路由.md?t=1579169719995) - * [原理分析之文档归纳](files/90013_原理分析之文档归纳.md?t=1579169719996) - * [原理分析之预发布灰度发布](files/90014_原理分析之预发布灰度发布.md?t=1579169719996) - * [2.x升3.x注意事项](files/90099_2.x升3.x注意事项.md?t=1579169719996) - * [常见问题](files/90100_常见问题.md?t=1579169719996) + * [原理分析之@ApiMapping](files/90010_原理分析之@ApiMapping.md?t=1579512230211) + * [原理分析之如何存储路由](files/90011_原理分析之如何存储路由.md?t=1579512230211) + * [原理分析之如何路由](files/90012_原理分析之如何路由.md?t=1579512230211) + * [原理分析之文档归纳](files/90013_原理分析之文档归纳.md?t=1579512230211) + * [原理分析之预发布灰度发布](files/90014_原理分析之预发布灰度发布.md?t=1579512230211) + * [2.x升3.x注意事项](files/90099_2.x升3.x注意事项.md?t=1579512230212) + * [常见问题](files/90100_常见问题.md?t=1579512230212) + * [网关性能测试](files/90999_网关性能测试.md?t=1579512230212) diff --git a/doc/docs/files/90999_网关性能测试.md b/doc/docs/files/90999_网关性能测试.md new file mode 100644 index 00000000..38c47d0e --- /dev/null +++ b/doc/docs/files/90999_网关性能测试.md @@ -0,0 +1,112 @@ +# 网关性能测试 + +**测试环境** + +> 注意:记得关闭限流功能 + +- 测试工具:[wrk](https://github.com/wg/wrk),[安装教程](https://www.cnblogs.com/quanxiaoha/p/10661650.html) +- 服务器:CentOS7(虚拟机,宿主机:macbookpro),内存:2G,CPU: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 +``` + +以上配置仅针对zuul,Spring 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×tamp=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具有优势。 \ No newline at end of file diff --git a/sop-common/sop-bridge-zuul/src/main/resources/sop-bridge.properties b/sop-common/sop-bridge-zuul/src/main/resources/sop-bridge.properties index 7f4699ad..4f95afd1 100644 --- a/sop-common/sop-bridge-zuul/src/main/resources/sop-bridge.properties +++ b/sop-common/sop-bridge-zuul/src/main/resources/sop-bridge.properties @@ -25,10 +25,11 @@ spring.datasource.password=${mysql.password} # https://blog.csdn.net/qq_36872046/article/details/81058045 # 路由转发超时时间,毫秒,默认值1000,详见:RibbonClientConfiguration.DEFAULT_READ_TIMEOUT。 # 如果微服务端 处理时间过长,会导致ribbon read超时,解决办法将这个值调大一点 -ribbon.ReadTimeout=2000 +ribbon.ReadTimeout=5000 # 设置为true(默认false),则所有请求都重试,默认只支持get请求重试 # 请谨慎设置,因为post请求大多都是写入请求,如果要支持重试,确保服务的幂等性 ribbon.OkToRetryOnAllOperations=false +hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds=5000 # 不用改 mybatis.fill.com.gitee.fastmybatis.core.support.DateFillInsert=gmt_create diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/manager/AbstractConfiguration.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/manager/AbstractConfiguration.java index d2a7c7ac..ff8dd619 100644 --- a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/manager/AbstractConfiguration.java +++ b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/manager/AbstractConfiguration.java @@ -18,8 +18,11 @@ import com.gitee.sop.gatewaycommon.secret.IsvManager; import com.gitee.sop.gatewaycommon.session.SessionManager; import com.gitee.sop.gatewaycommon.validate.SignConfig; import com.gitee.sop.gatewaycommon.validate.Validator; +import lombok.extern.slf4j.Slf4j; import org.springframework.beans.BeansException; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.ApplicationArguments; +import org.springframework.boot.ApplicationRunner; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.cloud.client.discovery.event.HeartbeatEvent; @@ -37,11 +40,20 @@ import org.springframework.web.cors.UrlBasedCorsConfigurationSource; import org.springframework.web.filter.CorsFilter; import javax.annotation.PostConstruct; +import java.util.concurrent.locks.Condition; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReentrantLock; /** * @author tanghc */ -public class AbstractConfiguration implements ApplicationContextAware { +@Slf4j +public class AbstractConfiguration implements ApplicationContextAware, ApplicationRunner { + + private Lock lock = new ReentrantLock(); + private Condition condition = lock.newCondition(); + + private volatile boolean isStartupCompleted; @Autowired protected Environment environment; @@ -63,6 +75,17 @@ public class AbstractConfiguration implements ApplicationContextAware { */ @EventListener(classes = HeartbeatEvent.class) public void listenNacosEvent(ApplicationEvent heartbeatEvent) { + // 没有启动完毕先等待 + if (!isStartupCompleted) { + lock.lock(); + try { + condition.await(); + } catch (InterruptedException e) { + log.error("condition.await() error", e); + } finally { + lock.unlock(); + } + } registryListener.onEvent(heartbeatEvent); } @@ -200,10 +223,22 @@ public class AbstractConfiguration implements ApplicationContextAware { return corsConfiguration; } + @Override + public void run(ApplicationArguments args) throws Exception { + this.isStartupCompleted = true; + lock.lock(); + condition.signal(); + lock.unlock(); + after(); + } + @PostConstruct - public final void after() { + private void post() { EnvironmentContext.setEnvironment(environment); SpringContext.setApplicationContext(applicationContext); + } + + public final void after() { if (RouteRepositoryContext.getRouteRepository() == null) { throw new IllegalArgumentException("RouteRepositoryContext.setRouteRepository()方法未使用"); } @@ -219,7 +254,6 @@ public class AbstractConfiguration implements ApplicationContextAware { initMessage(); initBeanInitializer(); doAfter(); - } protected void initBeanInitializer() { diff --git a/sop-test/src/test/java/com/gitee/sop/test/AlipayClientPostTest.java b/sop-test/src/test/java/com/gitee/sop/test/AlipayClientPostTest.java index 67d80f96..fa3c93e2 100644 --- a/sop-test/src/test/java/com/gitee/sop/test/AlipayClientPostTest.java +++ b/sop-test/src/test/java/com/gitee/sop/test/AlipayClientPostTest.java @@ -56,7 +56,6 @@ public class AlipayClientPostTest extends TestBase { System.out.println("----------- 请求信息 -----------"); System.out.println("请求参数:" + buildParamQuery(params)); - System.out.println("URL参数:" + buildUrlQuery(params)); System.out.println("商户秘钥:" + privateKey); String content = AlipaySignature.getSignContent(params); System.out.println("待签名内容:" + content); @@ -64,6 +63,7 @@ public class AlipayClientPostTest extends TestBase { System.out.println("签名(sign):" + sign); params.put("sign", sign); + System.out.println("URL参数:" + buildUrlQuery(params)); System.out.println("----------- 返回结果 -----------"); String responseData = get(url, params);// 发送请求