← Назад к вопросам

Какие знаешь инструменты для отправки запроса в другой сервис?

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());
        }
    }
}

Сравнение инструментов

ИнструментТипАсинхронныйПростотаРекомендуется
RestTemplateSpringНетВысокаяНет (deprecated)
WebClientSpringДаСредняяДа
OkHttpStandaloneДаСредняяДа
HttpClientBuilt-inДаСредняяДа (11+)
FeignSpring CloudЧастичноВысокаяДа
RetrofitStandaloneДаСредняяДа

Рекомендации

  1. Spring приложение: WebClient или Feign
  2. Java 11+: встроенный HttpClient
  3. Высокая производительность: WebClient
  4. Простота кода: Feign
  5. Микросервисы: Feign + Spring Cloud
  6. Non-Spring: OkHttp или Retrofit

Для современных Spring приложений рекомендую WebClient для асинхронного выполнения запросов и Feign для простоты кода в микросервисах.

Какие знаешь инструменты для отправки запроса в другой сервис? | PrepBro