solr1.4 中SearchHandler使用的httpclient在高并发可能出现的问题

来源:互联网 发布:如何退出linux命令 编辑:程序博客网 时间:2024/06/06 00:14
solr 1.4 中使用的分布式搜索,是基于httpclient发出分布结点的请求,主要实现在SearchHandler类,该类里有个内部类

HttpCommComponent

里面有一个httpclient ,是一个静态实例,也就是说在同一个jvm里只有一个实例,可以重复使用,主要代码:


static HttpClientclient;


static {

MultiThreadedHttpConnectionManagermgr =new MultiThreadedHttpConnectionManager();

mgr.getParams().setDefaultMaxConnectionsPerHost(20);

mgr.getParams().setMaxTotalConnections(10000);

mgr.getParams().setConnectionTimeout(SearchHandler.connectionTimeout);

mgr.getParams().setSoTimeout(SearchHandler.soTimeout);

// mgr.getParams().setStaleCheckingEnabled(false);

client = new HttpClient(mgr);

}



其中有两个参数设置死了。。这两个参数是作用于线程池,管理于http连接


httpclient在处理请求连接方面使用了连接池,它内部实际上有两种连接池,一种是全局的ConnectionPool,一种是每主机(per-host)HostConnectionPool。参数maxHostConnections就HostConnectionPool中表示每主机可保持连接的连接数,maxTotalConnections是ConnectionPool中可最多保持的连接数。每主机的配置类是HostConfiguration,HttpClient有个int executeMethod(final HostConfiguration hostConfiguration, final HttpMethod method)方法可以指定使用哪个HostConfiguration,不过多数情况都是不显示指定HostConfiguration,这样httpclient就用了默认的HostConfiguration=null,也就是说所有的请求可以认为指自同一个主机。如果不设置这两个参数,httpclient自然会用默认的配置,也就是MultiThreadedHttpConnectionManager中的:

[java] view plaincopy
  1. /** The default maximum number of connections allowed per host */  
  2.    public static final int DEFAULT_MAX_HOST_CONNECTIONS = 2;   // Per RFC 2616 sec 8.1.4  
  3.   
  4.    /** The default maximum number of connections allowed overall */  
  5.    public static final int DEFAULT_MAX_TOTAL_CONNECTIONS = 20;  



设置于对应请求的目标主机线程数最多为20条

mgr.getParams().setDefaultMaxConnectionsPerHost(20);

总共的线程数为10000。

mgr.getParams().setMaxTotalConnections(10000);


具体如何分配可以看MultiThreadedHttpConnectionManagermgr的实现代码,在不大于总线程的情况下,最多分配给某个目标主机最多20条线程。


这里有个问题,如果 主要请求两台机器 ,那么最终分配的线程数为20*2=40条,在高并发情况下就会出现阻塞情况。所以对于高并发的线上服务来说,20是比较吝啬的。。


这里是一段httclient请求调用的方法,应该就是在高并发中,阻塞在getConnectionWithTimeout()这个方法中。。具体可能追综下源代码 .。



这里为了测试这个参数引起的问题,简单实现了一个小程序,代码如下:


[java] view plaincopy
  1. package org.yzy.jetty;  
  2.   
  3. import org.apache.commons.httpclient.HttpClient;  
  4. import org.apache.commons.httpclient.HttpMethod;  
  5. import org.apache.commons.httpclient.MultiThreadedHttpConnectionManager;  
  6. import org.apache.commons.httpclient.methods.GetMethod;  
  7.   
  8. public class HttpClientTest {  
  9.   
  10.     static HttpClient client;  
  11.   
  12.     static {  
  13.         MultiThreadedHttpConnectionManager mgr = new MultiThreadedHttpConnectionManager();  
  14.         mgr.getParams().setDefaultMaxConnectionsPerHost(2);  
  15.         mgr.getParams().setMaxTotalConnections(10);  
  16.         mgr.getParams().setConnectionTimeout(2000);  
  17.         mgr.getParams().setSoTimeout(1000);  
  18.         client = new HttpClient(mgr);  
  19.     }  
  20.   
  21.     public static void main(String[] args) {  
  22.           
  23.         Thread t[]=new Thread[3];  
  24.   
  25.         for (int i = 0; i < t.length; i++) {  
  26.             t[i]=new Thread(new Send());  
  27.         }  
  28.           
  29.         for (int i = 0; i < t.length; i++) {  
  30.             t[i].start();  
  31.         }  
  32.       
  33.     }  
  34.   
  35.     public static class Send implements Runnable {  
  36.         @Override  
  37.         public void run() {  
  38.             try{  
  39.                 HttpMethod method = new GetMethod("http://localhost:8080/solr");  
  40.                 System.out.println(Thread.currentThread().getName()+"-" +Thread.currentThread().getId() +":send");  
  41.                 int result = client.executeMethod(method);  
  42.                 System.out.println(Thread.currentThread().getName()+"-" +Thread.currentThread().getId() +":back" +result);  
  43.             }catch(Exception e){  
  44.                 e.printStackTrace();  
  45.             }  
  46.   
  47.         }  
  48.   
  49.     }  
  50.   
  51. }  



