Solr优化之路之Recovering solr优化Recoveringsolr
程序员文章站
2022-07-13 12:18:52
...
一、Solr的Recovering问题解决
经常碰到solr的recovering的问题,经过检查大部分的recovering的情况在lead的error日志中都会出现如下错误日志。
错误1:[ERROR] 16:46:20.617 [http-nio-8088-exec-620] o.a.s.u.p.DistributedUpdateProcessor [DistributedUpdateProcessor.java:896] Setting up to try to start recovery on replica http://10.201.32.56:8088/search-service/product/
java.net.SocketException: Connection reset
错误2:[ERROR] 06:10:31.351 [http-nio-8088-exec-573] o.a.s.u.p.DistributedUpdateProcessor [DistributedUpdateProcessor.java:896] Setting up to try to start recovery on replica http://10.201.32.39:8088/search-service/product/
java.net.SocketException: Broken pipe
错误3:[ERROR] 16:37:00.167 [http-nio-8088-exec-581] o.a.s.u.p.DistributedUpdateProcessor [DistributedUpdateProcessor.java:896] Setting up to try to start recovery on replica http://10.201.32.82:8088/search-service/product/
org.apache.http.NoHttpResponseException: 10.201.32.82:8088 failed to respond
以上错误解释如下:
Caused by: java.net.SocketException: Broken pipe
是网络不稳定,一个请求连接两次,导致出现问题,这种情况比较少,不影响整体的性能~检查网络原因,原因是往一个socket写一个序列化对象写了2次,因为网络不稳定,所以有断开重新连接的机制,但是由于两太机器通过服务代理的方式传输,所以server无法发现客户端退出,这样造成客户端列表里有一个机器一个端口的两个socket对象,这样往socket里写的时候就造成一个对象写两次的现象
java.net.SocketException: Connection reset
如果此时服务器端正在Socket套接字的流中读数据,客户端中断,服务器端则会提示“Connection reset”。比较常见的错误“Connection reset by peer”,该错误和“Connection reset”是有区别的:服务器返回了“RST”时,如果此时客户端正在从Socket套接字的输出流中读数据则会提示Connection reset”;服务器返回了“RST”时,如果此时客户端正在往Socket套接字的输入流中写数据则会提示“Connection reset by peer”。
主要是由于httpClient用到了keepalive,连接池管理,当服务器端的超时时间小于连接池的超时时间,那么在临界时间上客户端连接池拿到一个可用连接王服务器端进行读取或写入数据时,这时候服务器端连接超时关闭链接,导致请求报错。
优化思路是,设置客户端连接池的超时时间小于服务器端的连接超时时间。
二、Solr连接池相关参数配置
通过配置solr.xml来实现httpclient连接池部分。
初始化solr中的httpclient参考SolrDispatchFilter.java 和 UpdateShardHandler.java。
solr.xml配置中关于httpclient的连接池配置参数说明:
(1)
//设置整个连接池最大连接数 根据自己的场景决定
//是路由的默认最大连接(该值默认为2),限制数量实际使用DefaultMaxPerRoute并非MaxTotal。
private final int maxUpdateConnections;
public static final int DEFAULT_MAXUPDATECONNECTIONS = 100000;
conMgr.setMaxTotal(maxUpdateConnections;);
(2)
// DefaultMaxPerRoute是根据连接到一台主机的最大数
private final int maxUpdateConnectionsPerHost;
public static final int DEFAULT_MAXUPDATECONNECTIONSPERHOST = 100000;
conMgr.setDefaultMaxPerRoute(maxUpdateConnectionsPerHost);
此处解释下MaxtTotal和DefaultMaxPerRoute的区别:
1、MaxtTotal是整个池子的大小;
2、DefaultMaxPerRoute是根据连接到的主机对MaxTotal的一个细分;比如:
MaxtTotal=400 DefaultMaxPerRoute=200
而我只连接到http://sishuok.com时,到这个主机的并发最多只有200;而不是400;
而我连接到http://sishuok.com 和 http://qq.com时,到每个主机的并发最多只有200;即加起来是400(但不能超过400);所以起作用的设置是DefaultMaxPerRoute。
(3)
//ConnectTimeout:响应超时时间,超过此时间不再读取响应;
private final int distributedSocketTimeout;
public static final int DEFAULT_DISTRIBUPDATESOTIMEOUT = 600000;
clientParams.set(HttpClientUtil.PROP_SO_TIMEOUT,cfg.getDistributedSocketTimeout());
(4)
//SocketTimeout:链接建立的超时时间;
private final int distributedConnectionTimeout;
public static final int DEFAULT_DISTRIBUPDATECONNTIMEOUT = 60000;
clientParams.set(HttpClientUtil.PROP_CONNECTION_TIMEOUT,cfg.getDistributedConnectionTimeout());
(5)
//轮询休眠时间
private final int updateConnectionsEvictorSleepDelay;
public static final int DEFAULT_UPDATECONNECTIONSEVICTORSLEEPDELAY = 5000;
(6)
//connection的最大保持时间
private final int maxUpdateConnectionIdleTime;
public static final int DEFAULT_MAXUPDATECONNECTIONIDLETIME = 40000;
(7)其它
ConnectionRequestTimeout: http clilent中从connetcion pool中获得一个connection的超时时间;
//retry 0次
DefaultHttpMethodRetryHandler retryHandler = new DefaultHttpMethodRetryHandler(0, false);
method.getParams().setParameter(HttpMethodParams.RETRY_HANDLER,retryHandler);
//retry 默认是3次
clientParams.set(HttpClientUtil.PROP_USE_RETRY, true);
(8)附录:创建一个httpclient的例子。
public static CloseableHttpClient getHttpClient() {
PoolingHttpClientConnectionManager manager = new PoolingHttpClientConnectionManager();
int maxUpdateConnections = 300;
int maxUpdateConnectionsPerHost=100;
manager.setMaxTotal(maxUpdateConnections);
manager.setDefaultMaxPerRoute(maxUpdateConnectionsPerHost);
int distributedSocketTimeout = 600000;
int distributedConnectionTimeout = 30000;
RequestConfig.Builder requestBuilder = RequestConfig.custom();
requestBuilder.setSocketTimeout(distributedSocketTimeout);
requestBuilder.setConnectTimeout(distributedConnectionTimeout);
requestBuilder.setConnectionRequestTimeout(300000);
HttpClientBuilder builder = HttpClientBuilder.create();
builder.setDefaultRequestConfig(requestBuilder.build());
builder.setConnectionManager(manager);
long updateConnectionsEvictorSleepDelay = 5000;
long maxUpdateConnectionIdleTime = 40000;
UpdateShardHandler.IdleConnectionsEvictor idleConnectionsEvictor = new UpdateShardHandler.IdleConnectionsEvictor((ClientConnectionManager)manager,updateConnectionsEvictorSleepDelay, TimeUnit.MILLISECONDS, maxUpdateConnectionIdleTime, TimeUnit.MILLISECONDS);
idleConnectionsEvictor.start();
CloseableHttpClient client = builder.build();
return client;
}
经常碰到solr的recovering的问题,经过检查大部分的recovering的情况在lead的error日志中都会出现如下错误日志。
错误1:[ERROR] 16:46:20.617 [http-nio-8088-exec-620] o.a.s.u.p.DistributedUpdateProcessor [DistributedUpdateProcessor.java:896] Setting up to try to start recovery on replica http://10.201.32.56:8088/search-service/product/
java.net.SocketException: Connection reset
错误2:[ERROR] 06:10:31.351 [http-nio-8088-exec-573] o.a.s.u.p.DistributedUpdateProcessor [DistributedUpdateProcessor.java:896] Setting up to try to start recovery on replica http://10.201.32.39:8088/search-service/product/
java.net.SocketException: Broken pipe
错误3:[ERROR] 16:37:00.167 [http-nio-8088-exec-581] o.a.s.u.p.DistributedUpdateProcessor [DistributedUpdateProcessor.java:896] Setting up to try to start recovery on replica http://10.201.32.82:8088/search-service/product/
org.apache.http.NoHttpResponseException: 10.201.32.82:8088 failed to respond
以上错误解释如下:
Caused by: java.net.SocketException: Broken pipe
是网络不稳定,一个请求连接两次,导致出现问题,这种情况比较少,不影响整体的性能~检查网络原因,原因是往一个socket写一个序列化对象写了2次,因为网络不稳定,所以有断开重新连接的机制,但是由于两太机器通过服务代理的方式传输,所以server无法发现客户端退出,这样造成客户端列表里有一个机器一个端口的两个socket对象,这样往socket里写的时候就造成一个对象写两次的现象
java.net.SocketException: Connection reset
如果此时服务器端正在Socket套接字的流中读数据,客户端中断,服务器端则会提示“Connection reset”。比较常见的错误“Connection reset by peer”,该错误和“Connection reset”是有区别的:服务器返回了“RST”时,如果此时客户端正在从Socket套接字的输出流中读数据则会提示Connection reset”;服务器返回了“RST”时,如果此时客户端正在往Socket套接字的输入流中写数据则会提示“Connection reset by peer”。
主要是由于httpClient用到了keepalive,连接池管理,当服务器端的超时时间小于连接池的超时时间,那么在临界时间上客户端连接池拿到一个可用连接王服务器端进行读取或写入数据时,这时候服务器端连接超时关闭链接,导致请求报错。
优化思路是,设置客户端连接池的超时时间小于服务器端的连接超时时间。
二、Solr连接池相关参数配置
通过配置solr.xml来实现httpclient连接池部分。
初始化solr中的httpclient参考SolrDispatchFilter.java 和 UpdateShardHandler.java。
solr.xml配置中关于httpclient的连接池配置参数说明:
(1)
//设置整个连接池最大连接数 根据自己的场景决定
//是路由的默认最大连接(该值默认为2),限制数量实际使用DefaultMaxPerRoute并非MaxTotal。
private final int maxUpdateConnections;
public static final int DEFAULT_MAXUPDATECONNECTIONS = 100000;
conMgr.setMaxTotal(maxUpdateConnections;);
(2)
// DefaultMaxPerRoute是根据连接到一台主机的最大数
private final int maxUpdateConnectionsPerHost;
public static final int DEFAULT_MAXUPDATECONNECTIONSPERHOST = 100000;
conMgr.setDefaultMaxPerRoute(maxUpdateConnectionsPerHost);
此处解释下MaxtTotal和DefaultMaxPerRoute的区别:
1、MaxtTotal是整个池子的大小;
2、DefaultMaxPerRoute是根据连接到的主机对MaxTotal的一个细分;比如:
MaxtTotal=400 DefaultMaxPerRoute=200
而我只连接到http://sishuok.com时,到这个主机的并发最多只有200;而不是400;
而我连接到http://sishuok.com 和 http://qq.com时,到每个主机的并发最多只有200;即加起来是400(但不能超过400);所以起作用的设置是DefaultMaxPerRoute。
(3)
//ConnectTimeout:响应超时时间,超过此时间不再读取响应;
private final int distributedSocketTimeout;
public static final int DEFAULT_DISTRIBUPDATESOTIMEOUT = 600000;
clientParams.set(HttpClientUtil.PROP_SO_TIMEOUT,cfg.getDistributedSocketTimeout());
(4)
//SocketTimeout:链接建立的超时时间;
private final int distributedConnectionTimeout;
public static final int DEFAULT_DISTRIBUPDATECONNTIMEOUT = 60000;
clientParams.set(HttpClientUtil.PROP_CONNECTION_TIMEOUT,cfg.getDistributedConnectionTimeout());
(5)
//轮询休眠时间
private final int updateConnectionsEvictorSleepDelay;
public static final int DEFAULT_UPDATECONNECTIONSEVICTORSLEEPDELAY = 5000;
(6)
//connection的最大保持时间
private final int maxUpdateConnectionIdleTime;
public static final int DEFAULT_MAXUPDATECONNECTIONIDLETIME = 40000;
(7)其它
ConnectionRequestTimeout: http clilent中从connetcion pool中获得一个connection的超时时间;
//retry 0次
DefaultHttpMethodRetryHandler retryHandler = new DefaultHttpMethodRetryHandler(0, false);
method.getParams().setParameter(HttpMethodParams.RETRY_HANDLER,retryHandler);
//retry 默认是3次
clientParams.set(HttpClientUtil.PROP_USE_RETRY, true);
(8)附录:创建一个httpclient的例子。
public static CloseableHttpClient getHttpClient() {
PoolingHttpClientConnectionManager manager = new PoolingHttpClientConnectionManager();
int maxUpdateConnections = 300;
int maxUpdateConnectionsPerHost=100;
manager.setMaxTotal(maxUpdateConnections);
manager.setDefaultMaxPerRoute(maxUpdateConnectionsPerHost);
int distributedSocketTimeout = 600000;
int distributedConnectionTimeout = 30000;
RequestConfig.Builder requestBuilder = RequestConfig.custom();
requestBuilder.setSocketTimeout(distributedSocketTimeout);
requestBuilder.setConnectTimeout(distributedConnectionTimeout);
requestBuilder.setConnectionRequestTimeout(300000);
HttpClientBuilder builder = HttpClientBuilder.create();
builder.setDefaultRequestConfig(requestBuilder.build());
builder.setConnectionManager(manager);
long updateConnectionsEvictorSleepDelay = 5000;
long maxUpdateConnectionIdleTime = 40000;
UpdateShardHandler.IdleConnectionsEvictor idleConnectionsEvictor = new UpdateShardHandler.IdleConnectionsEvictor((ClientConnectionManager)manager,updateConnectionsEvictorSleepDelay, TimeUnit.MILLISECONDS, maxUpdateConnectionIdleTime, TimeUnit.MILLISECONDS);
idleConnectionsEvictor.start();
CloseableHttpClient client = builder.build();
return client;
}