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做如下配置后:

yaml 复制代码
shenyu:
  register:
    registerType: http
    serverLists: 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: true
    protocol: http://
    type: nacos
    serverList: ${NACOS_ADDR:88.100.29.241:8848}
    registerPath: ${SHENYU_REGISTER_PATH:ems_admin_dev}
    props:
      nacosNameSpace: gateway
      groupName: DEFAULT_GROUP
      username: ${NACOS_USERNAME:nacos}
      password: ${NACOS_PASSWORD:nacos}

可自动创建Divide插件:

前文出现的,默认3次,默认3秒,估计在ShenYu源码里配置化的。

源码如下:

java 复制代码

自动配置原理:有待进一步调研。

Sa-Token

ShenYu可集成Sa-Token。

点击右侧的Edit:

配置Sa-Token使用的Redis地址。

注册

有些服务使用IP注册。

无法自动注册的服务,如Python服务,使用应用名:

如何实现??

问题

错误码

404

请求服务A接口正常:

请求服务B报错404:

解决方法:重启服务B。

408

某个接口请求报错,信息如下:

json 复制代码
{
    "code": 408,
    "message": "Request timeout, the maximum number of retry times has been exceeded"
}

排查此请求全链路涉及到的所有服务,检查retryCounttimeout配置

配置超时次数和时间:

记住上图的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次请求。

附录:

groovy 复制代码
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.JdbcTemplate

class SendMail {
	@Autowired
	JdbcTemplate 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_position
	                FROM ems.energy_consumption_analysis
	                WHERE acquisition_month = month(now()) - 1
	                GROUP BY device_position
	                ORDER 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

通过网关请求某个服务时报错:

json 复制代码
{
    "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 space
    at 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 space
    at 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服务)上做任何配置化操作。。。

groovy 复制代码
bootRun {
    ignoreExitValue true
    jvmArgs('-Dspring.output.ansi.enabled=always',
            '-noverify',
            '-XX:TieredStopAtLevel=1',
            '-Xmx1024m')
    sourceResources sourceSets.main
}
yaml 复制代码
logging:
  level:
    root: info
    org.apache.shenyu.bonuspoint: info
    org.apache.shenyu.lottery: info
    org.apache.shenyu: debug
    org.apache.shenyu.plugin.sync.data.websocket.client.ShenyuWebsocketClient: INFO
    org.apache.shenyu.plugin.base.AbstractShenyuPlugin: WARN
    org.apache.shenyu.plugin.api.ShenyuPlugin: debug
yaml 复制代码
org.apache.shenyu.plugin.sync.data.websocket.client.ShenyuWebsocketClient: INFO
org.apache.shenyu.plugin.base.AbstractShenyuPlugin: WARN

参考/推荐阅读

相关推荐
未来影子2 天前
Spring Ai Alibaba Gateway 实现存量应用转 MCP 工具
gateway·springai·mcp
亲爱的非洲野猪2 天前
Spring Cloud Gateway介绍 - -基础概念,简单工作原理和配置示例
java·spring boot·gateway
你喜欢喝可乐吗?4 天前
RuoYi-Cloud ruoyi-gateway 网关模块
java·spring cloud·gateway
lwb_01189 天前
SpringCloud——Gateway新一代网关
spring·spring cloud·gateway
你是人间五月天9 天前
gateway断言配置详解
gateway
weixin_387545649 天前
深入解析 AI Gateway:新一代智能流量控制中枢
人工智能·gateway
yuluo_YX23 天前
AI Gateway 介绍
人工智能·gateway
网硕互联的小客服1 个月前
502 Bad Gateway:服务器作为网关或代理时收到无效响应处理方式
运维·服务器·gateway
sevevty-seven1 个月前
什么是Gateway
gateway