运行结果如下:

Thread-2-11:send

Thread-1-10:send

Thread-3-12:send

Thread-3-12:back200

Thread-2-11:back200


可以看到有一条没有执行成功,一直阻塞中。。。




将Send类修改一下,代码再改下:


[java] view plaincopy
  1. public static class Send implements Runnable {  
  2.         @Override  
  3.         public void run() {  
  4.             HttpMethod method = new GetMethod("http://localhost:8080/solr");  
  5.             try{  
  6.                 System.out.println(Thread.currentThread().getName()+"-" +Thread.currentThread().getId() +":send");  
  7.                 int result = client.executeMethod(method);  
  8.                 System.out.println(Thread.currentThread().getName()+"-" +Thread.currentThread().getId() +":back" +result);  
  9.                 Thread.sleep(1000);  
  10.             }catch(Exception e){  
  11.                 e.printStackTrace();  
  12.             }finally{  
  13.                 method.releaseConnection();  
  14.                 System.out.println("relase..");  
  15.             }  
  16.   
  17.         }  
  18.   
  19.     }  


再运行:


Thread-3-12:send

Thread-1-10:send

Thread-2-11:send

Thread-2-11:back200

Thread-3-12:back200

relase..

relase..

Thread-1-10:back200

relase..


当有连接断掉的时候,阻塞的线程可用。。完成请求。。


还有个问题,就是用户请求solr时,分发为三个请求,分别请求,主索引,小索引,专辑索引,最后发现,总是主索引抛出socke超时异常,又作何解释呢:


首先,分发的三个请求是在多线程的情况下处理的,当主索引搜索时间过长,而小索引,专辑索引搜索时间较短,比较快地 releaseConnection,

所以相对大索引来说,其它两上在同一时间内可用的连接比较多,相反大索引由于响应过慢,导致同一时间内占握的连接超过了默认设置的20条连接。

所以才会在大索引上产生请求阻塞。


至于为什么抛出socket超时异常,因为solr的服务运行在tomcat上,tomcat 设置了连接超时等待,比如3000ms,这个时候,由于阻塞的连接没完成,所以这个时候,tomcat主动抛弃了连接,最后看到的就是socket超时异常。。


因为socket异常主要发生在等待读取数据造成的。。。这就是我的分析。。。



当然solr的最新版本已解决了这个问题,连接池已改为可以配置的形式。。


新版本的solr可配置方式,请看wiki..  http://wiki.apache.org/solr/SolrConfigXml/


[html] view plaincopy
  1. <requestHandler name="standard" class="solr.SearchHandler" default="true">  
  2.     <!-- other params go here -->  
  3.      <shardHandlerFactory class="HttpShardHandlerFactory">  
  4.         <int name="socketTimeOut">1000</int>  
  5.         <int name="connTimeOut">5000</int>  
  6.       </shardHandler>  
  7.   </requestHandler>  

[html] view plaincopy
  1. The parameters that can be specified are as follows:  
  2.   
  3. socketTimeout. default: 0 (use OS default) - The amount of time in ms that a socket is allowed to wait for  
  4. connTimeout. default: 0 (use OS default) - The amount of time in ms that is accepted for binding / connection a socket  
  5. maxConnectionsPerHost. default: 20 - The maximum number of connections that is made to each individual shard in a distributed search  
  6. corePoolSize. default: 0 - The retained lowest limit on the number of threads used in coordinating distributed search  
  7. maximumPoolSize. default: Integer.MAX_VALUE - The maximum number of threads used for coordinating distributed search  
  8. maxThreadIdleTime. default: 5 seconds - The amount of time to wait for before threads are scaled back in response to a reduction in load  
  9. sizeOfQueue. default: -1 - If specified the thread pool will use a backing queue instead of a direct handoff buffer. This may seem difficult to grasp, essentially high throughput systems will want to configure this to be a direct hand off (with -1). Systems that desire better latency will want to configure a reasonable size of queue to handle variations in requests.  
  10. fairnessPolicy. default: false - Chooses in the JVM specifics dealing with fair policy queuing, if enabled distributed searches will be handled in a First in First out fashion at a cost to throughput. If disabled throughput will be favoured over latency.  


http://blog.csdn.net/duck_genuine/article/details/7839479

0 0