我正在使用Spring Boot 1.5.4.RELEASE微服务使用ElasticSearch提供的低级Rest客户端连接到ElasticSearch 5.5.0实例。
pom.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... } }
很明显,在调用restClient.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("\n\n\tException: " + e + "\n\n"); e.printStackTrace(); } finally { restClient.close(); }
阅读Elastic Search的文档,了解RestClient类是线程安全的。
另外,请阅读有关restClient.performRequestAsync()方法的信息,但是对线程没有任何经验,文档中的描述也很模糊。
问题:
我的解决方案是处理和关闭大量套接字资源的最佳方法吗?
如果有人可以向我展示一种更好的方法来将低级RestClient与Elastic Search结合使用,就不会在套接字资源未被释放导致HTTP 500的情况下引起相同的问题,我将不胜感激。我是否应该使用restClient.performRequestAsync ?有人可以提供一个例子吗?
感谢您抽出时间来阅读…
RestClient在每个请求上创建一个不是一个好习惯。您应该通过如下所示的配置bean创建一个实例:
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在容器关闭时关闭实例):
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); } } }