RestTemplate、OpenFeign、WebClient 服务间通信实战
在微服务架构中,服务间通信是核心功能。主要有以下几种方式:
RestTemplate 是 Spring 提供的同步 HTTP 客户端,简单易用。
@Configuration
public class RestTemplateConfig {
@Bean
@LoadBalanced // 启用负载均衡
public RestTemplate restTemplate() {
return new RestTemplate();
}
// 自定义配置
@Bean
public RestTemplate customRestTemplate() {
SimpleClientHttpRequestFactory factory = new SimpleClientHttpRequestFactory();
factory.setConnectTimeout(5000); // 连接超时5秒
factory.setReadTimeout(10000); // 读取超时10秒
return new RestTemplate(factory);
}
}
@Service
public class UserService {
@Autowired
private RestTemplate restTemplate;
// 1. getForObject - 直接返回对象
public User getUserById(Long id) {
String url = "http://user-service/api/users/" + id;
return restTemplate.getForObject(url, User.class);
}
// 2. getForEntity - 返回 ResponseEntity
public ResponseEntity<User> getUserWithStatus(Long id) {
String url = "http://user-service/api/users/" + id;
return restTemplate.getForEntity(url, User.class);
}
// 3. 带参数的 GET 请求
public List<User> searchUsers(String keyword, Integer page) {
String url = "http://user-service/api/users/search?keyword={keyword}&page={page}";
Map<String, Object> params = new HashMap<>();
params.put("keyword", keyword);
params.put("page", page);
return restTemplate.exchange(
url,
HttpMethod.GET,
null,
new ParameterizedTypeReference<List<User>>() {},
params
).getBody();
}
}
@Service
public class OrderService {
@Autowired
private RestTemplate restTemplate;
// 1. postForObject - 创建订单
public Order createOrder(OrderRequest request) {
String url = "http://order-service/api/orders";
return restTemplate.postForObject(url, request, Order.class);
}
// 2. postForEntity - 获取完整响应
public ResponseEntity<Order> createOrderWithStatus(OrderRequest request) {
String url = "http://order-service/api/orders";
return restTemplate.postForEntity(url, request, Order.class);
}
// 3. 带请求头的 POST 请求
public Order createOrderWithHeaders(OrderRequest request, String token) {
String url = "http://order-service/api/orders";
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
headers.set("Authorization", "Bearer " + token);
HttpEntity<OrderRequest> entity = new HttpEntity<>(request, headers);
return restTemplate.postForObject(url, entity, Order.class);
}
}
@Service
public class ProductService {
@Autowired
private RestTemplate restTemplate;
// PUT 请求 - 更新产品
public void updateProduct(Long id, Product product) {
String url = "http://product-service/api/products/" + id;
restTemplate.put(url, product);
}
// DELETE 请求 - 删除产品
public void deleteProduct(Long id) {
String url = "http://product-service/api/products/" + id;
restTemplate.delete(url);
}
// exchange 方法 - 通用请求
public ResponseEntity<Product> updateProductWithResponse(Long id, Product product) {
String url = "http://product-service/api/products/" + id;
HttpEntity<Product> entity = new HttpEntity<>(product);
return restTemplate.exchange(
url,
HttpMethod.PUT,
entity,
Product.class
);
}
}
OpenFeign 是声明式的 HTTP 客户端,通过接口和注解定义服务调用,代码更简洁。
// pom.xml 依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
// 启动类启用 Feign
@SpringBootApplication
@EnableFeignClients
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
// application.yml 配置
feign:
client:
config:
default:
connectTimeout: 5000
readTimeout: 10000
loggerLevel: full
compression:
request:
enabled: true
response:
enabled: true
@FeignClient(name = "user-service", path = "/api/users")
public interface UserServiceClient {
// GET 请求
@GetMapping("/{id}")
User getUserById(@PathVariable("id") Long id);
// GET 请求带参数
@GetMapping("/search")
List<User> searchUsers(@RequestParam("keyword") String keyword,
@RequestParam("page") Integer page);
// POST 请求
@PostMapping
User createUser(@RequestBody User user);
// PUT 请求
@PutMapping("/{id}")
User updateUser(@PathVariable("id") Long id, @RequestBody User user);
// DELETE 请求
@DeleteMapping("/{id}")
void deleteUser(@PathVariable("id") Long id);
// 带请求头
@GetMapping("/{id}")
User getUserWithToken(@PathVariable("id") Long id,
@RequestHeader("Authorization") String token);
}
@Service
public class OrderService {
@Autowired
private UserServiceClient userServiceClient;
@Autowired
private ProductServiceClient productServiceClient;
public OrderDTO createOrder(OrderRequest request) {
// 调用用户服务
User user = userServiceClient.getUserById(request.getUserId());
if (user == null) {
throw new BusinessException("用户不存在");
}
// 调用产品服务
Product product = productServiceClient.getProductById(request.getProductId());
if (product == null) {
throw new BusinessException("产品不存在");
}
// 检查库存
if (product.getStock() < request.getQuantity()) {
throw new BusinessException("库存不足");
}
// 创建订单
Order order = new Order();
order.setUserId(user.getId());
order.setProductId(product.getId());
order.setQuantity(request.getQuantity());
order.setTotalPrice(product.getPrice() * request.getQuantity());
// 保存订单并返回
return orderRepository.save(order);
}
}
// 自定义 Feign 拦截器
@Component
public class FeignRequestInterceptor implements RequestInterceptor {
@Override
public void apply(RequestTemplate template) {
// 添加请求头
template.header("X-Request-Id", UUID.randomUUID().toString());
// 从上下文获取 Token
ServletRequestAttributes attributes =
(ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
if (attributes != null) {
HttpServletRequest request = attributes.getRequest();
String token = request.getHeader("Authorization");
if (token != null) {
template.header("Authorization", token);
}
}
}
}
// 配置拦截器
@Configuration
public class FeignConfig {
@Bean
public RequestInterceptor requestInterceptor() {
return new FeignRequestInterceptor();
}
// 日志级别
@Bean
Logger.Level feignLoggerLevel() {
return Logger.Level.FULL;
}
}
WebClient 是 Spring 5 引入的响应式 HTTP 客户端,支持异步非阻塞调用。
// pom.xml 依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
@Configuration
public class WebClientConfig {
@Bean
public WebClient webClient() {
return WebClient.builder()
.baseUrl("http://localhost:8080")
.defaultHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
.defaultHeader("User-Agent", "Spring WebClient")
.build();
}
// 带负载均衡的 WebClient
@Bean
@LoadBalanced
public WebClient.Builder loadBalancedWebClientBuilder() {
return WebClient.builder();
}
}
@Service
public class ReactiveUserService {
@Autowired
private WebClient.Builder webClientBuilder;
// GET 请求 - 返回 Mono
public Mono<User> getUserById(Long id) {
return webClientBuilder.build()
.get()
.uri("http://user-service/api/users/{id}", id)
.retrieve()
.bodyToMono(User.class);
}
// GET 请求 - 返回 Flux
public Flux<User> getAllUsers() {
return webClientBuilder.build()
.get()
.uri("http://user-service/api/users")
.retrieve()
.bodyToFlux(User.class);
}
// POST 请求
public Mono<User> createUser(User user) {
return webClientBuilder.build()
.post()
.uri("http://user-service/api/users")
.bodyValue(user)
.retrieve()
.bodyToMono(User.class);
}
// 带错误处理
public Mono<User> getUserWithErrorHandling(Long id) {
return webClientBuilder.build()
.get()
.uri("http://user-service/api/users/{id}", id)
.retrieve()
.onStatus(HttpStatus::is4xxClientError,
response -> Mono.error(new BusinessException("客户端错误")))
.onStatus(HttpStatus::is5xxServerError,
response -> Mono.error(new BusinessException("服务器错误")))
.bodyToMono(User.class)
.timeout(Duration.ofSeconds(5))
.retry(3);
}
// 并行调用多个服务
public Mono<OrderDTO> getOrderDetails(Long orderId) {
Mono<Order> orderMono = getOrder(orderId);
Mono<User> userMono = orderMono.flatMap(order -> getUserById(order.getUserId()));
Mono<Product> productMono = orderMono.flatMap(order -> getProductById(order.getProductId()));
return Mono.zip(orderMono, userMono, productMono)
.map(tuple -> {
OrderDTO dto = new OrderDTO();
dto.setOrder(tuple.getT1());
dto.setUser(tuple.getT2());
dto.setProduct(tuple.getT3());
return dto;
});
}
}
Q: RestTemplate、Feign、WebClient 如何选择?
A: RestTemplate 适合简单场景,Feign 适合复杂的服务调用(推荐),WebClient 适合高并发响应式场景。
Q: 如何处理服务调用超时?
A: 设置合理的超时时间,实现重试机制,结合熔断降级保护系统。超时时间应根据业务场景设置,一般 3-10 秒。
服务调用是微服务通信的核心。通过本课学习,你应该掌握: