Как правильно закрыть Raw RestClient при использовании Elastic Search 5.5.0 для оптимальной производительности?
Я использую пружинный ботинок 1.5.4.Конструирование выхода для подключения к Elasticsearch в 5.5.0 экземпляра, используя низкий REST-клиента уровне что Elasticsearch обеспечивает.
Пом.xml
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.4.RELEASE</version>
</parent>
<dependencies>
<!-- Spring -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!-- Elasticsearch -->
<dependency>
<groupId>org.elasticsearch</groupId>
<artifactId>elasticsearch</artifactId>
<version>5.5.0</version>
</dependency>
<dependency>
<groupId>org.elasticsearch.client</groupId>
<artifactId>transport</artifactId>
<version>5.5.0</version>
</dependency>
<!-- Apache Commons -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.6</version>
</dependency>
<!-- Jackson -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.8.9</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.8.9</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>2.8.9</version>
</dependency>
<!-- Log4j -->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
<!-- JUnit -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>
<!-- Swagger -->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>2.6.1</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>2.6.1</version>
<scope>compile</scope>
</dependency>
</dependencies>
Все настроено правильно, но после нескольких обращений клиентские приложения сообщали об ошибке HTTP 500, и вот что появилось в файлах журнала:
java.io.IOException: Too many open files
at sun.nio.ch.IOUtil.makePipe(Native Method) ~[na:1.8.0_141]
at sun.nio.ch.EPollSelectorImpl.<init>(EPollSelectorImpl.java:65) ~[na:1.8.0_141]
at sun.nio.ch.EPollSelectorProvider.openSelector(EPollSelectorProvider.java:36) ~[na:1.8.0_141]
at java.nio.channels.Selector.open(Selector.java:227) ~[na:1.8.0_141]
at org.apache.http.impl.nio.reactor.AbstractMultiworkerIOReactor.<init>(AbstractMultiworkerIOReactor.java:142) ~[httpcore-nio-4.4.5.jar!/:4.4.5]
at org.apache.http.impl.nio.reactor.DefaultConnectingIOReactor.<init>(DefaultConnectingIOReactor.java:79) ~[httpcore-nio-4.4.5.jar!/:4.4.5]
at org.apache.http.impl.nio.client.IOReactorUtils.create(IOReactorUtils.java:43) ~[httpasyncclient-4.1.3.jar!/:4.1.3]
at org.apache.http.impl.nio.client.HttpAsyncClientBuilder.build(HttpAsyncClientBuilder.java:666) ~[httpasyncclient-4.1.3.jar!/:4.1.3]
at org.elasticsearch.client.RestClientBuilder.createHttpClient(RestClientBuilder.java:202) ~[rest-5.5.0.jar!/:5.5.0]
at org.elasticsearch.client.RestClientBuilder.build(RestClientBuilder.java:180) ~[rest-5.5.0.jar!/:5.5.0]
at com.myapp.controller.SearchController.getSearchQueryResults(SearchController.java:94) ~[classes!/:1.0]
Внутри SearchController (вторая строка после комментария / / - строка 94):
@RestController
@RequestMapping("/api/v1")
public class SearchController {
@RequestMapping(value = "/search", method = RequestMethod.GET, produces="application/json" )
public ResponseEntity<Object> getSearchQueryResults(@RequestParam(value = "criteria") String criteria) throws IOException {
// Setup HTTP Headers
HttpHeaders headers = new HttpHeaders();
headers.add("Content-Type", "application/json");
// Setup RestClient
RestClient restClient = RestClient.builder(new HttpHost("localhost", 9200))
.setRequestConfigCallback(new RestClientBuilder.RequestConfigCallback() {
@Override
public RequestConfig.Builder customizeRequestConfig(RequestConfig.Builder requestConfigBuilder) {
return requestConfigBuilder.setConnectTimeout(5000).setSocketTimeout(60000);
}
}).setMaxRetryTimeoutMillis(60000).build();
// Setup query and send and return ResponseEntity...
}
}
Очевидно, что то, чем он никогда не был закрыто после вызова рестклиента.метод performRequest ()...
Итак, я ввел это в свой код:
Response response = null;
try {
// Submit Query and Obtain Response
response = restClient.performRequest("POST", endPoint, Collections.singletonMap("pretty", "true"), entity);
}
catch (IOException e) {
LOG.error("nntException: " + e + "nn");
e.printStackTrace();
}
finally {
restClient.close();
}
Прочитайте в документации Elastic Search, что класс RestClient потокобезопасен...
Кроме того, прочитайте о restClient.performRequestAsync() метод, но я несколько неопытен с потоками и описание внутри документации расплывчато.
Вопрос(ы):
-
Является ли мое решение лучшим способом справиться и закрыть кучу сокетов ресурсы?
-
Был бы признателен, если бы кто-нибудь показал мне лучший способ использовать низкоуровневый RestClient с эластичным поиском в том смысле, что он не вызовет ту же проблему с ресурсами сокета, не освобождающимися в результате HTTP 500. Я должен использовать restClient.performRequestAsync? Не мог бы кто-нибудь привести пример?
Спасибо, что нашли время, чтобы прочитать это...
1 ответ:
Это не очень хорошая практика, чтобы создать
RestClient
на каждом отдельном запросе. Вы должны создать один экземпляр с помощью Бина конфигурации, как показано ниже:@Configuration public class ElasticsearchConfig { @Value("${elasticsearch.host}") private String host; @Value("${elasticsearch.port}") private int port; @Bean public RestClient restClient() { return RestClient.builder(new HttpHost(host, port)) .setRequestConfigCallback(new RestClientBuilder.RequestConfigCallback() { @Override public RequestConfig.Builder customizeRequestConfig(RequestConfig.Builder requestConfigBuilder) { return requestConfigBuilder.setConnectTimeout(5000).setSocketTimeout(60000); } }).setMaxRetryTimeoutMillis(60000).build(); } }
И затем в вашем
SearchController
классе вы можете ввести его следующим образом (а также добавить метод очистки, чтобы закрыть экземплярrestClient
, Когда ваш контейнер выходит из строя):@RestController @RequestMapping("/api/v1") public class SearchController { @Autowired private RestClient restClient; @RequestMapping(value = "/search", method = RequestMethod.GET, produces="application/json" ) public ResponseEntity<Object> getSearchQueryResults(@RequestParam(value = "criteria") String criteria) throws IOException { // Setup HTTP Headers HttpHeaders headers = new HttpHeaders(); headers.add("Content-Type", "application/json"); // Setup query and send and return ResponseEntity... Response response = this.restClient.performRequest(...); } @PreDestroy public void cleanup() { try { logger.info("Closing the ES REST client"); this.restClient.close(); } catch (IOException ioe) { logger.error("Problem occurred when closing the ES REST client", ioe); } } }