Java 无法配置";“保持活力”;在Camel HTTP组件中
我在HTTP组件的正确设置方面遇到了一些问题。目前,微服务从提供者提取JSON内容,对其进行处理,并将其发送到下一个服务进行进一步处理。主要的问题是这个微服务创建了大量的CLOSE_WAIT套接字连接。我知道“KEEP-ALIVE”的整个概念将使连接保持打开状态,直到我关闭它为止,但服务器可能会因为某些原因中断连接并创建此close_WAIT套接字 我创建了一个用于调试/测试目的的小型服务,它向Google发出GET调用,但即使是这个连接,在我关闭程序之前都保持打开状态。我尝试了许多不同的解决方案:Java 无法配置";“保持活力”;在Camel HTTP组件中,java,sockets,apache-camel,Java,Sockets,Apache Camel,我在HTTP组件的正确设置方面遇到了一些问题。目前,微服务从提供者提取JSON内容,对其进行处理,并将其发送到下一个服务进行进一步处理。主要的问题是这个微服务创建了大量的CLOSE_WAIT套接字连接。我知道“KEEP-ALIVE”的整个概念将使连接保持打开状态,直到我关闭它为止,但服务器可能会因为某些原因中断连接并创建此close_WAIT套接字 我创建了一个用于调试/测试目的的小型服务,它向Google发出GET调用,但即使是这个连接,在我关闭程序之前都保持打开状态。我尝试了许多不同的解决方
- .setHeader(“连接”,常量(“关闭”))
- -Dhttp.keepAlive=false作为VM参数
- 从Camel Http切换到Camel-Http4
- httpClient.soTimeout=500(驼峰HTTP)、httpClient.socketTimeout=500和ConnectionTimeOlive=500(驼峰-HTTP4)
- .setHeader(“连接”,简单(“保持活动”))和 .setHeader(“保持活动”,简单(“超时=10”)(Camel-HTTP4)
- 通过调试DefaultConnectionKeepAlivestStrategy从-1(永无止境)到Camel-HTTP4中的特定值的响应进行设置-这是可行的,但我无法注入自己的策略
- 我如何告诉驼峰HTTP在经过特定时间后应该关闭连接?例如,该服务每小时从内容提供商处提取一次。3-4小时后,HttpComponent应在拉动后关闭连接,并在下一次拉动时重新打开连接。目前,每个连接都将放回多线程HttpConnectionManager,并且套接字仍然打开
- 如果不能用Camel-HTTP实现这一点:如何将HttpClientBuilder注入到路由的创建中?我知道应该可以通过httpClient选项实现,但我不理解文档中的具体部分
感谢大家的帮助您可以向HTTP4提供自己的
客户端连接管理器
。通常,您应该使用org.apache.http.impl.conn.poolighttpclientconnectionmanager
的实例,您可以使用自己的org.apache.http.config.SocketConfig
将其传递给连接管理器的setDefaultSocketConfig
方法来配置该实例
如果您将Spring与Java配置一起使用,您将有一个方法:
@Bean
PoolingHttpClientConnectionManager connectionManager() {
SocketConfig socketConfig = SocketConfig.custom()
.setSoKeepAlive(false)
.setSoReuseAddress(true)
.build();
PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager();
connectionManager.setDefaultSocketConfig(socketConfig);
return connectionManager;
}
然后您只需在端点定义中使用它,就像这样:
clientConnectionManager=#connectionManager
如果空闲连接在配置的时间内处于空闲状态,则可以通过关闭空闲连接来完成。您可以通过为驼峰Http组件配置空闲连接超时来实现同样的目的。
骆驼Http提供了这样做的接口
将org.apache.camel.component.http4.HttpComponent强制转换为PoolighttpClientConnectionManager
PoolingHttpClientConnectionManager poolingClientConnectionManager = (PoolingHttpClientConnectionManager) httpComponent
.getClientConnectionManager();
poolingClientConnectionManager.closeIdleConnections(5000, TimeUnit.MILLISECONDS);
请访问此处[,java.util.concurrent.TimeUnit)]不幸的是,在应用程序最终关闭之前,所有建议的答案都没有解决我方的
CLOSE\u WAIT
连接状态
我用以下测试用例重现了这个问题:
公共类HttpInvokationTest扩展了对SpringTest的支持{
私有静态最终记录器LOG=LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
@端点注入(uri=“mock:success”)
私有模拟点;
@端点注入(uri=“mock:failure”)
私有模拟端点故障端点;
@凌驾
受保护的AbstractApplicationContext createApplicationContext(){
返回新的AnnotationConfigApplicationContext(ContextConfig.class);
}
@配置
@导入(HttpClientSpringTestConfig.class)
公共静态类ContextConfig扩展了配置{
@凌驾
公共列表路线(){
列表路由=新的ArrayList(1);
routes.add(新RouteBuilder(){
@凌驾
public void configure(){
从(“直接:开始”)
.log(LoggingLevel.INFO,log,机密,“调用外部URL:${header[ERPEL_URL]}”)
.setHeader(“连接”,常量(“关闭”))
.recipientList(标题(“测试URL”))
.log(LoggingLevel.DEBUG,“HTTP响应代码:${header[“+Exchange.HTTP\u response\u code+“]}”)
.bean(CopyBodyToHeaders.class)
.choice()
.when(标头(Exchange.HTTP\u RESPONSE\u CODE).isGreaterThanOrEqualTo(300))
.至(“模拟:失败”)
.否则()
。至(“模仿:成功”);
}
});
返回路线;
}
}
@试验
public void testHttpInvocation()引发异常{
SuccessedPoint.expectedMessageCount(1);
failureEndpoint.expectedMessageCount(0);
ProducerTemplate=context.createProducerTemplate();
sendboyandheader(“direct:start”,null,“TEST_URL”,“http4://meta.stackoverflow.com”);
successedpoint.assertessatified();
failureEndpoint.Assertessatified();
Exchange=successedpoint.getExchanges().get(0);
Map headers=exchange.getIn().getHeaders();
String body=exchange.getIn().getBody(String.class);
for(字符串键:headers.keySet()){
LOG.info(“Header:{}->{}”,key,headers.get(key));
}
LOG.info(“正文:{}”,正文);
睡眠(120000);
}
}
并发出netstat-ab-ptcp | grep151.101.129.69
请求,其中IP是meta.stackoverflow.com
中的一个
这给了如下回答:
tcp4 0 0 192.168.0.10.52183 151.101.129.69.https ESTABLISHED 37562 2118
tcp4 0 0 192.168.0.10.52182 151.101.129.69.http ESTABLISHED 885 523
就在调用之后
tcp4 0 0 192.168.0.10.52183 151.101.129.69.https CLOSE_WAIT 37562 2118
tcp4 0 0 192.168.0.10.52182 151.101.129.69.http CLOSE_WAIT 885 523
响应,直到应用程序由于连接:keep alive
标题而关闭,即使配置如下:
@配置
@EnableConfigurationProperties(HttpClientSettings.class)
公共类HttpClientSpringTestConfig{
专用最终静态记录器日志=记录器
/**
* This singleton monitor will check every few seconds for idle and stale connections and perform
* a cleanup on the connections using the registered connection managers.
*/
public enum IdleConnectionMonitor {
INSTANCE;
private final static Logger LOG = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
/** The execution service which runs the cleanup every 5 seconds **/
private ScheduledExecutorService executorService =
Executors.newScheduledThreadPool(1, new NamingThreadFactory());
/** The actual thread which performs the monitoring **/
private IdleConnectionMonitorThread monitorThread = new IdleConnectionMonitorThread();
IdleConnectionMonitor() {
// execute the thread every 5 seconds till the application is shutdown (or the shutdown method
// is invoked)
executorService.scheduleAtFixedRate(monitorThread, 5, 5, TimeUnit.SECONDS);
}
/**
* Registers a {@link HttpClientConnectionManager} to monitor for stale connections
*/
public void registerConnectionManager(HttpClientConnectionManager connMgr) {
monitorThread.registerConnectionManager(connMgr);
}
/**
* Request to stop the monitoring for stale HTTP connections.
*/
public void shutdown() {
executorService.shutdown();
try {
if (!executorService.awaitTermination(3, TimeUnit.SECONDS)) {
LOG.warn("Connection monitor shutdown not finished after 3 seconds!");
}
} catch (InterruptedException iEx) {
LOG.warn("Execution service was interrupted while waiting for graceful shutdown");
}
}
/**
* Upon invocation, the list of registered connection managers will be iterated through and if a
* referenced object is still reachable {@link HttpClientConnectionManager#closeExpiredConnections()}
* and {@link HttpClientConnectionManager#closeIdleConnections(long, TimeUnit)} will be invoked
* in order to cleanup stale connections.
* <p/>
* This runnable implementation holds a weakly referable list of {@link
* HttpClientConnectionManager} objects. If a connection manager is only reachable by {@link
* WeakReference}s or {@link PhantomReference}s it gets eligible for garbage collection and thus
* may return null values. If this is the case, the connection manager will be removed from the
* internal list of registered connection managers to monitor.
*/
private static class IdleConnectionMonitorThread implements Runnable {
// we store only weak-references to connection managers in the list, as the lifetime of the
// thread may extend the lifespan of a connection manager and thus allowing the garbage
// collector to collect unused objects as soon as possible
private List<WeakReference<HttpClientConnectionManager>> registeredConnectionManagers =
Collections.synchronizedList(new ArrayList<>());
@Override
public void run() {
LOG.trace("Executing connection cleanup");
Iterator<WeakReference<HttpClientConnectionManager>> conMgrs =
registeredConnectionManagers.iterator();
while (conMgrs.hasNext()) {
WeakReference<HttpClientConnectionManager> weakConMgr = conMgrs.next();
HttpClientConnectionManager conMgr = weakConMgr.get();
if (conMgr != null) {
LOG.trace("Found connection manager: {}", conMgr);
conMgr.closeExpiredConnections();
conMgr.closeIdleConnections(30, TimeUnit.SECONDS);
} else {
conMgrs.remove();
}
}
}
void registerConnectionManager(HttpClientConnectionManager connMgr) {
registeredConnectionManagers.add(new WeakReference<>(connMgr));
}
}
private static class NamingThreadFactory implements ThreadFactory {
@Override
public Thread newThread(Runnable r) {
Thread t = new Thread(r);
t.setName("Connection Manager Monitor");
return t;
}
}
}
/**
* This Camel service with take care of the lifecycle management of {@link IdleConnectionMonitor}
* and invoke {@link IdleConnectionMonitor#shutdown()} once Camel is closing down in order to stop
* listening for stale connetions.
*/
public class IdleConnectionMonitorService extends EventNotifierSupport {
private final static Logger LOG = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
private IdleConnectionMonitor connectionMonitor;
@Override
public void notify(EventObject event) {
if (event instanceof CamelContextStartedEvent) {
LOG.info("Start listening for closable HTTP connections");
connectionMonitor = IdleConnectionMonitor.INSTANCE;
} else if (event instanceof CamelContextStoppingEvent){
LOG.info("Shutting down listener for open HTTP connections");
connectionMonitor.shutdown();
}
}
@Override
public boolean isEnabled(EventObject event) {
return event instanceof CamelContextStartedEvent || event instanceof CamelContextStoppingEvent;
}
public IdleConnectionMonitor getConnectionMonitor() {
return this.connectionMonitor;
}
}
private void registerHttpClientConnectionManager(HttpClientConnectionManager conMgr) {
if (!getIdleConnectionMonitorService().isPresent()) {
// register the service with Camel so that on a shutdown the monitoring thread will be stopped
camelContext.getManagementStrategy().addEventNotifier(new IdleConnectionMonitorService());
}
IdleConnectionMonitor.INSTANCE.registerConnectionManager(conMgr);
}
private Optional<IdleConnectionMonitorService> getIdleConnectionMonitorService() {
for (EventNotifier eventNotifier : camelContext.getManagementStrategy().getEventNotifiers()) {
if (eventNotifier instanceof IdleConnectionMonitorService) {
return Optional.of((IdleConnectionMonitorService) eventNotifier);
}
}
return Optional.empty();
}
PoolingHttpClientConnectionManager conMgr = new PoolingHttpClientConnectionManager();
registerHttpClientConnectionManager(conMgr);
@Bean(name = "httpClientConfigurer")
public HttpClientConfigurer httpConfiguration() {
return builder -> builder.setDefaultSocketConfig(socketConfig)
.setDefaultRequestConfig(requestConfig)
.setConnectionReuseStrategy(NoConnectionReuseStrategy.INSTANCE);
}
HttpComponent http4 = camelContext.getComponent("https4", HttpComponent.class);
http4.setHttpClientConfigurer(new HttpClientConfigurer() {
@Override
public void configureHttpClient(HttpClientBuilder builder) {
builder.setConnectionReuseStrategy(NoConnectionReuseStrategy.INSTANCE);
}
});