服务调用(OpenFeign / Dubbo / gRPC)
服务调用方式演进
第一阶段:HTTP 直连
微服务架构早期,服务间直接通过 HTTP 请求调用,硬编码目标地址。
text
服务 A ──HTTP GET http://192.168.1.10:8080/api/order/100──→ 服务 B问题:
- 地址硬编码,扩缩容需修改配置
- 无负载均衡,单点故障风险
- 无服务发现,运维成本高
第二阶段:客户端负载均衡
引入注册中心(Consul / Eureka / Nacos)和负载均衡客户端。
text
服务 A → 负载均衡器(Ribbon / LoadBalancer) → 服务 B 实例列表
├─ 192.168.1.10:8080
└─ 192.168.1.11:8080改进:
- 服务实例动态上下线
- 客户端侧负载均衡(轮询、加权、最小连接数)
- 配合声明式 HTTP 客户端(Feign)大幅简化代码
第三阶段:RPC 框架
对于高性能场景,HTTP 协议冗余较大,RPC(Remote Procedure Call)框架采用定制协议 + 高效序列化 + 连接复用,显著降低延迟。
text
服务 A ──Dubbo 协议(二进制、长连接)──→ 服务 B
├── 序列化:Hessian2 / JSON / Protobuf
├── 传输:Netty(TCP 长连接)
└── 治理:熔断、限流、降级、追踪OpenFeign 原理与实战
概述
OpenFeign 是 Spring Cloud 生态中的声明式 HTTP 客户端,让开发者通过 Java 接口 + 注解即可定义远程调用,无需编写重复的 HTTP 请求代码。
声明式 HTTP 客户端
java
@FeignClient(name = "order-service", url = "${order.service.url}")
public interface OrderFeignClient {
@GetMapping("/order/{orderId}")
OrderVO getOrder(@PathVariable("orderId") Long orderId);
@PostMapping("/order/create")
R<OrderVO> createOrder(@RequestBody CreateOrderRequest request);
}java
// 使用
@Autowired
private OrderFeignClient orderFeignClient;
public void handle() {
OrderVO order = orderFeignClient.getOrder(100L);
}核心注解:
| 注解 | 作用 |
|---|---|
@FeignClient | 声明 Feign 客户端,指定服务名和配置 |
@RequestMapping / @GetMapping / @PostMapping | 映射目标 HTTP 方法及路径 |
@PathVariable | URL 路径参数 |
@RequestParam | 请求查询参数 |
@RequestBody | 请求体(序列化为 JSON) |
@RequestHeader | 请求头参数 |
与 LoadBalancer 整合的负载均衡
java
// application.yml
spring.cloud.loadbalancer.retry.enabled: true
// 自动整合(引入 spring-cloud-starter-loadbalancer 后)
@FeignClient(name = "order-service") // name 与注册中心服务名对应
public interface OrderFeignClient {
@GetMapping("/order/list")
List<OrderVO> listOrders(@RequestParam("userId") Long userId);
}OpenFeign 默认整合 Spring Cloud LoadBalancer(替代已进入维护状态的 Netflix Ribbon),拦截 FeignClient 的请求 URL,用服务名从注册中心获取实例列表,执行负载均衡策略。
负载均衡策略:
| 策略 | 说明 |
|---|---|
| RoundRobinLoadBalancer | 轮询(默认) |
| RandomLoadBalancer | 随机 |
| 自定义 ReactiveLoadBalancer | 实现接口自行定义 |
请求拦截器
java
@Component
public class FeignRequestInterceptor implements RequestInterceptor {
@Override
public void apply(RequestTemplate template) {
// 透传 TraceId
String traceId = MDC.get("traceId");
if (StringUtils.hasText(traceId)) {
template.header("X-Trace-Id", traceId);
}
// 透传 Token
ServletRequestAttributes attrs = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
if (attrs != null) {
String token = attrs.getRequest().getHeader("Authorization");
template.header("Authorization", token);
}
}
}日志配置
java
// 配置类
@Configuration
public class FeignConfig {
@Bean
Logger.Level feignLoggerLevel() {
// NONE: 不记录(默认)
// BASIC: 记录 Method + URL + 响应状态码 + 耗时
// HEADERS: BASIC + 请求/响应头
// FULL: HEADERS + 请求/响应体
return Logger.Level.FULL;
}
}yaml
# application.yml
logging.level.com.example.feign.OrderFeignClient: debug超时配置
yaml
# application.yml
spring.cloud.openfeign.client.config:
default:
connectTimeout: 5000 # 连接超时(ms)
readTimeout: 10000 # 读取超时(ms)
order-service:
connectTimeout: 3000
readTimeout: 5000源码概要
text
Feign 核心调用链:
@FeignClient 接口
↓
JDK 动态代理(FeignInvocationHandler)
↓
MethodHandler(SynchronousMethodHandler)
↓
RequestTemplate → 解析注解、拼接 URL、组装 Headers
↓
Client(执行 HTTP 请求)
├── Client.Default → java.net.HttpURLConnection
├── ApacheHttpClient → Apache HttpClient(连接池)
├── OkHttpClient → OkHttp(连接池、HTTP/2)
└── LoadBalancerFeignClient → 包装负载均衡逻辑
↓
Response → 反序列化 → 返回结果关键源码定位:
| 类 | 作用 |
|---|---|
Feign.Builder | 构建 Feign 客户端入口 |
ReflectiveFeign | 解析接口注解,创建 JDK 动态代理 |
FeignInvocationHandler | 代理调用分发到对应 MethodHandler |
SynchronousMethodHandler | 执行请求、处理重试和 fallback |
Client.Default | 默认 HTTP 客户端(基于 HttpURLConnection) |
LoadBalancerFeignClient | 整合负载均衡 |
Dubbo 原理与实战
RPC 核心概念
RPC(Remote Procedure Call)让开发者像调用本地方法一样调用远程服务。
text
调用方(Consumer) 提供方(Provider)
┌──────────────────┐ ┌──────────────────┐
│ Stub(代理) │ ── 网络传输 → │ Skeleton(骨架) │
│ 序列化参数 │ ←─ 响应返回 ─ │ 反序列化参数 │
│ 反序列化结果 │ │ 执行方法 │
└──────────────────┘ └──────────────────┘核心要素:
| 要素 | 说明 |
|---|---|
| Stub / Proxy | 客户端代理,屏蔽网络通信细节 |
| 序列化 | 将对象转为二进制流(Hessian2 / JSON / Protobuf) |
| 网络传输 | TCP 长连接 / HTTP / HTTP/2 |
| 服务注册与发现 | 动态获取服务提供方地址 |
Dubbo 架构
text
┌─────────────┐
│ Registry │
│ (Nacos/ZK) │
└──────┬──────┘
│
subscribe ──────┴────── register
┌─────────────┐ ┌─────────────┐
│ Consumer │ ── invoke ──→ │ Provider │
│ (客户端) │ ←─ response─ │ (服务端) │
└─────────────┘ └─────────────┘
│
▼
┌─────────────┐
│ Monitor │
│ (统计调用) │
└─────────────┘各角色职责:
| 角色 | 职责 |
|---|---|
| Provider | 服务提供方,启动时向注册中心注册服务 |
| Consumer | 服务消费方,启动时从注册中心订阅服务列表 |
| Registry | 注册中心,负责服务地址的存储与通知 |
| Monitor | 监控中心,统计调用次数和耗时(可选) |
| Container | 服务容器,负责加载、运行服务 |
实战示例
服务定义
java
// API 接口(单独 jar 包)
public interface OrderService {
OrderVO getOrder(Long orderId);
R<OrderVO> createOrder(CreateOrderRequest request);
}Provider 端
java
@DubboService // 声明 Dubbo 服务(替代 @Service)
public class OrderServiceImpl implements OrderService {
@Override
public OrderVO getOrder(Long orderId) {
return orderMapper.selectById(orderId);
}
@Override
public R<OrderVO> createOrder(CreateOrderRequest request) {
// 业务逻辑
}
}yaml
# application.yml
dubbo:
application:
name: order-provider
protocol:
name: dubbo # 协议
port: 20880 # 端口
registry:
address: nacos://127.0.0.1:8848Consumer 端
java
@DubboReference // 注入远程服务代理
private OrderService orderService;
public void handle() {
OrderVO order = orderService.getOrder(100L);
}yaml
dubbo:
application:
name: order-consumer
registry:
address: nacos://127.0.0.1:8848协议对比
| 协议 | 传输层 | 序列化 | 连接方式 | 适用场景 |
|---|---|---|---|---|
| Dubbo | TCP (Netty) | Hessian2 | 长连接 | 内部 RPC,高并发低延迟 |
| Hessian | HTTP | Hessian | 短连接 | 跨语言,兼容性一般 |
| gRPC | HTTP/2 | Protobuf | 长连接 | 跨语言、流式通信 |
| Thrift | TCP | Thrift Binary | 长/短连接 | 跨语言,需代码生成 |
| REST | HTTP | JSON | 短连接 | 开放 API、浏览器兼容 |
集群容错策略
java
@DubboReference(cluster = "failover") // 默认
private OrderService orderService;| 策略 | 说明 | 适用场景 |
|---|---|---|
| Failover(失败自动切换) | 调用失败后重试其他节点 | 幂等操作(读请求) |
| Failfast(快速失败) | 调用失败立即报错 | 非幂等操作(写请求) |
| Failsafe(失败安全) | 调用失败忽略异常 | 日志上报、审计 |
| Failback(失败自动恢复) | 调用失败后定时重试 | 异步通知 |
| Forking(并行调用) | 同时调用多个节点,取最快结果 | 实时性极高的读请求 |
| Broadcast(广播调用) | 逐个调用所有节点 | 缓存更新、状态同步 |
SPI 扩展机制
Dubbo 的微内核 + 插件架构——通过 SPI 机制加载各组件实现,便于自定义扩展。
text
接口 内置实现 可扩展
Protocol → DubboProtocol, InjvmProtocol, RegistryProtocol 自定义协议
Serialize → Hessian2, FastJson, Protobuf 自定义序列化
LoadBalance → Random, RoundRobin, LeastActive, ConsistentHash 自定义负载均衡
Filter → ExceptionFilter, TimeoutFilter, TokenFilter 自定义过滤器自定义扩展示例:
java
// 1. 实现扩展点
public class MyLoadBalance implements LoadBalance {
@Override
public <T> Invoker<T> select(List<Invoker<T>> invokers, URL url, Invocation invocation) {
// 自定义策略
return invokers.get(ThreadLocalRandom.current().nextInt(invokers.size()));
}
}
// 2. 配置 SPI 文件
// META-INF/dubbo/org.apache.dubbo.rpc.cluster.LoadBalance
// my=com.example.MyLoadBalance
// 3. 使用
@DubboReference(loadbalance = "my")
private OrderService orderService;gRPC 原理与实战
概述
gRPC 是 Google 开源的高性能 RPC 框架,基于 HTTP/2 协议,使用 Protocol Buffers 作为接口定义语言和序列化工具,支持多种语言。
Protocol Buffers 定义服务
protobuf
// order.proto
syntax = "proto3";
package com.example.order;
service OrderService {
// 一元调用(Unary)
rpc GetOrder (GetOrderRequest) returns (OrderVO);
// 服务端流式(Server Streaming)
rpc ListOrders (ListOrdersRequest) returns (stream OrderVO);
// 客户端流式(Client Streaming)
rpc BatchCreateOrder (stream CreateOrderRequest) returns (BatchCreateResponse);
// 双向流式(Bidirectional Streaming)
rpc ProcessOrder (stream OrderEvent) returns (stream OrderResult);
}
message GetOrderRequest {
int64 order_id = 1;
}
message OrderVO {
int64 id = 1;
string order_no = 2;
int64 user_id = 3;
double amount = 4;
int32 status = 5;
int64 create_time = 6;
}xml
<!-- Maven 插件自动生成 Java Stub -->
<plugin>
<groupId>org.xolstice.maven.plugins</groupId>
<artifactId>protobuf-maven-plugin</artifactId>
<version>0.6.1</version>
<configuration>
<protocArtifact>com.google.protobuf:protoc:3.24.0:exe:${os.detected.classifier}</protocArtifact>
<pluginId>grpc-java</pluginId>
<pluginArtifact>io.grpc:protoc-gen-grpc-java:1.58.0:exe:${os.detected.classifier}</pluginArtifact>
</configuration>
</plugin>HTTP/2 多路复用
text
HTTP/1.1:一个连接一次只能发一个请求(串行)
┌────────┐
│ Req 1 │──────→───→ Resp 1
│ Req 2 │ (等待) → Resp 2
└────────┘
HTTP/2:一个连接同时发多个请求(多路复用)
┌────────┐
│ Req 1 │──────→ Resp 1
│ Req 2 │────→ Resp 2
│ Req 3 │──→ Resp 3
└────────┘- Stream:HTTP/2 连接内的独立双向数据流
- Frame:HTTP/2 最小通信单位(Header Frame / Data Frame)
- 多路复用减少连接数,消除队头阻塞
四种通信模式
1. Unary(一元调用)
text
Client ── Request ──→ Server
Client ←─ Response ── Serverjava
// Server
public class OrderServiceImpl extends OrderServiceGrpc.OrderServiceImplBase {
@Override
public void getOrder(GetOrderRequest request, StreamObserver<OrderVO> responseObserver) {
OrderVO vo = orderService.getById(request.getOrderId());
responseObserver.onNext(vo);
responseObserver.onCompleted();
}
}
// Client
OrderServiceGrpc.OrderServiceBlockingStub stub = OrderServiceGrpc.newBlockingStub(channel);
OrderVO vo = stub.getOrder(GetOrderRequest.newBuilder().setOrderId(100).build());2. Server Streaming(服务端流式)
text
Client ── Request ──→ Server
Client ←─ Response 1 ── Server
Client ←─ Response 2 ── Server
Client ←─ Response 3 ── Server
Server 关闭流java
// Client
Iterator<OrderVO> iterator = stub.listOrders(request);
while (iterator.hasNext()) {
OrderVO vo = iterator.next();
// 处理每个返回结果
}3. Client Streaming(客户端流式)
text
Client ── Request 1 ──→ Server
Client ── Request 2 ──→ Server
Client ── Request 3 ──→ Server
Client 关闭发送
Client ←─ Response ──── Serverjava
StreamObserver<BatchCreateResponse> responseObserver = new StreamObserver<>() {
@Override public void onNext(BatchCreateResponse resp) { /* 处理结果 */ }
@Override public void onError(Throwable t) { /* 处理错误 */ }
@Override public void onCompleted() { /* 完成 */ }
};
StreamObserver<CreateOrderRequest> requestObserver = stub.batchCreateOrder(responseObserver);
requestObserver.onNext(req1);
requestObserver.onNext(req2);
requestObserver.onNext(req3);
requestObserver.onCompleted();4. Bidirectional Streaming(双向流式)
text
Client ── Request 1 ──→ Server
Client ── Request 2 ──→ Server
Client ←─ Response 1 ── Server
Client ── Request 3 ──→ Server
Client ←─ Response 2 ── Server
... ...java
StreamObserver<OrderResult> responseObserver = new StreamObserver<>() {
@Override public void onNext(OrderResult result) { /* 实时处理服务端推送 */ }
@Override public void onError(Throwable t) { /* 处理错误 */ }
@Override public void onCompleted() { /* 完成 */ }
};
StreamObserver<OrderEvent> requestObserver = stub.processOrder(responseObserver);
requestObserver.onNext(event1);
requestObserver.onNext(event2);
// 持续双向发送
requestObserver.onCompleted();三大框架对比
| 维度 | OpenFeign | Dubbo | gRPC |
|---|---|---|---|
| 协议 | HTTP/1.1 | Dubbo 协议(TCP) | HTTP/2 |
| 序列化 | JSON(Jackson / Gson) | Hessian2 / JSON / Protobuf | Protocol Buffers |
| 服务定义 | Java 接口 + 注解 | Java 接口 + 注解 | .proto 文件(语言无关) |
| 通信模式 | 同步请求-响应 | 同步/异步请求-响应 | Unary / Server Streaming / Client Streaming / Bidirectional |
| 连接方式 | 短连接(无连接池需额外配) | 长连接复用 | 长连接(HTTP/2 多路复用) |
| 负载均衡 | Spring Cloud LoadBalancer | 内置(随机、轮询、一致性哈希等) | 客户端自行实现 |
| 服务治理 | 需整合 Sentinel / Hystrix | 内置集群容错、路由、限流 | 需额外中间件 |
| 跨语言 | 仅限 Java(HTTP 接口可被其他语言调用) | 多语言 SDK(Java、Go、Python 等) | 原生跨语言(C++、Java、Go、Python 等) |
| 性能 | 中等(JSON + HTTP 开销) | 高(二进制协议 + 长连接) | 极高(Protobuf + HTTP/2) |
| 调试便利性 | 高(HTTP + JSON 可 curl 测试) | 中(需 telnet 或控制台) | 低(二进制不可读,需 grpcurl) |
| 生态 | Spring Cloud 全家桶 | Apache Dubbo 微服务体系 | CNCF 云原生生态 |
| 适用场景 | Spring Cloud 微服务、网关、开放 API | 内部高性能 RPC、同步/异步调用 | 跨语言、流式通信、云原生服务网格(Istio) |
选型建议
text
Spring Cloud 体系(标准 HTTP/REST) ← OpenFeign
- 已有 Spring Cloud 技术栈
- 需要调试方便、对外暴露 REST API
- 性能要求中等
高性能内部 RPC ← Dubbo
- 高并发、低延迟内部调用
- 需要完善的服务治理(熔断、限流、路由)
- Java 技术栈统一
跨语言 / 流式通信 / 云原生 ← gRPC
- 多语言异构系统通信
- 需要流式处理(实时推送、大数据传输)
- 部署在 Kubernetes / Service Mesh 环境