ShenYu实战、问题记录
概述
一款高性能的国产的Apache开源API网关,官方文档。
在ShenYu v2.6.1, ShenYu注册中心只支持http类型,中间件注册类型已经被移除。 所以,请使用http注册类型来注册你的服务。不是微服务注册中心,它只是将元数据、选择器数据、规则数据注册到shenyu-admin,shenyu-admin将数据同步到shenyu-gateway。
插件
选择器和规则:一个插件有多个选择器,一个选择器对应多种规则。选择器相当于是对流量的一级筛选,规则就是最终的筛选。
包括:
- HTTP Process:
- ContextPath:上下文路径;
- ModifyResponse:响应修改;
- ParamMapping:
- Redirect:
- Request:
- Rewrite:
- Proxy:
- Divide:
- Dubbo:
- gRPC:
- Motan:
- Mqtt:
- Sofa:
- Spring Cloud:
- Tars:腾讯开源RPC框架;
- Tcp:
- WebSocket:
- Fault-Tolerance:容错插件
- Hystrix:
- RateLimiter:
- Resilience4j:
- Sentinel:
- Security:
- Casdoor:
- CryptorRequest:
- CryptorResponse:
- JWT:
- OAuth2:
- Sign:
- Waf:
- Observability:
- Aliyun SLS:
- Logging-ElasticSearch:
- Logging-Huawei LTS:
- Logging-Kafka:
- Logging:
- Logging-Pulsar:
- Logging-RabbitMQ:
- Logging-RocketMQ:
- Logging-Tencent-Cls:
- Metrics:
- Common:目前仅有一个General-Context:
- Cache:目前仅有一个Cache:
- Mock:目前仅有一个Mock:
- AI:
- AI Prompt:
- AI Proxy:
- AI TokenLimiter:
参考插件中心
选择器和规则
在PluginList->Proxy->Divide
页面,新增Selector时,可快速配置:
同样地,对于Rules,也可快速配置:
自动配置
在Nacos做如下配置后:
shenyu:register:registerType: httpserverLists: http://${GATEWAY_ADMIN_ADDR:88.100.29.241:9095}props:username: ${GATEWAY_ADMIN_USERNAME:admin}password: ${GATEWAY_ADMIN_PASSWORD:123456}client:http:props:contextPath: ${SHENYU_CONTEXT_PATH:ems_admin_dev}host: ${POD_IP:8.130.15.145}discovery:enable: trueprotocol: http://type: nacosserverList: ${NACOS_ADDR:88.100.29.241:8848}registerPath: ${SHENYU_REGISTER_PATH:ems_admin_dev}props:nacosNameSpace: gatewaygroupName: DEFAULT_GROUPusername: ${NACOS_USERNAME:nacos}password: ${NACOS_PASSWORD:nacos}
可自动创建Divide插件:
前文出现的,默认3次,默认3秒,估计在ShenYu源码里配置化的。
源码如下:
自动配置原理:有待进一步调研。
Sa-Token
ShenYu可集成Sa-Token。
点击右侧的Edit:
配置Sa-Token使用的Redis地址。
注册
有些服务使用IP注册。
无法自动注册的服务,如Python服务,使用应用名:
如何实现??
问题
错误码
404
请求服务A接口正常:
请求服务B报错404:
解决方法:重启服务B。
408
某个接口请求报错,信息如下:
{"code": 408,"message": "Request timeout, the maximum number of retry times has been exceeded"
}
排查此请求全链路涉及到的所有服务,检查retryCount
和timeout
配置
配置超时次数和时间:
记住上图的headerMaxSize和requestMaxSize,后面还会提到。
邮件发送4次
还是上面这个retryCount
配置导致的问题,不过报错码并不是408,也没有直接且明确地告诉你:Request timeout, the maximum number of retry times has been exceeded
。
因此排查起来略微花费一点时间。
背景:postman请求接口,模拟定时任务执行,定时任务是判断收件人,抄送人,开启状态,查询MySQL数据库,获取Groovy脚本,解析Groovy脚本并执行,然后获取结果。脚本里写MySQL查询语句,获取查询结果后,作为附件发送出去。理论上只应该收到一封邮件,结果却几乎同一时间收到4次一模一样的邮件。
经过排查后,定位到ShenYu配置的超时次数是3次,超时时间为3秒;超时3次后,不再继续重试,即前前后后发起4次请求。
附录:
package com.tesla.admin.service
import cn.hutool.core.date.DateUtil
import com.baomidou.dynamic.datasource.toolkit.DynamicDataSourceContextHolder
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.jdbc.core.JdbcTemplateclass SendMail {@AutowiredJdbcTemplate jdbcTemplate// {// mainTitle: \'\', //主标题// mainContent: \'\', //主内容// subTitle: \'\', //副标题// listing: { //如果邮件是个列表}// table: { //如果邮件是个表格// name: \'\',// columns: [// {// title: \'\', //标题// width: \'\', //宽度// field: \'id\', //数据字段// }// ],// data: [// {// id: \'1\'// }// ]// }// detail: {// header: \'\',// body: \'\',// footer: \'\'// }// }def run() {DynamicDataSourceContextHolder.push("mysql1")try {def results = jdbcTemplate.queryForList("""SELECT device_positionFROM ems.energy_consumption_analysisWHERE acquisition_month = month(now()) - 1GROUP BY device_positionORDER BY abs(mom_rate) DESC;""")def tableDef = [name : \'异常详情\',columns: [[title: \'设备位置\', width: \'250\', field: \'device_position\']],data : results]return [mainTitle : \'尊敬的用户,您好!\',mainContent : "附件是上月(${DateUtil.month(new Date())}月份)内所有用能异常情况汇总,请您下载查阅。",table : tableDef, // 附件使用hideTableInBody : true, // 模板用以跳过渲染detail : [footer: \'系统自动发送,请勿回复。\'],]} catch (Exception e) {log.error("groovy run error: ${e.message}")} finally {DynamicDataSourceContextHolder.poll()}}
}
413
报错信息:Request entity too large 请求实体太大
。
出现此报错原因在于,ShenYu里配置过headerMaxSize和requestMaxSize,且该值过小。一般情况下,这两个值都是0,即没有限制。
解决方法:设置为0,或放大配置值。
-106
-107
通过网关请求某个服务时报错:
{"code": -107,"message": "divide:Can not find selector, please check your configuration!"
}
可能的原因:
- 该环境(dev、test或prod)没有发布过这个服务;
- 写错服务名字:
-119
请确认接口经过网关,且satoken插件配置正常
解决方法:
无法修改注册IP
在修改注册IP时遇到告警,无法修改:
解决方法:删除整个服务的配置信息,然后重新添加配置+(自动或手动)注册IP。
无法删除注册IP
正常情况下的服务注册
遇到的问题如下,找不到删除按钮:
Pod异常,ShenYu没有自动剔除该异常Pod的注册信息;因此,页面上看到的IP肯定有问题;流量请求到不存在的IP。但是无法删除。
解决方法:只能手动删除整个配置,然后重新配置。
至于为什么ShenYu没有自动剔除异常Pod注册的IP,这才是根本问题。
OOM
几乎(所有需要经过ShenYu网关)全部接口超时异常:
如上图所示,一般是20秒左右定义为【超时】。
排除掉阿里云ALB、网络、k8s集群等可能原因之后,将问题定位在ShenYu网关上。
生产环境,gateway pod设置为3副本:
逐个查看pod日志,发现其中只有一个pod有异常,且时间停留在17点01分:
具体的报错日志:
2025-07-02 10:08:13 [shenyu-netty-epoll-5] [traceId= spanId= sampled=] INFO com.tesla.gateway.plugin.SatokenPlugin - CheckAndRemoveKeySet: conversation_id:44b81c59-3a26-480c-bb66-ba44a6ee2692, step: false
Exception in thread "AsyncAppender-Worker-ASYNC_FILE" Exception in thread "AsyncAppender-Worker-ASYNC_STDOUT" java.lang.OutOfMemoryError: Java heap spaceat java.base/java.util.Arrays.copyOf(Arrays.java:3745)at java.base/java.lang.AbstractStringBuilder.ensureCapacityInternal(AbstractStringBuilder.java:172)at java.base/java.lang.AbstractStringBuilder.append(AbstractStringBuilder.java:538)at java.base/java.lang.StringBuilder.append(StringBuilder.java:178)at ch.qos.logback.core.pattern.FormattingConverter.write(FormattingConverter.java:39)at ch.qos.logback.core.pattern.CompositeConverter.convert(CompositeConverter.java:24)at ch.qos.logback.core.pattern.FormattingConverter.write(FormattingConverter.java:36)at ch.qos.logback.core.pattern.PatternLayoutBase.writeLoopOnConverters(PatternLayoutBase.java:115)at ch.qos.logback.classic.PatternLayout.doLayout(PatternLayout.java:165)at ch.qos.logback.classic.PatternLayout.doLayout(PatternLayout.java:39)at ch.qos.logback.core.encoder.LayoutWrappingEncoder.encode(LayoutWrappingEncoder.java:116)at ch.qos.logback.core.OutputStreamAppender.subAppend(OutputStreamAppender.java:230)at ch.qos.logback.core.OutputStreamAppender.append(OutputStreamAppender.java:102)at ch.qos.logback.core.UnsynchronizedAppenderBase.doAppend(UnsynchronizedAppenderBase.java:84)at ch.qos.logback.core.spi.AppenderAttachableImpl.appendLoopOnAppenders(AppenderAttachableImpl.java:51)at ch.qos.logback.core.AsyncAppenderBase$Worker.run(AsyncAppenderBase.java:290)
java.lang.OutOfMemoryError: Java heap spaceat java.base/java.util.Arrays.copyOf(Arrays.java:3745)at java.base/java.lang.AbstractStringBuilder.ensureCapacityInternal(AbstractStringBuilder.java:172)at java.base/java.lang.AbstractStringBuilder.append(AbstractStringBuilder.java:538)at java.base/java.lang.StringBuilder.append(StringBuilder.java:178)at ch.qos.logback.core.pattern.FormattingConverter.write(FormattingConverter.java:39)at ch.qos.logback.core.pattern.CompositeConverter.convert(CompositeConverter.java:24)at ch.qos.logback.core.pattern.FormattingConverter.write(FormattingConverter.java:36)at ch.qos.logback.core.pattern.PatternLayoutBase.writeLoopOnConverters(PatternLayoutBase.java:115)at ch.qos.logback.classic.PatternLayout.doLayout(PatternLayout.java:165)at ch.qos.logback.classic.PatternLayout.doLayout(PatternLayout.java:39)at ch.qos.logback.core.encoder.LayoutWrappingEncoder.encode(LayoutWrappingEncoder.java:116)at ch.qos.logback.core.OutputStreamAppender.subAppend(OutputStreamAppender.java:230)at ch.qos.logback.core.rolling.RollingFileAppender.subAppend(RollingFileAppender.java:235)at ch.qos.logback.core.OutputStreamAppender.append(OutputStreamAppender.java:102)at ch.qos.logback.core.UnsynchronizedAppenderBase.doAppend(UnsynchronizedAppenderBase.java:84)at ch.qos.logback.core.spi.AppenderAttachableImpl.appendLoopOnAppenders(AppenderAttachableImpl.java:51)at ch.qos.logback.core.AsyncAppenderBase$Worker.run(AsyncAppenderBase.java:290)
出问题的pod只有一个,但现象是gateway作为一个服务,可以接收新的请求,但是全部接口都超时。
观察Grafana面板,10点多有一个内存使用率的【非常小】的跃升,哪怕是跃升之后,内存使用也不过900多MiB:
k8s yaml文件定义如下:
限额是3G,远远没有达到k8s内存异常触发OOM自动重启的条件。
重启有问题的一个pod,接口请求报错:
{"code":-106,"message":"Can not find url, please check your configuration!"}
意思是gateway pod状态虽然是Running,但是得等pod加载配置成功。
出现-106,无需在页面(对应于gateway-admin服务)上做任何配置化操作。。。
bootRun {ignoreExitValue truejvmArgs('-Dspring.output.ansi.enabled=always','-noverify','-XX:TieredStopAtLevel=1','-Xmx1024m')sourceResources sourceSets.main
}
logging:level:root: infoorg.apache.shenyu.bonuspoint: infoorg.apache.shenyu.lottery: infoorg.apache.shenyu: debugorg.apache.shenyu.plugin.sync.data.websocket.client.ShenyuWebsocketClient: INFOorg.apache.shenyu.plugin.base.AbstractShenyuPlugin: WARNorg.apache.shenyu.plugin.api.ShenyuPlugin: debug
org.apache.shenyu.plugin.sync.data.websocket.client.ShenyuWebsocketClient: INFO
org.apache.shenyu.plugin.base.AbstractShenyuPlugin: WARN
- 可考虑将logback切换到性能更强的log4j2日志框架:
https://logging.apache.org/log4j/2.12.x/performance.html
参考/推荐阅读
- 从独立开发到进入Apache孵化器
- sign鉴权前置到网关
- Apache ShenYu:让API网关更简单