本文汇总梳理了 RPC 使用过程中遇到的常见问题及排查思路。
调用服务时报“RPC-02306: 没有获得服务 [{0}] 的调用地址,请检查服务是否已经推送”错误
排查思路如下:
检查服务地址是否推送。
检查客户端启动时是否收到 RPC Config 推送。
检查 RPC 服务端和客户端应用配置信息是否匹配。
检查服务注册中心连接。
运行以下命令以检查客户端和服务端与服务注册中心的连接情况:
9600 端口是服务注册中心的监听端口,客户端和服务端与 9600 端口建立长连接,向服务注册中心发布和订阅服务。如果客户端或者服务端与 9600 端口的连接断开,则需要重启应用恢复,并进一步排查端口异常断开的原因。
检查 RPC 服务端地址绑定。
查看进程启动参数 rpc_bind_network_interface 或 rpc_enabled_ip_range 是否绑定了正确的 IP 地址。
您可以通过如下步骤进行排查:
服务端业务代码执行慢。
服务端本身有外网服务调用,或者服务端又调用了其他 RPC 服务(client > RPC Server A > RPC Server B),此种情况需要分别排查 A 和 B,确定问题。
服务端有数据库操作,如数据库连接耗时、慢 SQL 等。
如因服务端本身原因导致超时,建议调整代码。
查看是否因服务端 RPC 线程池耗尽导致的超时。
查看是否因 GC 问题(Garbage Collection,简称 GC),导致线程停止。
某些 GC 类型会触发“stop the world”问题,会将所有线程挂起。若要排查是否是 GC 导致的超时问题,可以通过以下方法开启 GC 日志。
方法一:
在 config/java_opts 文件中加入以下启动参数,并重新打包发布。
方法二:
用 kill -15 命令结束服务端进程。
手动启动 RPC 服务。
运行 su admin 进入 admin 用户,用如下 nohup 形式启动 RPC 服务:
查看是否因网络延时抖动导致的超时。
您可以通过以下步骤排查:
在客户端和服务端运行 tsar -i 1 查看问题发生的时间点是否有网络重传。
在客户端和服务端同时部署 tcpdump 进行循环抓包,当问题发生后分析网络包。
在客户端和服务端运行 ping 观察是否存在网络延时。
确认是否因其他外部因素影响服务器性能,如任务调度、批处理,或者与宿主机上其他虚拟机、容器发生资源争抢。
可以参考以下示例语句打印调用 sofa2-rpc-server 的应用超过 3 秒的请求总数、服务端IP、服务应用和客户端IP:
实际使用时,请将 sofa2-rpc-server 替换成对应的服务端应用名称,并根据日志中处理时长所对应列的具体位置调整 $18 数值。打印信息也可以根据需要调整。
您可以根据以下几个情况进行排查:
应用非正常启动
通常可以查看 health-check 日志。如果有 error 日志,可以根据相关信息进行排查,常见的故障信息包括:
redis 没有正确配置。
一个服务在本地开启了多个实例。
Bolt 服务没有启动,并发现端口占用等。
注册中心问题
如果应用已经启动,但服务没有发布成功,则按下述步骤排查:
查看注册中心内有没有服务被注册, 如果没有,则排除注册中心的故障。
查看是否是 ACVIP 的问题。如果排除后,服务还有问题,按下述步骤排查:
通过命令判断当前注册中心是否正常,示例如下:curl -i -XPOST {antvip}:9003/antcloud/antvip/instances/get -d '{"vipDomainName2ChecksumMap":{"000001-DSR_CLOUD":"N"}}'。如果不正常,请检查注册中心是否配置正确。
服务提供方的运行模式
问题描述:
如何将 Dubbo 内部项目迁移到 SOFABoot 上?
如果第三方需要保有 Dubbo,系统要如何设计?
解决方案:
系统改造过程中,并不能确保所有关联系统一次性改造完成,会面临需要和历史系统兼容的场景。例如一个服务被改造成 SOFA Bolt 服务后,发现还有调用方依然是依赖 Dubbo 的。那么,一个简单的兼容办法为:这个服务同时暴露 BOLT 和 Dubbo 服务。
在 SOFABoot 中暴露 Dubbo 服务,步骤如下:
加入 Dubbo 的 starter 依赖。
示例如下:
示例如下:
添加 Dubbo 服务发布。
示例如下:
服务需要引入 Dubbo 的 schema,这样基于 Dubbo 的定义才会被显示,并且 Dubbo 的注册中心是 ZK,也需要配置。
更新 main 函数,开启 Dubbo。
示例如下:
问题现象:
Bolt 端口 12200 被占用。
解决方案:
更换端口或者把本地占用端口的服务关闭。
问题描述:
应用依赖了一些 SOFA 组件,但在本地环境中仅希望测试 RPC,如何处理下述问题:
应用启动失败
服务无法注册
解决方案:
健康检查机制会在项目启动的时候对所有组件进行探活,如果此时引用了 DDCS(Distributed Dynamic Configuration Service) 或者其它组件,会出现应用启动正常,但 RPC 无法在本地注册的现象。此时,如果业务暂时没有用到这些组件,可以在 healthcheck 时略过这些检查。本质原因是这些组件会到 antvip 中寻找组件地址,而此时应用并不在云上,所以会失败。具体操作如下:
此方案只建议在测试中使用,线上环境一定要打开健康检查。
问题现象:
SOFARest 接口在上传文件时,文件超过 10 MB 时会报错,报错信息如下:
排查步骤:
示例如下:
获取具体报错的原因,例如 Failed to send a 413 Request Entity Too Large。
假如应用的内存容量默认比较小,比如 1 GB 或者 2 GB,而 Netty 的 REST 所请求的 Payload 是放在 DirectMemory 里的,且该 DirectMemory 有个最大值,默认是系统 JVM 初始化时所申请内存大小。如果上传大文件时发生了 OOM 或者 DirectMemory 内存溢出的错误,需要进行下述处理:
确定当前系统的内存大小是否能够承受所传大文件。
确认运行时内存或者 DirectMemory 的最大值。
如果可以优化,请选择分段上传。
您可以排查以下几个方面:
线程池发生了阻塞
GC 处理遇到严重故障
硬件磁盘 IO 故障
一般通过 tsar -I 1 查看下一分钟内的 IO 请求次数。在某些场景下,如果磁盘 IO 较高会影响到整个系统的性能。
网络故障
网络问题一般都很难定位,这里介绍一个较为常见的网络设备故障问题,即防火墙会剔除不活跃(90s)链接或 LVS 故障切流量剔除链接时,均不会向客户端 Socket 发 RST 包,这样会导致客户端存在脏 Socket。
某台机器请求一个具体 IP 的服务,该服务流量不大,所以请求频率很低,几十分钟甚至几小时一次。当超时时,会超时一次即断开连接,或连续超时 n 次后链接才断开。超时一次,应该是防火墙断开的链接;超时 n 次,则是 LVS 断开的链接。
问题描述:
SOFA 客户端调用耗时较长的服务时,需要注意什么?
排查思路:
服务发起方如果发现对方是一个耗时较长的服务,则需要配置一个比较合理的超时时间,否则,要判断该接口是不是需要一个 oneway 方式去执行。如果必须等待结果,且触发后发现,无论如何配置,超时时间都无法生效,则需检查防火墙或负载均衡器是否在上游配置了连接超时控制。
这个问题,只会在本地开发的时候会遇到。云上开发不需要关心注册中心。 企业版是通过 antvip 来获取一个健康的注册中心的地址,然后会构建 dsr://ip:port,同时,企业版将这个构建过程包装在了框架里。
示例如下:
SpringMVC 的 Controller 请求方法:
代码配置,示例如下:
Resteasy 的 GET 请求:
类型一:
代码配置,示例如下:
类型二(key-value)
代码配置,示例如下:
@FormParam:将表单中的字段映射到方法调用上,此类方式提交方式一般为 Post。
SOFARPC 在框架层面提供了通用的接口方法和类型:
服务的接口名:通过服务定义设置。
方法名和参数列表:通过 $invoke 或 $genericInvoke 传入。
自定义类型:使用 GenericObject。
其中几个特别需要注意事项为:
$invoke 方法:只用于参数类型,可以被当前应用的类加载器加载,如果只有基础类型,则可以使用此方法。
$genericInvoke 结合 GenericObject,当参数类型无法被当前应用的类加载器加载时,使用该方法。
argTypes 必须传递接口声明的参数类型,不可使用子类类型。
GerericContext 暂时只用于单元化场景。
GenericObject、fields 的 value 也可以是一个 GenericObject。
SOFARPC 的泛化调用,示例如下:
服务方:
服务、接口和类型定义:
服务方的接口、自定义类型和发布泛化调用的配置:
客户端:
定义泛化的服务,并设置正确的目标服务接口。
reference 里的 interface 需要填写框架定义的 GenericService 接口。 global-attrs 里的 generic-interface 才是填写真正的目标服务接口。 reference 里的 interface 都是 GenericService,如果要泛化调用多个不同的服务接口,可通过 reference 的 id 来区分。
通过 GenericService 的方法来调用目标方法。
目前提供了两种方法:
$invoke:仅支持方法参数类型在当前应用的 ClassLoader 中存在的情况。
$genericInvoke:支持方法参数类型在当前应用的 ClassLoader 中不存在的情况。
具体使用,示例如下:
服务方:
服务引用,示例如下:
服务端服务定义,示例如下:
客户方:
泛化调用,示例如下:
泛化调用提供了让客户端,在不需要依赖服务端接口的情况下,也能发起调用的能力。在 Bolt 通信协议下使用 Hessian2 作为序列化协议,是目前 SOFARPC 的泛化调用仅支持的方式。
泛化调用的常见场景:
在开发中遇到一些第三方应用不想要依赖我们自己开发的依赖接口 JAR,但也想通过某种方式发起调用,或者更进一步,做一个非依赖 JAR 的简单微服务网关。
可通过过滤器的方式进行 RPC 接口过滤,比如 IP 黑白名单的过滤、Token 的验证等。
白名单过滤的实现步骤如下:
继承 SOFA 的 Filter 抽象类,实现里面的 invoke 方法:
在需要白名单验证的接口上配置过滤器:
SOFARPC 的 REST 协议底层使用的是 Resteasy,可以实现文件上传下载。
主要步骤如下:
实现下载方法:
文件名中存在中文字符时,需确保服务端与客户端处理字符编码一致。
实现上传方法:
一般实现思路为:
通过通用的 RESTful 协议进行接入。
后端根据泛化接口对传入数据进行解析。
通过代理的过滤器指定路由配置。
具体可以参考 spring-cloud-gateway 的过滤器设计和 SOFABolt 协议的泛化设计。可能需要下述扩展:
动态路由
注册中心联动
缓存设计
自动代码生成等
默认该功能是关闭的,开启后会影响性能,请尽量避免使用。
实现步骤如下:
开启配置。
代码实现。
示例如下:
RpcInvokeContext 是一个 RPC 执行上下文,在这个上下文中,我们可以向 RequestBaggage 里添加数据,且数据必须是字符串类型。
在 SOFARPC 服务中,协议默认端口的约定为:
BOLT 协议:12200
REST 协议:8341
一个服务如果有多个实现,可以在 RPC 暴露服务和引用服务的地方配置一个 unique-id 来作为它的唯一标识。示例如下:
RPC服务在发布和引用时都有超时控制的配置,方法也可以做超时控制,其超时时间的优先级为:引用服务的方法超时 > 引用服务的全局超时时间 > 服务发布者的方法超时 > 服务发布者的全局超时时间。示例如下:
需要注意下述事项:
首先是需要明确中间件使用的版本,目前推荐 sofaboot-enterprise 的版本是 3.4.*,在使用该版本时,如果要使用 RESTful 接口进行开发时,只需要引入 RPC 对应的 starter 包依赖即可,无需再引入 REST 的 starter 依赖,否则会启动两个 RESTful 服务端,导致 8341 端口被占用。
发布服务时直接同时声明两种协议的 binding 即可,如下所示:
问题现象:
问题原因:
属性配置中打开了直连开关,而代码中配置的 URL 与实际 RPC 服务地址不一致。
代码中配置 test-url=“${servicename_tr_service_ur}”。
servicename_tr_service_ur 指向的地址与实际的 RPC 服务地址不符。
RPC provider 与 consumer 工程的 SOFABoot 版本不一致。
解决方案:
问题现象:
SOFA RPC 使用 REST 接口触发 RPC 的泛化调用,每次触发都需要 30 秒的时间,且不报超时异常。从业务日志中可以看出,开始处理业务和结束业务之间确实花了 30 秒。
问题原因:
可能由于 DNS 配置错误,导致超时。
解决方案:
在 /etc/hosts 中添加 IP 与主机名的映射,尝试解决该问题。
问题现象
RPC 注册不成功。
问题原因
解决方案
问题现象:
RPC 应用启动时出现如下报错:
问题原因:
rpc-enterprise-sofa-boot-starter 被注释掉了,而这个 JAR 包提供了如下 binding:
解决方案:
引入 rpc-enterprise-sofa-boot-starter JAR 包。
问题原因:
仅客户端迁移至了共享中间件,服务端并未迁移。
解决方案:
将服务端迁移到共享中间件。
问题现象:
具体报错信息如下:
解决方案:
出现这种问题,一般是重复多次注册服务导致,建议您检查服务的服务注册,删除多余的注册信息。
RPC 单次传输的数据量本身没有限制,但基于性能考虑,建议设置为 4 KB 以内。如果数据量超过限制,在高并发场景下,可能出现一些 overflow 的问题,报错关键字为 maybe write overflow。建议通过以下系统参数计算实际需要的大小:
参数
默认值
32 × 1024
64 × 1024
目前主要支持以下几种客户端:
Spring MVC
代码侵入:无
限流方法:Web URL
SOFA RPC Bolt
代码侵入:无
限流方法:接口方法
普通 Spring Bean
代码侵入:结合 AOP
限流方法:接口方法
不支持以下客户端:
SOFA RPC REST
代码侵入:无
限流方法:接口方法 、Web URL
SOFA REST(RESTEASY)
代码侵入:无
限流方法:接口方法、Web URL
对于SOFA REST,目前只能通过 AOP 的方式去拦截 REST 对应的 Bean 来实现限流,步骤如下:
设计要拦截的接口方法,示例如下:
定义 bean,示例如下:
配置 AOP,示例如下:
您可以通过以下方式进行检查:
在项目服务器上,通过 ps -ef | grep java 命令查看 Java 进程。如果进程存在,则表示项目已启动;反之,则没有启动。
在项目服务器上,执行以下命令,查看返回结果:
RPC 版本为 3.0 及以上
8080 为项目端口,您需要替换为实际的项目端口。
RPC 版本为 3.0 以下
如果返回的结果中没有 down 字段,则表示项目已启动;反之,则没有启动。
在项目服务器上,通过 ps -ef | grep 9600 命令检查 9600 端口是否正常。如果端口存在,则表示注册中心已连接;反正,则表示没有正常连接。
排查步骤如下:
检查异常日志的下一行的是否存在相同 traceid 的日志,确认是否存在其他调用。
如果存在,则排查同一个 traceid 下的其他调用是否有问题。
如果不存在,则建议您优化自身业务的功能。
关注阿里云公众号或下载阿里云APP,关注云资讯,随时随地运维管控云服务