HTTP 与 RPC 的基本概念
HTTP(HyperText Transfer Protocol)是一种应用层协议,基于请求-响应模式,最初设计用于 Web 浏览器与服务器间通信。它是无状态的,每个请求都是独立的,不依赖于之前的请求。
RPC(Remote Procedure Call)是一种远程过程调用技术,允许程序调用另一个地址空间(通常是网络中的另一台机器)的服务,就像调用本地方法一样。RPC 框架屏蔽了底层网络通信的复杂性。
HTTP 与 RPC 的本质区别
1. 通信模型差异
HTTP 是基于请求-响应的通信模型,主要面向资源。客户端通过 URL 定位资源,使用 GET、POST 等方法操作资源。
RPC 则是面向接口的远程过程调用,客户端直接调用服务器上的方法,就像调用本地函数一样,关注的是行为和能力。
2. 协议特性对比
HTTP 特性:
- 基于文本的协议,人类可读
- 无状态,客户端需在请求中携带完整上下文(如认证 token)
- 基于标准 TCP/IP,使用广泛
- RESTful 风格约定俗成
- 支持缓存、代理、负载均衡等 Web 基础设施
RPC 特性:
- 通常使用二进制编码,性能更高
- 可以保持连接状态(如 gRPC 的流式通信)
- 支持多种传输协议(TCP、HTTP/2 等)
- 强类型接口定义(如 Protobuf、Thrift)
- 大多数框架需要 IDL(接口定义语言)来定义服务
3. HTTP/2 与 gRPC 的关系
gRPC 是基于 HTTP/2 协议实现的 RPC 框架,充分利用了 HTTP/2 的多项技术优势:
- 二进制分帧层:HTTP/2 将请求和响应分解为更小的二进制帧,实现真正的请求-响应多路复用
- 头部压缩:HPACK 算法压缩 HTTP 头,减少网络传输量
- 多路复用:单个 TCP 连接上可以同时发起多个请求,解决了 HTTP/1.1 的队头阻塞问题
- 服务器推送:服务端可主动向客户端推送资源,gRPC 的双向流就是基于此特性
这些特性使 gRPC 在单连接下的并发处理能力远超 HTTP/1.1。
4. 序列化协议对比
RPC 和 HTTP 在序列化协议上的差异是性能差距的主要来源之一:
| 序列化协议 | 数据大小 | 序列化耗时 | 反序列化耗时 | 特点 |
|---|---|---|---|---|
| JSON(HTTP) | 100% | 100% | 100% | 人类可读、通用性强 |
| Protobuf(gRPC) | 35% | 25% | 30% | 二进制、高效、强类型 |
| Thrift | 40% | 30% | 35% | 二进制、跨语言、IDL 定义 |
| Avro | 38% | 40% | 45% | 动态 schema、兼容性好 |
以上数据基于相同结构、相同数据量的测试结果对比。可以看到 Protobuf 序列化后的二进制数据体积通常比 JSON 小 60-65%,解析速度快 3-4 倍。
具体案例分析
HTTP 应用案例
以下是一个使用 Spring Boot 创建 RESTful API 的示例,添加了 OpenAPI 规范和标准化的异常处理:
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.web.bind.annotation.*;
import org.springframework.http.*;
import org.springframework.http.ProblemDetail;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import java.time.Instant;
import java.util.HashMap;
import java.util.Map;
// Maven依赖
// <dependency>
// <groupId>org.springdoc</groupId>
// <artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
// <version>2.1.0</version>
// </dependency>
@SpringBootApplication
public class HttpServiceApplication {
public static void main(String[] args) {
SpringApplication.run(HttpServiceApplication.class, args);
}
}
@RestController
@RequestMapping("/api/users")
@Tag(name = "用户管理", description = "用户相关的CRUD操作")
class UserController {
private Map<Long, User> userMap = new HashMap<>();
// 构造函数中添加示例数据
public UserController() {
userMap.put(1L, new User(1L, "张三", 30));
userMap.put(2L, new User(2L, "李四", 25));
}
@Operation(summary = "根据ID获取用户", description = "返回指定ID的用户详细信息")
@GetMapping("/{id}")
public User getUserById(@PathVariable Long id) {
User user = userMap.get(id);
if (user == null) {
throw new ResourceNotFoundException("用户不存在: " + id);
}
return user;
}
@Operation(summary = "创建新用户", description = "创建用户并返回创建的用户信息")
@PostMapping
@ResponseStatus(HttpStatus.CREATED)
public User createUser(@RequestBody User user) {
userMap.put(user.getId(), user);
return user;
}
@ExceptionHandler(ResourceNotFoundException.class)
public ResponseEntity<ProblemDetail> handleResourceNotFound(
ResourceNotFoundException ex) {
ProblemDetail problemDetail = ProblemDetail.forStatusAndDetail(
HttpStatus.NOT_FOUND, ex.getMessage());
problemDetail.setTitle("资源未找到");
problemDetail.setProperty("timestamp", Instant.now());
return ResponseEntity.status(HttpStatus.NOT_FOUND).body(problemDetail);
}
}
class User {
private Long id;
private String name;
private int age;
public User() {}
public User(Long id, String name, int age) {
this.id = id;
this.name = name;
this.age = age;
}
// Getter和Setter方法
public Long getId() { return id; }
public void setId(Long id) { this.id = id; }
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public int getAge() { return age; }
public void setAge(int age) { this.age = age; }
}
class ResourceNotFoundException extends RuntimeException {
public ResourceNotFoundException(String message) {
super(message);
}
}
HTTP 客户端调用示例:
import org.springframework.web.client.RestTemplate;
import org.springframework.http.*;
import org.springframework.web.client.HttpClientErrorException;
public class HttpClientExample {
public static void main(String[] args) {
RestTemplate restTemplate = new RestTemplate();
try {
// 获取用户
String url = "http://localhost:8080/api/users/1";
ResponseEntity<User> response = restTemplate.getForEntity(url, User.class);
User user = response.getBody();
System.out.println("获取到用户: " + user.getName());
// 创建用户
User newUser = new User(3L, "王五", 28);
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
HttpEntity<User> request = new HttpEntity<>(newUser, headers);
ResponseEntity<User> createResponse = restTemplate.postForEntity(
"http://localhost:8080/api/users", request, User.class);
if (createResponse.getStatusCode() == HttpStatus.CREATED) {
System.out.println("创建用户成功: " + createResponse.getBody().getName());
}
} catch (HttpClientErrorException e) {
System.err.println("HTTP错误: " + e.getStatusCode() + " - " + e.getResponseBodyAsString());
} catch (Exception e) {
System.err.println("发生异常: " + e.getMessage());
}
}
static class User {
private Long id;
private String name;
private int age;
// Getter和Setter方法
public Long getId() { return id; }
public void setId(Long id) { this.id = id; }
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public int getAge() { return age; }
public void setAge(int age) { this.age = age; }
}
}
RPC 应用案例
使用 gRPC 框架的示例,包含流式通信和连接池优化:
首先,定义服务接口(使用 Protocol Buffers):
syntax = "proto3";
option java_multiple_files = true;
option java_package = "com.example.grpc.user";
service UserService {
// 普通RPC调用
rpc GetUser(GetUserRequest) returns (User) {}
rpc CreateUser(User) returns (User) {}
// 服务器流式RPC - 获取用户列表
rpc ListUsers(ListUsersRequest) returns (stream User) {}
// 双向流式RPC - 聊天服务
rpc Chat(stream ChatMessage) returns (stream ChatMessage) {}
}
message GetUserRequest {
int64 id = 1 [json_name="id"];
}
message ListUsersRequest {
int32 page_size = 1 [json_name="pageSize"];
int32 page_number = 2 [json_name="pageNumber"];
}
message User {
int64 id = 1 [json_name="id"];
string name = 2 [json_name="name"];
int32 age = 3 [json_name="age"];
}
message ChatMessage {
string sender = 1 [json_name="sender"];
string content = 2 [json_name="content"];
int64 timestamp = 3 [json_name="timestamp"];
}
编译 proto 文件的命令:
protoc --java_out=src/main/java --grpc-java_out=src/main/java user.proto
服务端实现(增加了流式 API 和线程安全的连接管理):
import io.grpc.Server;
import io.grpc.ServerBuilder;
import io.grpc.stub.StreamObserver;
import com.example.grpc.user.*;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
import java.util.logging.Logger;
// Maven依赖
// <dependency>
// <groupId>io.grpc</groupId>
// <artifactId>grpc-netty-shaded</artifactId>
// <version>1.53.0</version>
// </dependency>
// <dependency>
// <groupId>io.grpc</groupId>
// <artifactId>grpc-protobuf</artifactId>
// <version>1.53.0</version>
// </dependency>
// <dependency>
// <groupId>io.grpc</groupId>
// <artifactId>grpc-stub</artifactId>
// <version>1.53.0</version>
// </dependency>
public class UserServiceServer {
private static final Logger logger = Logger.getLogger(UserServiceServer.class.getName());
private Server server;
private void start() throws IOException {
int port = 50051;
server = ServerBuilder.forPort(port)
.addService(new UserServiceImpl())
.maxInboundMessageSize(10 * 1024 * 1024) // 支持大消息
.build()
.start();
logger.info("服务器启动,监听端口: " + port);
Runtime.getRuntime().addShutdownHook(new Thread() {
@Override
public void run() {
try {
UserServiceServer.this.stop();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
}
private void stop() throws InterruptedException {
if (server != null) {
server.shutdown().awaitTermination(30, TimeUnit.SECONDS);
}
}
private void blockUntilShutdown() throws InterruptedException {
if (server != null) {
server.awaitTermination();
}
}
public static void main(String[] args) throws IOException, InterruptedException {
final UserServiceServer server = new UserServiceServer();
server.start();
server.blockUntilShutdown();
}
static class UserServiceImpl extends UserServiceGrpc.UserServiceImplBase {
private Map<Long, User> userMap = new HashMap<>();
// 线程安全的客户端连接集合,用于并发管理聊天客户端
private Map<StreamObserver<ChatMessage>, String> chatClients = new ConcurrentHashMap<>();
public UserServiceImpl() {
userMap.put(1L, User.newBuilder().setId(1).setName("张三").setAge(30).build());
userMap.put(2L, User.newBuilder().setId(2).setName("李四").setAge(25).build());
}
@Override
public void getUser(GetUserRequest request, StreamObserver<User> responseObserver) {
long userId = request.getId();
User user = userMap.get(userId);
if (user != null) {
responseObserver.onNext(user);
} else {
responseObserver.onError(new RuntimeException("用户不存在: " + userId));
return;
}
responseObserver.onCompleted();
}
@Override
public void createUser(User request, StreamObserver<User> responseObserver) {
userMap.put(request.getId(), request);
responseObserver.onNext(request);
responseObserver.onCompleted();
}
@Override
public void listUsers(ListUsersRequest request, StreamObserver<User> responseObserver) {
int pageSize = request.getPageSize();
int pageNumber = request.getPageNumber();
// 简单分页逻辑
userMap.values().stream()
.skip((pageNumber - 1) * pageSize)
.limit(pageSize)
.forEach(responseObserver::onNext);
responseObserver.onCompleted();
}
@Override
public StreamObserver<ChatMessage> chat(StreamObserver<ChatMessage> responseObserver) {
// 双向流处理
return new StreamObserver<ChatMessage>() {
@Override
public void onNext(ChatMessage message) {
// 注册新用户
if (!chatClients.containsValue(message.getSender())) {
chatClients.put(responseObserver, message.getSender());
}
// 广播消息给所有连接的客户端
ChatMessage broadcastMessage = ChatMessage.newBuilder()
.setSender(message.getSender())
.setContent(message.getContent())
.setTimestamp(System.currentTimeMillis())
.build();
chatClients.forEach((observer, name) -> {
observer.onNext(broadcastMessage);
});
}
@Override
public void onError(Throwable t) {
logger.warning("Chat错误: " + t.getMessage());
chatClients.remove(responseObserver);
}
@Override
public void onCompleted() {
chatClients.remove(responseObserver);
responseObserver.onCompleted();
}
};
}
}
}
客户端实现(增加了连接池优化与超时控制):
import io.grpc.*;
import io.grpc.stub.StreamObserver;
import com.example.grpc.user.*;
import java.util.Iterator;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.logging.Logger;
import java.util.Scanner;
public class UserServiceClient {
private static final Logger logger = Logger.getLogger(UserServiceClient.class.getName());
private final ManagedChannel channel;
private final UserServiceGrpc.UserServiceBlockingStub blockingStub;
private final UserServiceGrpc.UserServiceStub asyncStub;
public UserServiceClient(String target) {
// 高级客户端配置,适用于生产环境
this.channel = ManagedChannelBuilder.forTarget(target)
.usePlaintext()
.defaultLoadBalancingPolicy("round_robin") // 负载均衡策略
.enableRetry() // 启用重试
.maxRetryAttempts(5) // 最大重试次数
.retryInitialBackoff(100, TimeUnit.MILLISECONDS) // 初始重试延迟
.retryMaxBackoff(1, TimeUnit.SECONDS) // 最大重试延迟
.keepAliveTime(10, TimeUnit.SECONDS) // 保持连接活跃时间
.keepAliveTimeout(5, TimeUnit.SECONDS) // 保持连接超时
.maxInboundMessageSize(10 * 1024 * 1024) // 大消息支持
.build();
this.blockingStub = UserServiceGrpc.newBlockingStub(channel);
this.asyncStub = UserServiceGrpc.newStub(channel);
}
public void shutdown() throws InterruptedException {
channel.shutdown().awaitTermination(5, TimeUnit.SECONDS);
}
public void getUser(long id) {
GetUserRequest request = GetUserRequest.newBuilder().setId(id).build();
try {
User response = blockingStub.getUser(request);
logger.info("获取到用户: " + response.getName());
} catch (StatusRuntimeException e) {
logger.warning("RPC调用失败: " + e.getStatus());
}
}
public void createUser(long id, String name, int age) {
User request = User.newBuilder().setId(id).setName(name).setAge(age).build();
try {
User response = blockingStub.createUser(request);
logger.info("创建用户成功: " + response.getName());
} catch (StatusRuntimeException e) {
logger.warning("RPC调用失败: " + e.getStatus());
}
}
public void listUsers(int pageSize, int pageNumber) {
ListUsersRequest request = ListUsersRequest.newBuilder()
.setPageSize(pageSize)
.setPageNumber(pageNumber)
.build();
try {
Iterator<User> users = blockingStub.listUsers(request);
while (users.hasNext()) {
User user = users.next();
logger.info("用户: " + user.getId() + " - " + user.getName());
}
} catch (StatusRuntimeException e) {
logger.warning("RPC调用失败: " + e.getStatus());
}
}
// 演示双向流聊天客户端
public void startChat(String username) {
final CountDownLatch finishLatch = new CountDownLatch(1);
StreamObserver<ChatMessage> requestObserver = asyncStub.chat(
new StreamObserver<ChatMessage>() {
@Override
public void onNext(ChatMessage message) {
logger.info(message.getSender() + ": " + message.getContent());
}
@Override
public void onError(Throwable t) {
logger.warning("聊天出错: " + t.getMessage());
finishLatch.countDown();
}
@Override
public void onCompleted() {
logger.info("聊天结束");
finishLatch.countDown();
}
});
// 发送消息示例
Scanner scanner = new Scanner(System.in);
logger.info("开始聊天,输入'exit'退出");
try {
// 发送第一条消息加入聊天
sendChatMessage(requestObserver, username, "大家好,我是" + username);
String line;
while (scanner.hasNextLine()) {
line = scanner.nextLine();
if ("exit".equals(line)) {
break;
}
sendChatMessage(requestObserver, username, line);
}
} catch (Exception e) {
logger.warning("发送消息失败: " + e.getMessage());
requestObserver.onError(e);
}
requestObserver.onCompleted();
try {
// 添加30秒超时控制,避免永久阻塞
if (!finishLatch.await(30, TimeUnit.SECONDS)) {
logger.warning("聊天会话超时");
}
} catch (InterruptedException e) {
logger.warning("聊天被中断");
}
}
private void sendChatMessage(StreamObserver<ChatMessage> requestObserver,
String sender, String content) {
requestObserver.onNext(ChatMessage.newBuilder()
.setSender(sender)
.setContent(content)
.setTimestamp(System.currentTimeMillis())
.build());
}
public static void main(String[] args) throws Exception {
// 支持服务发现/注册中心场景
UserServiceClient client = new UserServiceClient("discovery:///user-service");
try {
// 演示各种RPC调用
client.getUser(1);
client.createUser(3, "王五", 28);
client.listUsers(10, 1);
// 可选启动聊天演示
if (args.length > 0) {
client.startChat(args[0]);
}
} finally {
client.shutdown();
}
}
}
混合架构实践
在实际生产环境中,通常会采用混合架构模式,结合 HTTP 和 RPC 各自的优势。最常见的是"API 网关+内部 RPC"模式:
这种架构的优势在于:
- 外部流量通过 API 网关统一处理 HTTP 请求,对外提供标准 REST 接口
- 网关内部通过高性能 RPC 调用微服务
- 微服务之间也使用 RPC 通信,提高性能
- 服务网格层统一处理服务间通信的安全、监控和流量控制
Spring Cloud Gateway 与 gRPC 集成示例(含认证和限流):
import org.springframework.cloud.gateway.route.RouteLocator;
import org.springframework.cloud.gateway.route.builder.RouteLocatorBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.web.server.ResponseStatusException;
@Configuration
public class GatewayConfig {
@Bean
public RouteLocator customRouteLocator(RouteLocatorBuilder builder) {
return builder.routes()
.route("user-service", r -> r.path("/api/users/**")
.filters(f -> f
// JWT认证过滤
.filter((exchange, chain) -> {
ServerHttpRequest request = exchange.getRequest();
if (!request.getHeaders().containsKey("Authorization")) {
throw new ResponseStatusException(HttpStatus.UNAUTHORIZED, "未认证");
}
return chain.filter(exchange);
})
// 限流配置
.requestRateLimiter(c -> c
.setRateLimiter(redisRateLimiter())
.setKeyResolver(exchange ->
exchange.getRequest().getRemoteAddress().getAddress().getHostAddress()))
// 重写路径
.rewritePath("/api/users/(?<id>.*)", "/users/${id}"))
.uri("grpc://user-service:50051"))
.build();
}
@Bean
public RedisRateLimiter redisRateLimiter() {
return new RedisRateLimiter(10, 20); // 每秒请求数和突发请求数
}
}
使用 gRPC-Web 实现浏览器直接调用 gRPC 服务:
// 浏览器端gRPC-Web客户端
import {grpc} from '@improbable-eng/grpc-web';
import {UserService} from './user_service_pb_service';
import {GetUserRequest} from './user_service_pb';
const client = new UserService('https://api.example.com');
const request = new GetUserRequest();
request.setId(123);
client.getUser(request, {}, (err, response) => {
if (err) {
console.error('调用出错:', err);
} else {
console.log('用户名:', response.getName());
console.log('年龄:', response.getAge());
}
});
网络层优化对比
HTTP 和 RPC 框架在网络层的优化上也有明显差异:
TCP 参数调优
gRPC 默认启用 TCP_NODELAY(禁用 Nagle 算法),减少小数据包传输延迟;而 HTTP 服务通常需手动配置。在高并发场景下,RPC 服务还可通过调整以下参数进一步优化性能:
// gRPC服务端TCP优化示例
NettyServerBuilder.forPort(port)
.withChildOption(ChannelOption.TCP_NODELAY, true)
.withChildOption(ChannelOption.SO_KEEPALIVE, true)
.withChildOption(ChannelOption.SO_REUSEADDR, true)
.withChildOption(ChannelOption.SO_RCVBUF, 1048576) // 1MB接收缓冲区
.withChildOption(ChannelOption.SO_SNDBUF, 1048576) // 1MB发送缓冲区
.build();
QUIC 协议影响
HTTP/3 基于 QUIC 协议(UDP),在弱网络环境下(如 4G/5G 移动网络)的丢包重传效率比 TCP 高 30%。测试显示,在 20%丢包率的网络环境中,基于 HTTP/3 的 API 响应时间比 HTTP/2 低 40%。部分 RPC 框架如 gRPC-Web 已开始支持 QUIC,这对移动场景下的性能提升非常明显。
Serverless 与通信模型
在 Serverless 架构中,HTTP 因冷启动友好(无需长连接维护)更受看重。而 RPC 框架需要特别优化连接池管理才能适应函数计算的短暂生命周期:
- HTTP 适应性:无需维护长连接,函数实例可以随时销毁和创建
- RPC 挑战:gRPC 长连接在函数实例频繁创建销毁时可能造成连接泄漏
- 优化方案:使用连接池共享模式,将 gRPC 的 ManagedChannel 设计为跨函数调用复用
// Serverless环境下的gRPC连接池优化
public class GrpcConnectionPool {
private static final Map<String, ManagedChannel> CHANNELS = new ConcurrentHashMap<>();
public static ManagedChannel getChannel(String target) {
return CHANNELS.computeIfAbsent(target, key ->
ManagedChannelBuilder.forTarget(key)
.usePlaintext()
.keepAliveTime(5, TimeUnit.SECONDS) // 短保活时间
.build());
}
// Lambda函数结束前调用
public static void shutdown() {
CHANNELS.values().forEach(channel -> {
try {
channel.shutdown().awaitTermination(100, TimeUnit.MILLISECONDS);
} catch (Exception ignored) {}
});
}
}
服务治理对比
HTTP 和 RPC 在微服务治理方面的能力对比:
| 功能 | HTTP 实现(Spring Cloud) | RPC 实现(Dubbo/gRPC) |
|---|---|---|
| 服务注册 | Eureka Client 主动注册 | 服务提供者向注册中心推送 |
| 服务发现 | 服务消费者查询注册中心 | 注册中心推送服务列表到消费者 |
| 负载均衡 | Ribbon 客户端负载均衡 | 消费者基于服务列表路由 |
| 熔断降级 | Resilience4j/Hystrix | Sentinel/Dubbo Filter |
| 配置中心 | Spring Cloud Config | Nacos/Apollo |
| 链路追踪 | Spring Cloud Sleuth | Dubbo Tracing/SkyWalking |
| 流量镜像 | Spring Cloud Gateway Filter | Dubbo Service Mesh + VirtualService |
| 版本路由 | Spring Cloud Gateway Predicate | Dubbo 路由规则 + DestinationRule |
Istio 服务网格集成示例
# gRPC服务的Istio VirtualService配置
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: user-service
spec:
hosts:
- user-service
http:
- match:
- uri:
prefix: "/api/users"
route:
- destination:
host: user-service
port:
number: 50051
weight: 90
- destination:
host: user-service-canary
port:
number: 50051
weight: 10
选型决策
多语言生态最佳实践
在微服务体系中,多语言协作是常见需求。以下是跨语言接口设计原则:
- 使用 Protobuf 定义接口:确保多语言兼容性
- 基本类型优先:避免语言特定类型(如 Java 的 BigDecimal)
- 字段命名规范化:使用下划线命名+JSON 映射注解适配不同语言
// 多语言友好的Protobuf定义示例
message OrderDetail {
string order_id = 1 [json_name="orderId"]; // 下划线命名+JSON驼峰映射
int64 user_id = 2 [json_name="userId"];
double total_amount = 3 [json_name="totalAmount"]; // 使用double替代BigDecimal
enum OrderStatus {
UNKNOWN = 0; // 始终从0开始,便于各语言映射
CREATED = 1;
PAID = 2;
SHIPPED = 3;
COMPLETED = 4;
}
OrderStatus status = 4 [json_name="status"];
}
总结
| 对比维度 | HTTP | RPC |
|---|---|---|
| 设计理念 | 面向资源 | 面向接口的远程过程调用 |
| 数据格式 | 通常是 JSON/XML | 通常是二进制格式 |
| 可读性 | 高,人类可读 | 低,机器优化 |
| 传输效率 | 中等 | 高 |
| 接口定义 | 松散,基于 URL 和方法 | 严格,基于接口定义 |
| 跨平台性 | 优秀 | 一般(依赖特定框架) |
| 开发难度 | 低 | 中等 |
| 学习成本 | 低 | 中等 |
| 典型框架 | Spring MVC, JAX-RS | gRPC, Dubbo, Thrift |
| 适用场景 | 外部 API,异构系统集成 | 内部服务间通信,高性能场景 |
| 部署模式 | 灵活,支持 Serverless | 通常需要长连接,需额外优化 |