Как исправить «NoHttpResponseException» при запуске Wiremock на jenkins?

#spring-boot #wiremock

#весенняя загрузка #wiremock

Вопрос:

Я запускаю сервер wiremock в интеграционном тесте.

ИТ-передача в моем локальном, НО в каком-то случае произошла ошибка на сервере jenkins, ошибка

 localhost:8089 failed to respond; nested exception is org.apache.http.NoHttpResponseException: localhost:8089 failed to respond
  

Я пытаюсь добавить sleep(3000) в свой тест, который может устранить проблему, но я не знаю основной причины проблемы, поэтому обходной путь не является хорошей идеей

Я также пытаюсь использовать @AutoConfigureWireMock(port=8089) для замены WireMockServer для запуска сервера wiremock, что может решить проблему, НО я не знаю, как выполнить некоторую настройку сервера wiremock с помощью аннотации @AutoConfigureWireMock(port=8089) .

Вот мой код для запуска сервера wiremock, есть предложения по исправлению «NoHttpResponseException»?

 @ContextConfiguration(
initializers = ConfigFileApplicationContextInitializer.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT)
class BaseSpec extends Specification {
@Shared
WireMockServer wireMockServer

def setupSpec() {

wireMockServer = new WireMockServer(options().port(PORT).jettyHeaderBufferSize(12345)
.notifier(new ConsoleNotifier(new Boolean(System.getenv(“IT_WIREMOCK_LOG”) ?: ‘false’))) 
.extensions(new ResponseTemplateTransformer(true)))

wireMockServer.start()

}
  

Ответ №1:

Apache HttpClient NoHttpResponseException время от времени страдает. Это очень старая проблема.

В любом случае, я думаю, в вашем случае проблема может быть вызвана перезапуском сервера WireMock между тестами, и в то же время Apache HttpClient объединяет HTTP-соединения и пытается повторно использовать их между тестами. Если это так, есть два решения:

  1. Отключите объединение HTTP-соединений в своих тестах. Это имеет смысл, потому что считается нормальным, что сервер WireMock может быть перезапущен во время выполнения тестов. Кроме того, создайте свои заглушки WireMock, чтобы они всегда отправлялись "Connection": "close" среди заголовков. Результат будет тот же.

  2. Переключитесь с Apache HttpClient на Square OkHttp. OkHttp, хотя по умолчанию он объединяет http-соединения, всегда может корректно восстанавливаться в таких ситуациях, как устаревшее соединение. К сожалению, библиотека от Apache не такая умная.

Ответ №2:

Кстати, как уже писал Г. Демецки, это не связано с Wiremock. Это связано с вашим сервером приложений, который вызывает wiremock. Сегодня принято повторно использовать соединение для повышения производительности в инфраструктуре микросервиса. Таким образом, connection-close-header, клиент с ограниченным запросом и т. Д. Бесполезны.

Проверьте http-клиент apache:

httpclient-4.5.2 — PoolingHttpClientConnectionManager

Документация

Обработка устаревших соединений была изменена в версии 4.4. Ранее код проверял каждое соединение по умолчанию перед его повторным использованием. Теперь код проверяет соединение только в том случае, если время, прошедшее с момента последнего использования соединения, превышает установленное время ожидания. Тайм-аут по умолчанию установлен на 2000 мс

Каждый раз, когда конечная точка wiremock была уничтожена, а новая была создана для нового тестового класса, требуется 2 секунды, пока ваше приложение не обнаружит, что предыдущее соединение разорвано и необходимо открыть новое. Если вы не подождете 2 секунды, может быть вызвано такое исключение NoHttpResponseException, зависит от последней проверки.

Так Thread.sleep(2000); что выглядит некрасиво. Но это не так уж плохо, если мы знаем, зачем это требуется.

Комментарии:

1. Спасибо за эту полезную документацию — я не знал, что она установлена ровно на 2 секунды! КСТАТИ: спящий режим в течение 2 секунд в тестах — невероятно неэффективная идея, поэтому извините, но я не могу проголосовать 😉

2. Есть ли какое-либо решение этой проблемы, кроме использования 2-секундного тайм-аута между тестовыми классами? Я использую MockMvc для отправки запросов в WireMock, и у меня такая же проблема.

3. настройка http-клиента apache для его тестового экземпляра решила для меня эту проблему. Исправление заключалось в создании нового пула HttpClientConnectionManager и изменении времени ожидания с 2 секунд до 10 мс: setValidateAfterInactivity(10) как предложено в github.com/tomakehurst/wiremock/issues /…

Ответ №3:

Каждый раз, когда конечная точка wiremock уничтожается (потому что сервер wiremock перезапускается между тестами) и создается новая для нового теста, требуется 2 секунды (как указано в документации), пока приложение не обнаружит, что предыдущее http-соединение разорвано, и необходимо открыть новое.

Решение состоит в том, чтобы просто переопределить поведение подключения по умолчанию для каждого использования заглушки .withHeader("Connection", "close") . Что-то вроде:

   givenThat(get("/endpoint_path")
            .withHeader("Authorization", equalTo(authHeader))
            .willReturn(
               ok()
                 .withBody(body)
                 .withHeader(HttpHeaders.CONNECTION, "close")
            )
  )
  

Также возможно сделать это глобально с помощью transformer:

 public class NoKeepAliveTransformer extends ResponseDefinitionTransformer {

    @Override
    public ResponseDefinition transform(Request request,
                                        ResponseDefinition responseDefinition,
                                        FileSource files,
                                        Parameters parameters) {
        return ResponseDefinitionBuilder
            .like(responseDefinition)
            .withHeader(CONNECTION, "close")
            .build();
    }

    @Override
    public String getName() {
        return "keep-alive-disabler";
    }
}
  

Тогда этот преобразователь должен быть зарегистрирован при создании сервера wiremock:

 new WireMockServer(
    options()
        .port(port)
        .extensions(NoKeepAliveTransformer.class)
)
  

Ответ №4:

Решение, которое сработало для нас в этой ситуации, просто добавило повторную попытку в клиент apache:

 @Configuration
public class FeignTestConfig {

    @Bean
    @Primary
    public HttpClient testClient() {
        return HttpClientBuilder.create().setRetryHandler((exception, executionCount, context) -> {
            if (executionCount > 3) {
                return false;
            }
            return exception instanceof org.apache.http.NoHttpResponseException || exception instanceof SocketException;
        }).build();
    }
}
  

Исключение сокета также существует, потому что иногда это исключение выдается вместо NoHttpResponse