← Назад к вопросам
Какие знаешь инструменты для отправки запроса в другой сервис?
2.0 Middle🔥 221 комментариев
#REST API и микросервисы
Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Инструменты для отправки запроса в другой сервис
В микросервисной архитектуре Java приложения постоянно обращаются к другим сервисам. Есть множество инструментов для выполнения HTTP запросов.
1. RestTemplate - классический Spring подход
Автоматически управляет connection pooling и содержит удобные методы:
@Service
public class UserServiceClient {
private final RestTemplate restTemplate;
private final String apiUrl = "http://user-service:8080/api/v1";
public UserServiceClient(RestTemplate restTemplate) {
this.restTemplate = restTemplate;
}
// GET запрос
public User getUserById(Long id) {
String url = apiUrl + "/users/{id}";
return restTemplate.getForObject(url, User.class, id);
}
// POST запрос
public User createUser(UserRequest request) {
String url = apiUrl + "/users";
return restTemplate.postForObject(url, request, User.class);
}
// Запрос с обработкой ошибок
public ResponseEntity<User> getUserSafe(Long id) {
String url = apiUrl + "/users/{id}";
try {
return restTemplate.getForEntity(url, User.class, id);
} catch (RestClientException e) {
log.error("Failed to fetch user", e);
return ResponseEntity.notFound().build();
}
}
// Запрос с заголовками
public User getUserWithHeaders(Long id) {
String url = apiUrl + "/users/{id}";
HttpHeaders headers = new HttpHeaders();
headers.set("Authorization", "Bearer token");
headers.set("Content-Type", "application/json");
HttpEntity<Void> entity = new HttpEntity<>(headers);
ResponseEntity<User> response = restTemplate.exchange(
url,
HttpMethod.GET,
entity,
User.class,
id
);
return response.getBody();
}
}
// Конфигурация RestTemplate
@Configuration
public class RestTemplateConfig {
@Bean
public RestTemplate restTemplate(RestTemplateBuilder builder) {
return builder
.setConnectTimeout(Duration.ofSeconds(5))
.setReadTimeout(Duration.ofSeconds(10))
.defaultHeader("User-Agent", "MyApp/1.0")
.build();
}
}
Преимущества:
- Встроена в Spring
- Простая в использовании
- Автоматический сериализация/десериализация
- Connection pooling из коробки
Недостатки:
- Синхронная (блокирует поток)
- Меньше контроля
- Deprecated в новых версиях Spring (см. WebClient)
2. WebClient - асинхронный подход (рекомендуется)
Современная асинхронная альтернатива RestTemplate:
@Service
public class ReactiveUserServiceClient {
private final WebClient webClient;
public ReactiveUserServiceClient(WebClient.Builder webClientBuilder) {
this.webClient = webClientBuilder
.baseUrl("http://user-service:8080/api/v1")
.build();
}
// GET запрос с реактивным ответом
public Mono<User> getUserById(Long id) {
return webClient
.get()
.uri("/users/{id}", id)
.retrieve()
.bodyToMono(User.class)
.onErrorResume(ex -> {
log.error("Error fetching user", ex);
return Mono.empty();
});
}
// POST запрос
public Mono<User> createUser(UserRequest request) {
return webClient
.post()
.uri("/users")
.contentType(MediaType.APPLICATION_JSON)
.body(Mono.just(request), UserRequest.class)
.retrieve()
.bodyToMono(User.class);
}
// Запрос с обработкой статусов
public Mono<ResponseEntity<User>> getUserWithStatus(Long id) {
return webClient
.get()
.uri("/users/{id}", id)
.exchangeToMono(response -> {
if (response.getStatusCode().is2xxSuccessful()) {
return response.bodyToMono(User.class)
.map(user -> new ResponseEntity<>(user, response.getStatusCode()));
} else {
return response.createException()
.flatMap(Mono::error);
}
});
}
// Fetch множество пользователей параллельно
public Mono<List<User>> getAllUsers() {
return webClient
.get()
.uri("/users")
.retrieve()
.bodyToFlux(User.class)
.collectList();
}
}
// Конфигурация WebClient
@Configuration
public class WebClientConfig {
@Bean
public WebClient webClient(WebClient.Builder builder) {
return builder
.clientConnector(
new ReactorClientHttpConnector(
HttpClient.create()
.secure(ssl -> ssl.defaultConfiguration(SslProvider.JDK))
.responseTimeout(Duration.ofSeconds(10))
)
)
.build();
}
}
Преимущества:
- Асинхронная, non-blocking
- Отличная производительность
- Современная (рекомендуется Spring)
- Реактивное программирование
- Работает с Project Reactor
Недостатки:
- Сложнее для начинающих
- Требует понимания Reactive Streams
3. OkHttp - мощный HTTP клиент
Библиотека для HTTP запросов от Square:
@Service
public class OkHttpClientService {
private final OkHttpClient okHttpClient;
private final ObjectMapper objectMapper;
public OkHttpClientService() {
this.okHttpClient = new OkHttpClient.Builder()
.connectTimeout(10, TimeUnit.SECONDS)
.readTimeout(10, TimeUnit.SECONDS)
.writeTimeout(10, TimeUnit.SECONDS)
.build();
this.objectMapper = new ObjectMapper();
}
// GET запрос
public User getUserById(Long id) throws IOException {
Request request = new Request.Builder()
.url("http://user-service:8080/api/v1/users/" + id)
.get()
.build();
try (Response response = okHttpClient.newCall(request).execute()) {
if (!response.isSuccessful()) {
throw new RuntimeException("Unexpected code " + response);
}
return objectMapper.readValue(response.body().string(), User.class);
}
}
// POST запрос
public User createUser(UserRequest request) throws IOException {
String json = objectMapper.writeValueAsString(request);
RequestBody body = RequestBody.create(json, MediaType.get("application/json"));
Request httpRequest = new Request.Builder()
.url("http://user-service:8080/api/v1/users")
.post(body)
.build();
try (Response response = okHttpClient.newCall(httpRequest).execute()) {
if (!response.isSuccessful()) {
throw new RuntimeException("Unexpected code " + response);
}
return objectMapper.readValue(response.body().string(), User.class);
}
}
// Асинхронный запрос с callback
public void getUserByIdAsync(Long id, Callback callback) {
Request request = new Request.Builder()
.url("http://user-service:8080/api/v1/users/" + id)
.build();
okHttpClient.newCall(request).enqueue(callback);
}
}
Преимущества:
- Очень надежная
- Connection pooling и compression
- Полезные интерцепторы
- Асинхронные запросы
- Работает на Android
Недостатки:
- Требует явной обработки exceptions
- Нужна дополнительная зависимость
4. HttpClient (Java 11+) - встроенный в Java
Модерный HTTP клиент из стандартной библиотеки:
@Service
public class JavaHttpClientService {
private final HttpClient httpClient;
private final ObjectMapper objectMapper;
public JavaHttpClientService() {
this.httpClient = HttpClient.newBuilder()
.version(HttpClient.Version.HTTP_2)
.connectTimeout(Duration.ofSeconds(10))
.build();
this.objectMapper = new ObjectMapper();
}
// Синхронный GET запрос
public User getUserById(Long id) throws IOException, InterruptedException {
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("http://user-service:8080/api/v1/users/" + id))
.GET()
.timeout(Duration.ofSeconds(10))
.build();
HttpResponse<String> response = httpClient.send(
request,
HttpResponse.BodyHandlers.ofString()
);
if (response.statusCode() == 200) {
return objectMapper.readValue(response.body(), User.class);
} else {
throw new RuntimeException("Error: " + response.statusCode());
}
}
// Асинхронный запрос
public CompletableFuture<User> getUserByIdAsync(Long id) {
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("http://user-service:8080/api/v1/users/" + id))
.GET()
.build();
return httpClient.sendAsync(
request,
HttpResponse.BodyHandlers.ofString()
).thenApply(response -> {
try {
return objectMapper.readValue(response.body(), User.class);
} catch (JsonProcessingException e) {
throw new RuntimeException(e);
}
});
}
// POST запрос
public User createUser(UserRequest request) throws IOException, InterruptedException {
String json = objectMapper.writeValueAsString(request);
HttpRequest httpRequest = HttpRequest.newBuilder()
.uri(URI.create("http://user-service:8080/api/v1/users"))
.header("Content-Type", "application/json")
.POST(HttpRequest.BodyPublishers.ofString(json))
.build();
HttpResponse<String> response = httpClient.send(
httpRequest,
HttpResponse.BodyHandlers.ofString()
);
return objectMapper.readValue(response.body(), User.class);
}
}
Преимущества:
- Встроена в Java (Java 11+)
- Не требует зависимостей
- Поддержка HTTP/2
- Асинхронные запросы из коробки
- Современный API
Недостатки:
- Требует Java 11+
- Меньше features чем специализированные библиотеки
5. Feign - декларативный HTTP клиент
Interfase-based подход для простоты:
// Объявление HTTP клиента как интерфейса
@FeignClient(name = "user-service", url = "http://user-service:8080/api/v1")
public interface UserServiceFeignClient {
@GetMapping("/users/{id}")
User getUserById(@PathVariable("id") Long id);
@PostMapping("/users")
User createUser(@RequestBody UserRequest request);
@GetMapping("/users")
List<User> getAllUsers();
@PutMapping("/users/{id}")
User updateUser(@PathVariable("id") Long id, @RequestBody UserRequest request);
@DeleteMapping("/users/{id}")
void deleteUser(@PathVariable("id") Long id);
}
// Использование
@Service
public class UserServiceImpl {
private final UserServiceFeignClient client;
public UserServiceImpl(UserServiceFeignClient client) {
this.client = client;
}
public User getUser(Long id) {
return client.getUserById(id);
}
}
// Конфигурация Feign
@Configuration
public class FeignConfig {
@Bean
public Logger.Level feignLoggerLevel() {
return Logger.Level.FULL;
}
}
Преимущества:
- Очень просто читать код
- Автоматическая сериализация
- Хорошая интеграция с Spring
- Декларативный подход
Недостатки:
- Меньше контроля
- Не поддерживает асинхронность из коробки (в классическом Feign)
6. Retrofit - type-safe HTTP клиент
Популярная библиотека из Square (как OkHttp):
public interface UserService {
@GET("/users/{id}")
Call<User> getUserById(@Path("id") Long id);
@POST("/users")
Call<User> createUser(@Body UserRequest request);
@GET("/users")
Call<List<User>> getAllUsers();
}
@Service
public class RetrofitUserClient {
private final UserService userService;
public RetrofitUserClient() {
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("http://user-service:8080/api/v1/")
.addConverterFactory(JacksonConverterFactory.create())
.client(new OkHttpClient())
.build();
this.userService = retrofit.create(UserService.class);
}
public User getUserById(Long id) throws IOException {
Call<User> call = userService.getUserById(id);
Response<User> response = call.execute();
if (response.isSuccessful()) {
return response.body();
} else {
throw new RuntimeException("Error: " + response.code());
}
}
}
Сравнение инструментов
| Инструмент | Тип | Асинхронный | Простота | Рекомендуется |
|---|---|---|---|---|
| RestTemplate | Spring | Нет | Высокая | Нет (deprecated) |
| WebClient | Spring | Да | Средняя | Да |
| OkHttp | Standalone | Да | Средняя | Да |
| HttpClient | Built-in | Да | Средняя | Да (11+) |
| Feign | Spring Cloud | Частично | Высокая | Да |
| Retrofit | Standalone | Да | Средняя | Да |
Рекомендации
- Spring приложение: WebClient или Feign
- Java 11+: встроенный HttpClient
- Высокая производительность: WebClient
- Простота кода: Feign
- Микросервисы: Feign + Spring Cloud
- Non-Spring: OkHttp или Retrofit
Для современных Spring приложений рекомендую WebClient для асинхронного выполнения запросов и Feign для простоты кода в микросервисах.