REST 与 RPC 深度解析:从理论到实践的全面对比
REST 和 RPC 是分布式系统中两种最重要的通信范式,它们代表了不同的设计哲学和适用场景。本文将深入探讨两者的区别,并通过丰富的代码示例帮助你做出正确的技术选型。
一、基础概念:什么是 REST 和 RPC?
1.1 REST(表述性状态转移)
REST 是一种架构风格,而不是标准或协议。它基于 HTTP 协议,强调资源的表述和状态转移。
// RESTful API 设计示例
@RestController
@RequestMapping("/api/users")
public class UserRestController {
// GET /api/users/123 - 获取用户
@GetMapping("/{id}")
public ResponseEntity<User> getUser(@PathVariable Long id) {
User user = userService.findById(id);
return ResponseEntity.ok(user);
}
// POST /api/users - 创建用户
@PostMapping
public ResponseEntity<User> createUser(@RequestBody User user) {
User created = userService.create(user);
return ResponseEntity.status(HttpStatus.CREATED).body(created);
}
// PUT /api/users/123 - 更新用户
@PutMapping("/{id}")
public ResponseEntity<User> updateUser(@PathVariable Long id, @RequestBody User user) {
user.setId(id);
User updated = userService.update(user);
return ResponseEntity.ok(updated);
}
// DELETE /api/users/123 - 删除用户
@DeleteMapping("/{id}")
public ResponseEntity<Void> deleteUser(@PathVariable Long id) {
userService.delete(id);
return ResponseEntity.noContent().build();
}
// GET /api/users/123/orders - 获取用户的订单
@GetMapping("/{id}/orders")
public ResponseEntity<List<Order>> getUserOrders(@PathVariable Long id) {
List<Order> orders = orderService.findByUserId(id);
return ResponseEntity.ok(orders);
}
}
1.2 RPC(远程过程调用)
RPC 是一种协议,允许程序调用另一个地址空间(通常是另一台机器)的过程或函数,就像调用本地方法一样。
// RPC 服务定义示例
public interface UserService {
// 方法调用风格,不关心 HTTP 方法
User getUserById(Long id);
User createUser(User user);
User updateUser(User user);
void deleteUser(Long id);
List<Order> getUserOrders(Long userId);
}
// gRPC 服务定义
service UserService {
rpc GetUser (GetUserRequest) returns (UserResponse);
rpc CreateUser (CreateUserRequest) returns (UserResponse);
rpc UpdateUser (UpdateUserRequest) returns (UserResponse);
rpc DeleteUser (DeleteUserRequest) returns (google.protobuf.Empty);
rpc GetUserOrders (GetUserOrdersRequest) returns (GetUserOrdersResponse);
}
// Dubbo 服务实现
@Service
public class UserServiceImpl implements UserService {
@Override
public User getUserById(Long id) {
return userMapper.selectById(id);
}
@Override
public User createUser(User user) {
userMapper.insert(user);
return user;
}
@Override
public List<Order> getUserOrders(Long userId) {
return orderMapper.selectByUserId(userId);
}
}
二、核心设计哲学对比
2.1 REST 的设计原则
// REST 的六个约束原则体现
// 1. 客户端-服务器架构
@RestController // 明确的服务器角色
public class UserController {
// 客户端通过 HTTP 调用
}
// 2. 无状态
@GetMapping("/users/{id}")
public User getUser(@PathVariable Long id,
@RequestHeader("Authorization") String auth) {
// 每次请求必须包含所有必要信息
// 服务器不保存客户端状态
}
// 3. 可缓存
@GetMapping("/users/{id}")
@Cacheable(value = "users", key = "#id") // 响应可被缓存
public User getUser(@PathVariable Long id) {
// ...
}
// 4. 统一接口
public interface RestController {
// 使用标准的 HTTP 方法
// 资源标识符 (URI)
// 自描述消息
}
// 5. 分层系统
// 客户端 -> 负载均衡器 -> 认证网关 -> 业务服务 -> 数据库
// 6. 按需代码(可选)
@GetMapping(value = "/users/{id}", produces = MediaType.APPLICATION_JSON_VALUE)
public User getUser(@PathVariable Long id) {
// 返回 JSON 表述
}
2.2 RPC 的设计哲学
// RPC 的核心思想:像调用本地方法一样调用远程服务
// 服务接口定义
public interface CalculatorService {
// 透明性:调用者不需要知道这是远程调用
double add(double a, double b);
double subtract(double a, double b);
double multiply(double a, double b);
double divide(double a, double b);
}
// 服务实现
@Service
public class CalculatorServiceImpl implements CalculatorService {
@Override
public double add(double a, double b) {
return a + b;
}
@Override
public double divide(double a, double b) {
if (b == 0) {
// 异常处理也是透明的
throw new IllegalArgumentException("除数不能为零");
}
return a / b;
}
}
// 客户端调用
@Component
public class CalculatorClient {
@Reference // Dubbo 的远程引用
private CalculatorService calculatorService;
public void calculate() {
// 看起来像本地调用,实际上是远程调用
double result = calculatorService.add(10.5, 20.3);
System.out.println("计算结果: " + result);
try {
calculatorService.divide(10, 0);
} catch (IllegalArgumentException e) {
// 异常也是透明的
System.out.println("捕获异常: " + e.getMessage());
}
}
}
三、技术实现对比
3.1 REST 实现详解
3.1.1 Spring Boot RESTful API
// 完整的 RESTful 服务实现
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
private Long id;
private String username;
private String email;
private String phone;
private LocalDateTime createTime;
private UserStatus status;
}
enum UserStatus {
ACTIVE, INACTIVE, SUSPENDED
}
@RestController
@RequestMapping("/api/v1/users")
@Validated
@Slf4j
public class UserRestController {
@Autowired
private UserService userService;
// 查询用户列表(支持分页、过滤、排序)
@GetMapping
public ResponseEntity<PageResponse<User>> getUsers(
@RequestParam(defaultValue = "0") int page,
@RequestParam(defaultValue = "20") int size,
@RequestParam(required = false) String username,
@RequestParam(required = false) UserStatus status,
@RequestParam(defaultValue = "id,desc") String sort) {
log.info("查询用户列表 - page: {}, size: {}, username: {}, status: {}",
page, size, username, status);
PageRequest pageRequest = PageRequest.of(page, size, parseSort(sort));
Page<User> users = userService.findUsers(username, status, pageRequest);
PageResponse<User> response = PageResponse.of(users);
return ResponseEntity.ok(response);
}
// 获取单个用户
@GetMapping("/{id}")
public ResponseEntity<User> getUser(@PathVariable Long id) {
log.info("获取用户 - ID: {}", id);
User user = userService.findById(id)
.orElseThrow(() -> new ResourceNotFoundException("用户不存在"));
return ResponseEntity.ok(user);
}
// 创建用户
@PostMapping
public ResponseEntity<User> createUser(@Valid @RequestBody CreateUserRequest request) {
log.info("创建用户 - username: {}", request.getUsername());
User user = userService.create(request);
URI location = ServletUriComponentsBuilder
.fromCurrentRequest()
.path("/{id}")
.buildAndExpand(user.getId())
.toUri();
return ResponseEntity.created(location).body(user);
}
// 部分更新用户
@PatchMapping("/{id}")
public ResponseEntity<User> partialUpdateUser(
@PathVariable Long id,
@RequestBody Map<String, Object> updates) {
log.info("部分更新用户 - ID: {}, updates: {}", id, updates);
User user = userService.partialUpdate(id, updates);
return ResponseEntity.ok(user);
}
// 获取用户的订单(子资源)
@GetMapping("/{id}/orders")
public ResponseEntity<List<Order>> getUserOrders(@PathVariable Long id) {
log.info("获取用户订单 - 用户ID: {}", id);
List<Order> orders = orderService.findByUserId(id);
return ResponseEntity.ok(orders);
}
// 启用用户(特定操作)
@PostMapping("/{id}/enable")
public ResponseEntity<Void> enableUser(@PathVariable Long id) {
log.info("启用用户 - ID: {}", id);
userService.enableUser(id);
return ResponseEntity.ok().build();
}
// 自定义排序解析
private Sort parseSort(String sort) {
String[] parts = sort.split(",");
if (parts.length != 2) {
return Sort.by("id").descending();
}
String property = parts[0];
Sort.Direction direction = "desc".equalsIgnoreCase(parts[1])
? Sort.Direction.DESC
: Sort.Direction.ASC;
return Sort.by(direction, property);
}
}
// 分页响应包装类
@Data
@AllArgsConstructor
@NoArgsConstructor
public class PageResponse<T> {
private List<T> content;
private int page;
private int size;
private long totalElements;
private int totalPages;
public static <T> PageResponse<T> of(Page<T> page) {
return new PageResponse<>(
page.getContent(),
page.getNumber(),
page.getSize(),
page.getTotalElements(),
page.getTotalPages()
);
}
}
3.1.2 REST 客户端实现
// 使用 RestTemplate 的 REST 客户端
@Component
@Slf4j
public class UserRestClient {
private final RestTemplate restTemplate;
private final String baseUrl;
public UserRestClient(@Value("${user.service.url}") String baseUrl) {
this.baseUrl = baseUrl;
this.restTemplate = new RestTemplate();
// 配置 RestTemplate
restTemplate.setErrorHandler(new DefaultResponseErrorHandler() {
@Override
public void handleError(ClientHttpResponse response) throws IOException {
if (response.getStatusCode() == HttpStatus.NOT_FOUND) {
throw new ResourceNotFoundException("用户不存在");
}
super.handleError(response);
}
});
}
public User getUserById(Long id) {
String url = baseUrl + "/api/v1/users/" + id;
try {
ResponseEntity<User> response = restTemplate.getForEntity(url, User.class);
return response.getBody();
} catch (ResourceNotFoundException e) {
log.warn("用户不存在 - ID: {}", id);
return null;
}
}
public User createUser(User user) {
String url = baseUrl + "/api/v1/users";
ResponseEntity<User> response = restTemplate.postForEntity(url, user, User.class);
return response.getBody();
}
public void updateUser(Long id, User user) {
String url = baseUrl + "/api/v1/users/" + id;
restTemplate.put(url, user);
}
public void deleteUser(Long id) {
String url = baseUrl + "/api/v1/users/" + id;
restTemplate.delete(url);
}
// 带查询参数的调用
public List<User> findUsers(String username, UserStatus status) {
String url = baseUrl + "/api/v1/users";
UriComponentsBuilder builder = UriComponentsBuilder.fromHttpUrl(url);
if (username != null) {
builder.queryParam("username", username);
}
if (status != null) {
builder.queryParam("status", status);
}
ResponseEntity<User[]> response = restTemplate.getForEntity(
builder.toUriString(), User[].class);
return Arrays.asList(response.getBody());
}
}
// 使用 OpenFeign 的声明式 REST 客户端
@FeignClient(name = "user-service", url = "${user.service.url}")
public interface UserServiceClient {
@GetMapping("/api/v1/users/{id}")
ResponseEntity<User> getUserById(@PathVariable("id") Long id);
@PostMapping("/api/v1/users")
ResponseEntity<User> createUser(@RequestBody User user);
@PutMapping("/api/v1/users/{id}")
ResponseEntity<Void> updateUser(@PathVariable("id") Long id, @RequestBody User user);
@DeleteMapping("/api/v1/users/{id}")
ResponseEntity<Void> deleteUser(@PathVariable("id") Long id);
@GetMapping("/api/v1/users")
ResponseEntity<List<User>> findUsers(
@RequestParam(value = "username", required = false) String username,
@RequestParam(value = "status", required = false) UserStatus status);
}
3.2 RPC 实现详解
3.2.1 gRPC 实现
proto 文件定义:
syntax = "proto3";
package com.example.grpc;
import "google/protobuf/timestamp.proto";
import "google/protobuf/empty.proto";
service UserService {
rpc GetUser (GetUserRequest) returns (UserResponse);
rpc CreateUser (CreateUserRequest) returns (UserResponse);
rpc UpdateUser (UpdateUserRequest) returns (UserResponse);
rpc DeleteUser (DeleteUserRequest) returns (google.protobuf.Empty);
rpc ListUsers (ListUsersRequest) returns (ListUsersResponse);
rpc BatchGetUsers (BatchGetUsersRequest) returns (BatchGetUsersResponse);
}
message GetUserRequest {
int64 id = 1;
}
message CreateUserRequest {
string username = 1;
string email = 2;
string phone = 3;
}
message UpdateUserRequest {
int64 id = 1;
string username = 2;
string email = 3;
string phone = 4;
}
message DeleteUserRequest {
int64 id = 1;
}
message ListUsersRequest {
int32 page = 1;
int32 size = 2;
string username_filter = 3;
UserStatus status_filter = 4;
}
message BatchGetUsersRequest {
repeated int64 ids = 1;
}
message UserResponse {
int64 id = 1;
string username = 2;
string email = 3;
string phone = 4;
google.protobuf.Timestamp create_time = 5;
UserStatus status = 6;
}
message ListUsersResponse {
repeated UserResponse users = 1;
int32 total_pages = 2;
int64 total_elements = 3;
}
message BatchGetUsersResponse {
repeated UserResponse users = 1;
}
enum UserStatus {
ACTIVE = 0;
INACTIVE = 1;
SUSPENDED = 2;
}
gRPC 服务端实现:
// gRPC 服务实现
@Service
@Slf4j
public class UserGrpcService extends UserServiceGrpc.UserServiceImplBase {
@Autowired
private UserService userService;
@Override
public void getUser(GetUserRequest request, StreamObserver<UserResponse> responseObserver) {
log.info("gRPC - 获取用户: {}", request.getId());
try {
User user = userService.findById(request.getId())
.orElseThrow(() -> Status.NOT_FOUND
.withDescription("用户不存在")
.asRuntimeException());
UserResponse response = buildUserResponse(user);
responseObserver.onNext(response);
responseObserver.onCompleted();
} catch (Exception e) {
responseObserver.onError(e);
}
}
@Override
public void createUser(CreateUserRequest request, StreamObserver<UserResponse> responseObserver) {
log.info("gRPC - 创建用户: {}", request.getUsername());
try {
User user = new User();
user.setUsername(request.getUsername());
user.setEmail(request.getEmail());
user.setPhone(request.getPhone());
User created = userService.create(user);
UserResponse response = buildUserResponse(created);
responseObserver.onNext(response);
responseObserver.onCompleted();
} catch (Exception e) {
responseObserver.onError(Status.INTERNAL
.withDescription("创建用户失败")
.withCause(e)
.asRuntimeException());
}
}
@Override
public void listUsers(ListUsersRequest request, StreamObserver<ListUsersResponse> responseObserver) {
log.info("gRPC - 查询用户列表");
try {
PageRequest pageRequest = PageRequest.of(
request.getPage(),
request.getSize()
);
Page<User> users = userService.findUsers(
request.getUsernameFilter(),
convertStatus(request.getStatusFilter()),
pageRequest
);
ListUsersResponse response = buildListUsersResponse(users);
responseObserver.onNext(response);
responseObserver.onCompleted();
} catch (Exception e) {
responseObserver.onError(e);
}
}
@Override
public void batchGetUsers(BatchGetUsersRequest request, StreamObserver<BatchGetUsersResponse> responseObserver) {
log.info("gRPC - 批量获取用户: {}", request.getIdsList());
try {
List<User> users = userService.findByIds(request.getIdsList());
BatchGetUsersResponse response = BatchGetUsersResponse.newBuilder()
.addAllUsers(users.stream()
.map(this::buildUserResponse)
.collect(Collectors.toList()))
.build();
responseObserver.onNext(response);
responseObserver.onCompleted();
} catch (Exception e) {
responseObserver.onError(e);
}
}
private UserResponse buildUserResponse(User user) {
return UserResponse.newBuilder()
.setId(user.getId())
.setUsername(user.getUsername())
.setEmail(user.getEmail())
.setPhone(user.getPhone())
.setCreateTime(Timestamp.newBuilder()
.setSeconds(user.getCreateTime().toEpochSecond(ZoneOffset.UTC))
.build())
.setStatus(convertStatus(user.getStatus()))
.build();
}
private ListUsersResponse buildListUsersResponse(Page<User> users) {
return ListUsersResponse.newBuilder()
.addAllUsers(users.getContent().stream()
.map(this::buildUserResponse)
.collect(Collectors.toList()))
.setTotalPages(users.getTotalPages())
.setTotalElements(users.getTotalElements())
.build();
}
private com.example.grpc.UserStatus convertStatus(UserStatus status) {
switch (status) {
case ACTIVE: return com.example.grpc.UserStatus.ACTIVE;
case INACTIVE: return com.example.grpc.UserStatus.INACTIVE;
case SUSPENDED: return com.example.grpc.UserStatus.SUSPENDED;
default: return com.example.grpc.UserStatus.ACTIVE;
}
}
private UserStatus convertStatus(com.example.grpc.UserStatus status) {
switch (status) {
case ACTIVE: return UserStatus.ACTIVE;
case INACTIVE: return UserStatus.INACTIVE;
case SUSPENDED: return UserStatus.SUSPENDED;
default: return UserStatus.ACTIVE;
}
}
}
gRPC 客户端实现:
// gRPC 客户端
@Component
@Slf4j
public class UserGrpcClient {
private final UserServiceGrpc.UserServiceBlockingStub blockingStub;
private final UserServiceGrpc.UserServiceStub asyncStub;
public UserGrpcClient(@Value("${grpc.user-service.host:localhost}") String host,
@Value("${grpc.user-service.port:9090}") int port) {
ManagedChannel channel = ManagedChannelBuilder.forAddress(host, port)
.usePlaintext() // 生产环境使用 TLS
.build();
this.blockingStub = UserServiceGrpc.newBlockingStub(channel);
this.asyncStub = UserServiceGrpc.newStub(channel);
}
public User getUser(Long id) {
log.info("gRPC客户端 - 获取用户: {}", id);
try {
GetUserRequest request = GetUserRequest.newBuilder()
.setId(id)
.build();
UserResponse response = blockingStub.getUser(request);
return convertToUser(response);
} catch (StatusRuntimeException e) {
if (e.getStatus().getCode() == Status.Code.NOT_FOUND) {
log.warn("用户不存在: {}", id);
return null;
}
log.error("gRPC调用失败: {}", e.getStatus());
throw new RuntimeException("用户服务调用失败", e);
}
}
public User createUser(User user) {
log.info("gRPC客户端 - 创建用户: {}", user.getUsername());
CreateUserRequest request = CreateUserRequest.newBuilder()
.setUsername(user.getUsername())
.setEmail(user.getEmail())
.setPhone(user.getPhone())
.build();
UserResponse response = blockingStub.createUser(request);
return convertToUser(response);
}
public List<User> listUsers(int page, int size, String usernameFilter) {
log.info("gRPC客户端 - 查询用户列表");
ListUsersRequest request = ListUsersRequest.newBuilder()
.setPage(page)
.setSize(size)
.setUsernameFilter(usernameFilter != null ? usernameFilter : "")
.build();
ListUsersResponse response = blockingStub.listUsers(request);
return response.getUsersList().stream()
.map(this::convertToUser)
.collect(Collectors.toList());
}
// 异步调用
public CompletableFuture<List<User>> batchGetUsersAsync(List<Long> ids) {
log.info("gRPC客户端 - 异步批量获取用户: {}", ids);
CompletableFuture<List<User>> future = new CompletableFuture<>();
BatchGetUsersRequest request = BatchGetUsersRequest.newBuilder()
.addAllIds(ids)
.build();
asyncStub.batchGetUsers(request, new StreamObserver<BatchGetUsersResponse>() {
private final List<User> users = new ArrayList<>();
@Override
public void onNext(BatchGetUsersResponse response) {
users.addAll(response.getUsersList().stream()
.map(UserGrpcClient.this::convertToUser)
.collect(Collectors.toList()));
}
@Override
public void onError(Throwable t) {
log.error("异步批量获取用户失败", t);
future.completeExceptionally(t);
}
@Override
public void onCompleted() {
future.complete(users);
}
});
return future;
}
private User convertToUser(UserResponse response) {
User user = new User();
user.setId(response.getId());
user.setUsername(response.getUsername());
user.setEmail(response.getEmail());
user.setPhone(response.getPhone());
user.setCreateTime(LocalDateTime.ofInstant(
Instant.ofEpochSecond(response.getCreateTime().getSeconds()),
ZoneOffset.UTC
));
user.setStatus(convertFromGrpcStatus(response.getStatus()));
return user;
}
private UserStatus convertFromGrpcStatus(com.example.grpc.UserStatus status) {
switch (status) {
case ACTIVE: return UserStatus.ACTIVE;
case INACTIVE: return UserStatus.INACTIVE;
case SUSPENDED: return UserStatus.SUSPENDED;
default: return UserStatus.ACTIVE;
}
}
}
3.2.2 Dubbo RPC 实现
// Dubbo 服务接口
public interface UserRpcService {
User getUserById(Long id);
User createUser(User user);
User updateUser(User user);
void deleteUser(Long id);
PageResult<User> listUsers(int page, int size, String username, UserStatus status);
List<User> batchGetUsers(List<Long> ids);
}
// Dubbo 服务实现
@Service
@Slf4j
public class UserRpcServiceImpl implements UserRpcService {
@Autowired
private UserService userService;
@Override
public User getUserById(Long id) {
log.info("Dubbo - 获取用户: {}", id);
return userService.findById(id)
.orElseThrow(() -> new RuntimeException("用户不存在"));
}
@Override
public User createUser(User user) {
log.info("Dubbo - 创建用户: {}", user.getUsername());
return userService.create(user);
}
@Override
public PageResult<User> listUsers(int page, int size, String username, UserStatus status) {
log.info("Dubbo - 查询用户列表");
PageRequest pageRequest = PageRequest.of(page, size);
Page<User> users = userService.findUsers(username, status, pageRequest);
return PageResult.of(users.getContent(),
users.getTotalElements(),
users.getTotalPages());
}
@Override
public List<User> batchGetUsers(List<Long> ids) {
log.info("Dubbo - 批量获取用户: {}", ids);
return userService.findByIds(ids);
}
}
// Dubbo 配置
@Configuration
public class DubboConfig {
@Bean
public ApplicationConfig applicationConfig() {
ApplicationConfig config = new ApplicationConfig();
config.setName("user-service");
return config;
}
@Bean
public RegistryConfig registryConfig() {
RegistryConfig config = new RegistryConfig();
config.setAddress("nacos://localhost:8848");
return config;
}
@Bean
public ProtocolConfig protocolConfig() {
ProtocolConfig config = new ProtocolConfig();
config.setName("dubbo");
config.setPort(20880);
return config;
}
@Bean
public ServiceConfig<UserRpcService> userServiceConfig(UserRpcService userService) {
ServiceConfig<UserRpcService> service = new ServiceConfig<>();
service.setInterface(UserRpcService.class);
service.setRef(userService);
service.setVersion("1.0.0");
return service;
}
}
// Dubbo 客户端使用
@Component
@Slf4j
public class UserRpcClient {
@Reference(version = "1.0.0", check = false)
private UserRpcService userRpcService;
public void demonstrateRpcCalls() {
// 1. 获取单个用户
try {
User user = userRpcService.getUserById(1L);
log.info("获取用户成功: {}", user.getUsername());
} catch (Exception e) {
log.error("获取用户失败", e);
}
// 2. 创建用户
User newUser = new User();
newUser.setUsername("newuser");
newUser.setEmail("newuser@example.com");
User created = userRpcService.createUser(newUser);
log.info("创建用户成功, ID: {}", created.getId());
// 3. 批量查询
List<User> users = userRpcService.listUsers(0, 10, null, null).getData();
log.info("查询到 {} 个用户", users.size());
// 4. 批量获取
List<User> batchUsers = userRpcService.batchGetUsers(Arrays.asList(1L, 2L, 3L));
log.info("批量获取到 {} 个用户", batchUsers.size());
}
}
四、通信流程对比
4.1 REST 通信流程图
sequenceDiagram
participant C as REST Client
participant S as REST Server
Note over C,S: 1. 获取用户 (GET)
C->>S: GET /api/users/123
Note right of S: 解析URL路径参数<br>查询数据库
S-->>C: 200 OK + User JSON
Note over C,S: 2. 创建用户 (POST)
C->>S: POST /api/users
Note right of S: 验证请求体<br>创建用户记录
S-->>C: 201 Created + User JSON
Note over C,S: 3. 更新用户 (PUT)
C->>S: PUT /api/users/123
Note right of S: 全量更新用户信息
S-->>C: 200 OK + User JSON
Note over C,S: 4. 删除用户 (DELETE)
C->>S: DELETE /api/users/123
Note right of S: 删除用户记录
S-->>C: 204 No Content
Note over C,S: 5. 获取用户订单 (子资源)
C->>S: GET /api/users/123/orders
Note right of S: 查询关联订单
S-->>C: 200 OK + Orders JSON
4.2 RPC 通信流程图
sequenceDiagram
participant C as RPC Client
participant P as Proxy/Stub
participant N as Network
participant S as RPC Server
Note over C,S: 1. 方法调用透明化
C->>P: userService.getUser(123)
Note over P: 序列化参数<br>创建请求消息
P->>N: 发送二进制数据
Note over N: 网络传输 (TCP/HTTP2)
N->>S: 接收请求
Note over S: 反序列化<br>查找服务方法
S->>S: 执行业务逻辑
S->>N: 返回二进制响应
N->>P: 接收响应
Note over P: 反序列化结果<br>处理异常
P-->>C: 返回User对象或抛出异常
Note over C,S: 2. 批量调用示例
C->>P: userService.batchGetUsers([1,2,3])
P->>N: 批量请求
N->>S: 传输批量请求
S->>S: 批量处理逻辑
S->>N: 批量响应
N->>P: 接收批量结果
P-->>C: 返回List<User>
五、性能与特性对比
5.1 性能基准测试
// 性能测试对比
@SpringBootTest
@Slf4j
public class RestVsRpcPerformanceTest {
@Autowired
private UserRestClient restClient;
@Autowired
private UserGrpcClient grpcClient;
@Autowired
private UserRpcClient dubboClient;
private static final int WARMUP_ITERATIONS = 100;
private static final int TEST_ITERATIONS = 1000;
@Test
public void testSingleUserQuery() {
// 预热
warmup();
// REST 测试
long restTime = measureRestPerformance();
// gRPC 测试
long grpcTime = measureGrpcPerformance();
// Dubbo 测试
long dubboTime = measureDubboPerformance();
log.info("=== 单用户查询性能测试结果 ===");
log.info("REST 平均耗时: {} ms", restTime);
log.info("gRPC 平均耗时: {} ms", grpcTime);
log.info("Dubbo 平均耗时: {} ms", dubboTime);
log.info("gRPC 比 REST 快: {}%", (restTime - grpcTime) * 100 / restTime);
}
@Test
public void testBatchUsersQuery() {
List<Long> ids = Arrays.asList(1L, 2L, 3L, 4L, 5L);
long restTime = measureRestBatchPerformance(ids);
long grpcTime = measureGrpcBatchPerformance(ids);
long dubboTime = measureDubboBatchPerformance(ids);
log.info("=== 批量用户查询性能测试结果 ===");
log.info("REST 批量平均耗时: {} ms", restTime);
log.info("gRPC 批量平均耗时: {} ms", grpcTime);
log.info("Dubbo 批量平均耗时: {} ms", dubboTime);
}
private void warmup() {
for (int i = 0; i < WARMUP_ITERATIONS; i++) {
restClient.getUserById(1L);
grpcClient.getUser(1L);
dubboClient.getUserById(1L);
}
}
private long measureRestPerformance() {
long startTime = System.currentTimeMillis();
for (int i = 0; i < TEST_ITERATIONS; i++) {
restClient.getUserById((long) (i % 100 + 1));
}
return (System.currentTimeMillis() - startTime) / TEST_ITERATIONS;
}
private long measureGrpcPerformance() {
long startTime = System.currentTimeMillis();
for (int i = 0; i < TEST_ITERATIONS; i++) {
grpcClient.getUser((long) (i % 100 + 1));
}
return (System.currentTimeMillis() - startTime) / TEST_ITERATIONS;
}
// 其他测试方法...
}
5.2 特性对比表格
| 特性 | REST | gRPC | Dubbo |
|---|---|---|---|
| 通信协议 | HTTP/1.1 | HTTP/2 | 多协议支持 |
| 数据格式 | JSON/XML | Protocol Buffers | 多种序列化 |
| 性能 | 中等 | 高 | 高 |
| 浏览器支持 | 好 | 有限 | 有限 |
| 代码生成 | 可选 | 强制的 | 可选 |
| 流式支持 | 有限 | 好 | 有限 |
| 服务发现 | 需要额外组件 | 需要额外组件 | 内置 |
| 负载均衡 | 客户端/服务端 | 客户端 | 客户端 |
| 容错机制 | 需要额外实现 | 需要额外实现 | 内置 |
六、适用场景分析
6.1 REST 适用场景
// 场景1: 面向外部API
@RestController
@RequestMapping("/public/api/v1")
@Api(tags = "用户管理API")
public class PublicUserController {
@GetMapping("/users/{id}")
@ApiOperation("获取用户信息")
public ResponseEntity<UserResponse> getPublicUser(@PathVariable Long id) {
// 对外API,需要丰富的文档和标准化的错误处理
}
@PostMapping("/users")
@ApiOperation("创建用户")
public ResponseEntity<UserResponse> createPublicUser(
@Valid @RequestBody CreateUserRequest request) {
// 输入验证、API版本管理
}
}
// 场景2: 资源型操作
@RestController
@RequestMapping("/api/documents")
public class DocumentController {
@PostMapping("/{id}/publish")
public ResponseEntity<Void> publishDocument(@PathVariable Long id) {
// 发布文档 - 特定操作
}
@PostMapping("/{id}/versions")
public ResponseEntity<DocumentVersion> createVersion(
@PathVariable Long id,
@RequestBody CreateVersionRequest request) {
// 创建新版本 - 子资源操作
}
@GetMapping("/{id}/permissions")
public ResponseEntity<DocumentPermissions> getPermissions(@PathVariable Long id) {
// 获取权限 - 子资源查询
}
}
6.2 RPC 适用场景
// 场景1: 高性能内部服务调用
public interface OrderProcessingService {
// 内部服务,需要高性能
ProcessingResult processOrder(Order order);
// 批量处理
List<ProcessingResult> batchProcessOrders(List<Order> orders);
// 流式处理
StreamObserver<Order> streamProcessOrders(StreamObserver<ProcessingResult> responseObserver);
}
// 场景2: 复杂计算服务
public interface RiskCalculationService {
// 复杂的计算逻辑,不需要资源概念
RiskAssessment calculateRisk(RiskFactors factors);
// 实时风险监控
StreamObserver<MarketData> monitorRisk(StreamObserver<RiskAlert> responseObserver);
}
// 场景3: 实时通信
public interface ChatService {
// 双向流式通信
StreamObserver<ChatMessage> chat(StreamObserver<ChatMessage> responseObserver);
// 实时通知
void notifyUser(Long userId, Notification notification);
}
七、混合使用策略
7.1 API 网关模式
// API 网关 - 对外REST,对内RPC
@RestController
@RequestMapping("/api/v1")
@Slf4j
public class ApiGatewayController {
@Autowired
private UserRpcService userRpcService;
@Autowired
private OrderRpcService orderRpcService;
// 对外提供 RESTful API
@GetMapping("/users/{id}")
public ResponseEntity<UserResponse> getUser(@PathVariable Long id) {
try {
// 内部使用 RPC 调用
User user = userRpcService.getUserById(id);
UserResponse response = convertToResponse(user);
return ResponseEntity.ok(response);
} catch (Exception e) {
log.error("获取用户失败", e);
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
}
}
@GetMapping("/users/{userId}/orders")
public ResponseEntity<List<OrderResponse>> getUserOrders(
@PathVariable Long userId,
@RequestParam(defaultValue = "0") int page,
@RequestParam(defaultValue = "20") int size) {
// 组合多个 RPC 调用
User user = userRpcService.getUserById(userId);
PageResult<Order> orders = orderRpcService.getUserOrders(userId, page, size);
List<OrderResponse> responses = orders.getData().stream()
.map(order -> convertToOrderResponse(order, user))
.collect(Collectors.toList());
return ResponseEntity.ok(responses);
}
// 数据转换
private UserResponse convertToResponse(User user) {
UserResponse response = new UserResponse();
response.setId(user.getId());
response.setUsername(user.getUsername());
response.setEmail(user.getEmail());
response.setCreateTime(user.getCreateTime());
return response;
}
private OrderResponse convertToOrderResponse(Order order, User user) {
OrderResponse response = new OrderResponse();
response.setId(order.getId());
response.setOrderNumber(order.getOrderNumber());
response.setAmount(order.getAmount());
response.setUserName(user.getUsername());
return response;
}
}
7.2 BFF (Backend For Frontend) 模式
// 移动端 BFF
@RestController
@RequestMapping("/mobile/api/v1")
@Slf4j
public class MobileBffController {
@GetMapping("/user-dashboard")
public ResponseEntity<UserDashboard> getUserDashboard(@RequestHeader("User-ID") Long userId) {
// 为移动端定制的聚合接口
User user = userRpcService.getUserById(userId);
List<Order> recentOrders = orderRpcService.getRecentOrders(userId, 5);
List<Notification> notifications = notificationRpcService.getUnreadNotifications(userId);
UserDashboard dashboard = new UserDashboard();
dashboard.setUser(user);
dashboard.setRecentOrders(recentOrders);
dashboard.setNotifications(notifications);
return ResponseEntity.ok(dashboard);
}
}
// Web 端 BFF
@RestController
@RequestMapping("/web/api/v1")
@Slf4j
public class WebBffController {
@GetMapping("/admin-dashboard")
public ResponseEntity<AdminDashboard> getAdminDashboard() {
// 为Web管理端定制的聚合接口
long totalUsers = userRpcService.getUserCount();
long totalOrders = orderRpcService.getOrderCount();
SystemStats systemStats = systemRpcService.getSystemStats();
AdminDashboard dashboard = new AdminDashboard();
dashboard.setTotalUsers(totalUsers);
dashboard.setTotalOrders(totalOrders);
dashboard.setSystemStats(systemStats);
return ResponseEntity.ok(dashboard);
}
}
八、总结
8.1 核心区别总结
| 方面 | REST | RPC |
|---|---|---|
| 设计哲学 | 资源导向,状态转移 | 方法导向,过程调用 |
| 通信范式 | 基于HTTP方法的CRUD操作 | 基于接口方法的远程调用 |
| 数据格式 | 文本格式(JSON/XML) | 二进制格式(Protobuf/Thrift) |
| 耦合程度 | 松耦合(通过超媒体) | 紧耦合(需要接口定义) |
| 可发现性 | 自描述,易于探索 | 需要接口文档 |
| 性能 | 中等,文本解析开销 | 高,二进制序列化 |
| 缓存支持 | 天然支持HTTP缓存 | 需要额外实现 |
| 浏览器支持 | 完全支持 | 有限支持 |
8.2 选择指南
选择 REST 当:
- 面向外部API或第三方集成
- 需要良好的可发现性和文档
- 利用HTTP缓存机制
- 系统需要松耦合
- 浏览器客户端是主要消费者
选择 RPC 当:
- 内部服务间高性能通信
- 强类型和接口契约很重要
- 需要双向流式通信
- 系统已经采用微服务架构
- 对性能有极致要求
8.3 现代最佳实践
- 混合架构: 对外REST + 对内RPC
- API网关: 统一入口,协议转换
- BFF模式: 为不同客户端定制API
- gRPC-Web: 在浏览器中使用gRPC
- 统一认证: 无论哪种协议,统一安全机制
通过理解REST和RPC的深层区别,你可以在不同的场景中做出恰当的技术选择,构建出既满足性能要求又具备良好可维护性的分布式系统。
进一步学习建议:
- 深入学习 GraphQL 作为 REST 的替代方案
- 了解 gRPC 的流式处理和拦截器
- 掌握 API 版本管理策略
- 学习微服务通信模式
Happy API Designing! 🚀