
一、rpc调用是什么定义为了解决什么问题出现的什么场景可以使用为什么要使用rpc定义RPC远程过程调用Remote Procedure Call允许本地程序像调用本地函数一样调用另一台服务器上的函数屏蔽网络通信细节。解决的问题解决分布式场景下不同服务跨机器网络调用的通信问题屏蔽 socket 连接、报文编解码、网络收发等底层网络复杂逻辑。使用场景微服务多服务之间跨进程、跨服务器方法调用。为什么使用 RPC调用体验接近本地方法开发简洁封装底层网络通信逻辑开发者无需处理原生网络编程具备序列化、超时、负载均衡、重试等分布式通信基础能力。伪代码示例java// 客户端像调用本地方法一样远程调用 UserRpcService rpcClient RpcFramework.getClient(UserRpcService.class); User user rpcClient.getUserById(1001); // 远程调用另一台机器服务方法 // 服务端提供实现 public class UserRpcServiceImpl implements UserRpcService { Override public User getUserById(Long userId) { return userMapper.selectById(userId); } }二、进行后端开发时我们常说的调用远程服务包一层RPC是什么意思含义远程服务没有开放 HTTP 接口仅提供 RPC 服务在自身项目新建一层 RPC 客户端封装模块隔离底层 RPC 通信逻辑统一参数组装、异常处理、超时配置上层业务不再直接操作原生 RPC 客户端这就是包一层 RPC。真实例子项目需要调用商品微服务的 Dubbo RPC 接口引入商品 dubbo 依赖原生可直接注入接口调用javaReference private GoodsDubboApi goodsDubboApi;封装一层 RPC 包装类javaComponent public class GoodsRpcClient { Reference private GoodsDubboApi goodsDubboApi; // 统一入参参数组装goodsId和统一出参格式为GoodsDTO public GoodsDTO getGoodsInfo(Long goodsId) { GoodsQuery query new GoodsQuery(); query.setGoodsId(goodsId); GoodsResp resp goodsDubboApi.getGoods(query); // 统一转换DTO、捕获RPC异常、日志打印,还可以封装超时重试等逻辑 if (!resp.getSuccess()) { throw new BusinessException(查询商品失败:resp.getMsg()); } return convert(resp.getData()); } private GoodsDTO convert(GoodsModel model){ // 对象转换 } }业务 service 只调用封装好的 RpcClient不直接使用原生GoodsDubboApijavaService public class OrderService { Autowired private GoodsRpcClient goodsRpcClient; public void createOrder(Long goodsId){ GoodsDTO goods goodsRpcClient.getGoodsInfo(goodsId); // 下单逻辑 } }总的来说包一层rpc也就是包掉原生的底层RPC(goodsDubboApi),将原生RPC封装进统一的自定义客户端GoodsRpcClient用来进行远程调用达到客户端进行远程调用实现统一入参、出参、超时重试、异常处理等逻辑,对我们远程调用的调用前入参和调用后结果处理成功返回统一结果格式,失败进行统一异常处理和重试逻辑达到统一便于我们统一管理和改造远程调用的逻辑。三、OpenFeign远程调用是什么定义为了解决什么问题出现的什么场景可以使用为什么要使用OpenFeign 定义OpenFeign 是基于 HTTP 协议的声明式远程调用组件能够通过定义接口 注解的方式发起 HTTP 请求封装 HTTP 请求构建、参数组装、响应解析流程。解决的问题简化 Spring 项目中 HTTP 远程调用的编码消除手动拼接 url、组装请求参数、解析返回结果的重复代码。使用场景微服务之间基于 HTTP 接口进行服务调用。为什么要使用 OpenFeign声明式接口编码可读性高风格类似本地方法调用内置集成负载均衡结合 LoadBalancer统一管理请求配置、拦截器、异常处理。伪代码实例java// 1.定义Feign接口 FeignClient(name goods-service) public interface GoodsFeignClient { GetMapping(/goods/get) GoodsResp getGoods(RequestParam(goodsId) Long goodsId); } // 2.业务层调用 Service public class OrderService { Autowired private GoodsFeignClient goodsFeignClient; public void createOrder(Long goodsId){ GoodsResp resp goodsFeignClient.getGoods(goodsId); } }四、OpenFeign和RPC的区别是什么区别底层协议不同RPCDubbo/GRPC大多使用自定义 TCP 二进制协议OpenFeign 底层是标准HTTP 1.1。定位本质不同RPC 是一类远程调用技术统称OpenFeign只是 HTTP 调用的封装框架不属于 RPC 实现。数据序列化Dubbo/GRPC 默认二进制序列化Hessian、ProtobufOpenFeign 默认 JSON 序列化。性能差异TCP 二进制 RPC 通常性能更高HTTPJSON 开销相对更大。互通性OpenFeign 调用的服务只要提供标准 HTTP 接口语言无关Dubbo 等 RPC 服务需要对应客户端 SDK 才能通信。使用风格相似但载体不一样二者都做到「像调用本地方法一样发起远程请求」OpenFeign伪装本地方法实际发 HTTPDubbo RPC伪装本地方法实际走 TCP 私有协议最简代码对比Dubbo RPCjavaReference GoodsDubboApi api;OpenFeignjavaFeignClient(goods-service) public interface GoodsFeignClient {}场景简单区分内部微服务追求高性能Dubbo/GRPC调用第三方 http 接口、异构服务、SpringCloud HTTP 微服务OpenFeign补充关键概念Spring Cloud 里常说的「Feign 远程调用」是 HTTP 调用不是 RPC 调用。五、OpenFeign远程调用的降级策略是什么定义使用场景是什么如何实现?定义OpenFeign 降级策略依托 Sentinel/Hystrix远程调用失败、超时、异常时不持续等待或抛出异常执行预设兜底逻辑返回容错结果避免级联故障。使用场景微服务远程调用依赖服务不可用、响应超时、接口报错防止雪崩保障自身业务正常运行。实现方式OpenFeign 整合熔断组件Sentinel 主流定义接口降级实现类发生异常自动路由到降级方法。伪代码java// Feign接口 FeignClient(value goods-service, fallback GoodsFeignFallback.class) public interface GoodsFeignClient { GoodsVO getGoods(Long goodsId); } // 降级兜底实现类 Component public class GoodsFeignFallback implements GoodsFeignClient{ Override public GoodsVO getGoods(Long goodsId) { // 降级兜底数据 return new GoodsVO(null,商品服务暂时不可用); } }真实 SpringBoot 代码开启 feign sentinel 支持application.ymlyamlfeign: sentinel: enabled: trueFeign 接口javaFeignClient(value goods-service, fallback GoodsFeignFallback.class) public interface GoodsFeignClient { GetMapping(/goods/query) GoodsVO getGoods(RequestParam(goodsId) Long goodsId); }降级实现类javaComponent public class GoodsFeignFallback implements GoodsFeignClient { Override public GoodsVO getGoods(Long goodsId) { GoodsVO vo new GoodsVO(); vo.setGoodsName(服务繁忙暂时无法查询商品); return vo; } }业务调用javaService public class OrderService { Autowired private GoodsFeignClient goodsFeignClient; public void createOrder(Long goodsId){ GoodsVO goodsVO goodsFeignClient.getGoods(goodsId); } }六、RPC远程调用的降级策略是什么定义使用场景是什么定义RPC 降级策略RPC 远程调用出现超时、异常、服务不可用时放弃正常远程请求执行预先编写的兜底逻辑返回结果阻断故障传播防止服务雪崩。使用场景调用下游 RPC 服务宕机、网络波动、调用超时、限流触发避免大量请求阻塞耗尽自身线程资源。如何实现主流 Dubbo 依托 Sentinel/Hystrix 实现定义接口降级实现类RPC 调用失败自动执行降级方法。伪代码java// Dubbo RPC标准接口 public interface GoodsDubboApi { GoodsDTO getGoods(Long goodsId); } // 降级兜底实现 public class GoodsDubboFallback implements GoodsDubboApi { Override public GoodsDTO getGoods(Long goodsId) { GoodsDTO dto new GoodsDTO(); dto.setMsg(商品RPC服务暂时不可用); return dto; } }真实代码 (SpringBoot Dubbo Sentinel)application.yml 配置yamldubbo: consumer: # Dubbo 消费者启动默认会去检测注册中心是否存在对应提供者服务设为 false项目启动不阻塞 # 即使提供者未启动消费者也正常启动运行期调用时报错。 check: false sentinel: dubbo: # 含义开启Sentinel对Dubbo RPC调用的适配支持实现RPC限流、熔断降级。 # 开启 Sentinel Dubbo 适配器自动对所有 Dubbo 消费接口埋点能够针对 # RPC 接口配置限流、熔断、降级规则。 enabled: true2. RPC 接口javapublic interface GoodsDubboApi { GoodsDTO getGoods(Long goodsId); }3. 降级实现类javaComponent public class GoodsDubboFallback implements GoodsDubboApi { Override public GoodsDTO getGoods(Long goodsId) { GoodsDTO dto new GoodsDTO(); dto.setMsg(商品服务调用降级请稍后重试); return dto; } }4. RPC 封装客户端javaComponent public class GoodsRpcClient { # Reference 注解注入生成 Dubbo 动态代理对象代理封装网络通信、序列化、注册中心寻址 # 调用代理对象方法时发起 RPC 远程请求。 Reference private GoodsDubboApi goodsDubboApi; Autowired private GoodsDubboFallback goodsDubboFallback; public GoodsDTO getGoods(Long goodsId) { try { return goodsDubboApi.getGoods(goodsId); } catch (Exception e) { return goodsDubboFallback.getGoods(goodsId); } } }补充生产推荐使用 Sentinel 注解方式统一拦截降级javaSentinelResource(value getGoods, fallback getGoodsFallback) public GoodsDTO getGoods(Long goodsId){ return goodsDubboApi.getGoods(goodsId); } public GoodsDTO getGoodsFallback(Long goodsId){ GoodsDTO dto new GoodsDTO(); dto.setMsg(降级返回); return dto; }问题fallback getGoodsFallback这段代码中fallback的值取得是降级策略方法名还是降级策略类名取值为降级方法名称方法名字符串。硬性约束该方法必须和被 SentinelResource 修饰的主方法处于同一个类中方法入参、返回值类型需要与主方法保持一致。访问权限使用 public。示例对照javaSentinelResource(value goods#getGoods, fallback getGoodsFallback) public GoodsDTO getGoods(Long goodsId) { return goodsDubboApi.getGoods(goodsId); } // fallback填写的就是下方这个方法名 public GoodsDTO getGoodsFallback(Long goodsId) { GoodsDTO dto new GoodsDTO(); dto.setMsg(RPC调用触发降级); return dto; }补充区分OpenFeignFeignClient(fallback XxxClass.class)写法填写的是Class 类二者语法不要混淆。七、如何监控和管理RPC远程调用的降级策略定义RPC 降级监控管理采集 RPC 调用指标、异常、熔断触发事件可视化查看降级生效情况并支持动态修改降级、熔断规则无需重启服务。使用场景线上排查 RPC 调用频繁降级、评估下游稳定性动态调整阈值、开关降级规则。如何实现指标采集收集调用量、失败率、超时次数、降级触发次数可视化面板展示监控数据控制台动态推送降级 / 熔断规则至应用主流方案Dubbo Sentinel Dashboard伪代码java// 标记资源监控埋点 SentinelResource(value getGoodsRpc, fallback queryFallback) public GoodsDTO queryGoods(Long goodsId){ return goodsDubboApi.getGoods(goodsId); } // 降级方法 public GoodsDTO queryFallback(Long goodsId){ return fallbackData(); }真实代码 配套管控操作引入依赖xmldependency groupIdcom.alibaba.csp/groupId artifactIdsentinel-dubbo-adapter/artifactId /dependency dependency groupIdcom.alibaba.csp/groupId artifactIdsentinel-transport-simple-http/artifactId /dependency2. 启动参数连接 Sentinel 控制台plaintext-Dcsp.sentinel.dashboard.server127.0.0.1:8080 -Dproject.nameorder-service3.RPC 调用封装javaComponent public class GoodsRpcClient { Reference private GoodsDubboApi goodsDubboApi; SentinelResource(value goods#getGoods, fallback getGoodsFallback) public GoodsDTO getGoods(Long goodsId) { return goodsDubboApi.getGoods(goodsId); } public GoodsDTO getGoodsFallback(Long goodsId) { GoodsDTO dto new GoodsDTO(); dto.setMsg(RPC调用触发降级); return dto; } }控制台管控操作登录 Sentinel Dashboard找到资源goods#getGoods【熔断规则】配置异常比例、熔断时长实时监控QPS、异常数、熔断状态、降级触发记录支持动态新增 / 修改 / 删除规则无需重启服务。监控核心观测指标成功请求数、失败请求数请求平均耗时熔断开启 / 关闭状态降级触发总次数八、真实生产环境rpc sentinel实现降级熔断,我们必须将客户端包装的RpcClient中进行sentinel注解埋点的方法和降级策略方法写在一个类内吗注意这个是生产环境最佳实践必须将降级策略解耦否则维护起来特别冗余。总的思想就是我们降级策略方法和RpcClient中包装的RPC调用同名方法还是在同一个类内但是降级策略方法我们直接调用外部解耦维护的降级策略类,这样能同时实现降级策略使用(遵循同类内规范)以及对降级策略的统一维护。是的基于原生SentinelResource注解规范fallback 指定的降级方法必须与主方法在同一个类中。硬性约束fallback 函数和业务方法同类参数列表、返回值类型保持一致访问权限使用 public。备选方案想要拆分类实现降级逻辑使用blockHandler区分fallback业务异常、调用失败降级同类blockHandler限流触发拦截同样要求同类。两种标准写法参考javaComponent public class GoodsRpcClient { Reference private GoodsDubboApi goodsDubboApi; SentinelResource(value getGoods, fallback getGoodsFallback) public GoodsDTO getGoods(Long goodsId) { return goodsDubboApi.getGoods(goodsId); } // 必须同类内 public GoodsDTO getGoodsFallback(Long goodsId) { GoodsDTO dto new GoodsDTO(); dto.setMsg(RPC调用降级返回); return dto; } }如果你希望降级逻辑抽离到独立类原生SentinelResource fallback不支持。解决方案在当前 RpcClient 的 fallback 方法内部调用外部独立降级工具类完成逻辑代码如下javapublic GoodsDTO getGoodsFallback(Long goodsId) { // 调用外部类实现逻辑解耦 return rpcFallbackService.goodsDefaultFallback(goodsId); }九、RPC和OpenFeign二者的远程服务端是如何定义实现客户端可以远程调用的一、Dubbo RPCTCP 协议伪代码java// 1.公共API模块提供者、消费者共同依赖 public interface GoodsDubboApi { GoodsDTO getGoods(Long goodsId); } // 2.服务提供者实现 DubboService public class GoodsDubboApiImpl implements GoodsDubboApi{ Override public GoodsDTO getGoods(Long goodsId) { // 查询数据库 } }真实生产代码公共接口单独 maven 模块goods-apijavapublic interface GoodsDubboApi { GoodsDTO getGoods(RequestParam(goodsId) Long goodsId); }提供者实现javaimport org.apache.dubbo.config.annotation.DubboService; DubboService public class GoodsDubboApiImpl implements GoodsDubboApi { Override public GoodsDTO getGoods(Long goodsId) { GoodsDTO dto new GoodsDTO(); dto.setGoodsId(goodsId); dto.setGoodsName(测试商品); return dto; } }二、OpenFeignHTTP 接口伪代码java// 服务提供者Controller RestController RequestMapping(/goods) public class GoodsController { GetMapping(/query) public GoodsDTO getGoods(Long goodsId){ //业务逻辑 } }真实生产代码javaRestController RequestMapping(/goods) public class GoodsController { GetMapping(/query) public GoodsDTO getGoods(RequestParam Long goodsId) { GoodsDTO dto new GoodsDTO(); dto.setGoodsId(goodsId); dto.setGoodsName(测试商品); return dto; } }简要原理说明精简RPC (Dubbo)提供者使用DubboService暴露接口至注册中心消费者通过Reference订阅接口发起 TCP 远程调用。OpenFeign服务端编写标准 SpringMVC Controller 提供 HTTP 接口客户端定义FeignClient接口发起 http 请求调用。补充RPC客户端通过Reference进行远程调用Reference注解注入生成 Dubbo 动态代理对象代理封装网络通信、序列化、注册中心寻址调用代理对象方法时发起 RPC 远程请求。示例真实代码片段javaComponent public class GoodsRpcClient { Reference private GoodsDubboApi goodsDubboApi; public GoodsDTO getGoods(Long goodsId){ return goodsDubboApi.getGoods(goodsId); } }十、RPC远程调用服务提供者和消费者同时依赖的公共API模块公共接口单独 maven 模块goods-api)一般放在什么位置如何实现二者都能看到通过maven进行提供者和调用者的双方依赖吗?存放位置项目仓库独立 Maven Module模块名称 goods-api。可见实现将 goods-api 打包为 Jar 上传至私有 Maven 仓库Nexus。依赖方式服务提供者、消费客户端分别在各自 pom.xml 引入 goods-api 坐标完成依赖引用。提供者 pom 依赖示例xmldependency groupIdcom.demo/groupId artifactIdgoods-api/artifactId version1.0.0/version /dependency消费者 pom 依赖示例xmldependency groupIdcom.demo/groupId artifactIdgoods-api/artifactId version1.0.0/version /dependency下一篇文章带你实现如何将项目打包成maven进行模块化依赖提供给其它项目使用