zoukankan      html  css  js  c++  java
  • HttpClient有关方法及相关优化整理

    HttpClient4中采用 ThreadSafeClientConnManager来保证线程的安全,优于2.0的 MultiThreadedHttpConnectionManager类。另外Apache官方强烈推荐只使用一个HttpClient的实例,所以我 们可以将以下demo方法写成单例模式。
    demo将使用StringEntity来完成不指定参数名发送Post,已经采用ThreadSafeClientConnManager来保证线程的安全

    public String sendToHttpClientGprs(String url,String content) { 
    BufferedReader reader = null; 
    StringBuffer stringBuffer = null; 
    HttpParams httpParams = new BasicHttpParams(); 
    // 设置超时 
    ConnManagerParams.setTimeout(httpParams, 5*1000); 
    HttpConnectionParams.setConnectionTimeout(httpParams, 5*1000); 
    HttpConnectionParams.setSoTimeout(httpParams, 5*1000); 
    // 多线程最大连接数 
    ConnManagerParams.setMaxConnectionsPerRoute(httpParams, new ConnPerRouteBean(5)); 
    // 多线程总连接数 
    ConnManagerParams.setMaxTotalConnections(httpParams, 10); 
    // 设置异常处理机制 
    HttpProtocolParams.setUseExpectContinue(httpParams, true); 
    // 设置是否检查旧连接 
    HttpConnectionParams.setStaleCheckingEnabled(httpParams, false); 
    // 设置版本 
    HttpProtocolParams.setVersion(httpParams, HttpVersion.HTTP_1_1); 
    // 设置编码 
    HttpProtocolParams.setContentCharset(httpParams, HTTP.UTF_8); 
    // 设置重定向 
    HttpClientParams.setRedirecting(httpParams, false); 
    // 设置userAgent 
    String userAgent = "Mozilla/5.0 (Windows; U; Windows NT 5.1; zh-CN; rv:1.9.2) Gecko/20100115 Firefox/3.6"; 
    HttpProtocolParams.setUserAgent(httpParams, userAgent); 
    //设置是否延迟发送 
    HttpConnectionParams.setTcpNoDelay(httpParams, true); 
    //设置缓存大小 
    HttpConnectionParams.setSocketBufferSize(httpParams, DEFAULT_SOCKET_BUFFER_SIZE); 
    // 支持http与https 
    SchemeRegistry schemeRegistry = new SchemeRegistry(); 
    schemeRegistry.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), 80)); 
    schemeRegistry.register(new Scheme("https", SSLSocketFactory.getSocketFactory(), 443)); 
    //ThreadSafeClientConnManager线程安全管理类 
    ClientConnectionManager manager = new ThreadSafeClientConnManager(httpParams, schemeRegistry); 
    HttpClient   client = new DefaultHttpClient(manager, httpParams); 
    //post请求 
    HttpPost httppost =  new HttpPost(path); 
    httppost.setHeader("connection", "Keep-Alive"); 
    httppost.setHeader("Content-Type", "text/plain"); 
    try { 
    //直接传入发送内容,不指定参数名 
    StringEntity myEntity = new StringEntity(content, "UTF-8"); 
    httppost.setEntity(myEntity); 
    // 发送请求 
    HttpResponse response = client.execute(httppost); 
    Log.i("ResponseCode", response.getStatusLine().getStatusCode() + ""); 
    // 请求成功 
    if (response.getStatusLine().getStatusCode() == HttpStatus.SC_OK) { 
    reader = new BufferedReader(new InputStreamReader(response.getEntity().getContent())); 
    stringBuffer = new StringBuffer(); 
    String line = ""; 
    while ((line = reader.readLine()) != null) { 
    stringBuffer.append(line); 
    } 
    } 
    } catch (Exception e) { 
    e.printStackTrace(); 
    }  finally { 
    try { 
    // 关闭流 
    if (null != reader) { 
    reader.close(); 
    reader = null; 
    } 
    } catch (IOException e) { 
    e.printStackTrace(); 
    } 
    } 
    if (null == stringBuffer) { 
    return "-1"; 
    } else { 
    return stringBuffer.toString(); 
    } 
    } 
    

    先看看以前的代码,每次请求都会创建一个HttpClient,代码如下:

    HttpClient client = getHttpClient(5000, 5000);
    
    GetMethod getMethod = new GetMethod(url);
    
    getMethod.setQueryString(parameter);
    
    byte[] responseBody = null;
    
    try {
    
        int statusCode = client.executeMethod(getMethod);
    
        if (statusCode != HttpStatus.SC_OK) {
    
            logger.error("error !" + url + ",statusCode " + statusCode);
    
        }
    
        responseBody = getMethod.getResponseBody();
    
    }catch (IOException e) {
    
        logger.error("error!" + url, e);
    
    } finally {
    
       getMethod.releaseConnection();
    
    }
    


    这是最简单的代码,很多例子就是这样开始的,如果少量请求还行,请求很频繁的话,这样的代码不是很好。(注意这里连接用完后必须释放连接)。
    首先,HttpClient可以共用,减少创建HttpClient的开销。当然,如果你的应用调用HttpClient并不怎么频繁的话那就没必要共用了,毕竟在内存中维护一个空闲的httpClient对象是不保险的。
    其次,Connection可以重用,减少建立连接的开销。
    要完成以上两点,可以用多线程下的MultiThreadedHttpConnectionManager管理HttpConnection和 HttpClient。MultiThreadedHttpConnectionManager管理的HttpClient是线程安全的,可以做成单例 的。但是值得注意的是每个线程应该有自己的HttpMethod和HttpState、HttpConfiguration,以区分每次请求的host和 HttpSession 。如下所示:

    private static MultiThreadedHttpConnectionManager httpConnectionManager = new MultiThreadedHttpConnectionManager();
    
    private HttpClient client = new HttpClient(httpConnectionManager);
    

    定义好后,在静态块中初始化相关参数

    static {              
    
        //每主机最大连接数和总共最大连接数,通过hosfConfiguration设置host来区分每个主机  
    
        client.getHttpConnectionManager().getParams().setDefaultMaxConnectionsPerHost(8);
    
        client.getHttpConnectionManager().getParams().setMaxTotalConnections(48);
    
        client.getHttpConnectionManager().getParams().setConnectionTimeout(5000);
    
        client.getHttpConnectionManager().getParams().setSoTimeout(5000);
    
        client.getHttpConnectionManager().getParams().setTcpNoDelay(true);
    
        client.getHttpConnectionManager().getParams().setLinger(1000);                   
    
        //失败的情况下会进行3次尝试,成功之后不会再尝试
    
        client.getHttpConnectionManager().getParams().setParameter(HttpMethodParams.RETRY_HANDLER, new DefaultHttpMethodRetryHandler());
    
    }
    


    HttpConfiguration中有一些参数对提高性能有一些帮助,主要说明如下:


    DefaultMaxConnectionsPerHost参数定义每台主机允许的最大连接数,默认为2。这个参数只能用于一些特定的httpConnectionManager,比如MultiThreadedHttpConnectionManager。
    MaxTotalConnections参数表示httpConnectionManager管理的最大连接数,默认为20。同上个参数,这个参数也只是在某些特定的httpConnectionManager中有用。
    setTcpNoDelay(true)设置是否启用Nagle算法,设置true后禁用Nagle算法,默认为false(即默认启用Nagle算 法)。Nagle算法试图通过减少分片的数量来节省带宽。当应用程序希望降低网络延迟并提高性能时,它们可以关闭Nagle算法,这样数据将会更早地发 送,但是增加了网络消耗。
    setLinger(1000)设置socket延迟关闭时间,值为0表示这个选项是关闭的,值为-1表示使用JRE的默认设置。
    setStaleCheckingEnabled(true)参数设置是否启用旧连接检查,默认是开启的。关闭这个旧连接检查可以提高一点点性能,但是增 加了I/O错误的风险(当服务端关闭连接时)。开启这个选项则在每次使用老的连接之前都会检查连接是否可用,这个耗时大概在15-30ms之间[3]。


    然后,在每个线程代码中,创建自己的HttpMethod。


    还有一个很重要的优化点是采用请求/响应实体流,尤其是在请求频繁数据量大的情况下,很大的实体不会被缓存在内存中而直接发送或接收,采用流能有效的提高 性能。虽然这些实体可以是字符串或者字节数组,但是它们容易导致内存泄露,你得小心的使用他们,因为它们是整个实体缓存在内存中的。

     
    对于响应流,采用method的getResponseBodyAsStream()来代替getResponseBody() 和getResponseBodyAsString().如下所示:

    HttpClient httpclient = new HttpClient();
    
      GetMethod httpget = new GetMethod("http://www.myhost.com/");
    
      try {
    
        httpclient.executeMethod(httpget);
    
        Reader reader = new InputStreamReader(httpget.getResponseBodyAsStream(), httpget.getResponseCharSet());
    
        // consume the response entity
    
      } finally {
    
        httpget.releaseConnection();
    
      }
    

    而对于请求流,可以通过实现RequestEntity自定义自己的各种流,httpclient包含了常见的几种实现,如 FileRequestEntity、ByteArrayRequestEntity、StringRequestEntity、 MultipartRequestEntity等。使用示例如下:

    File myfile = new File("myfile.txt");
    
    PostMethod httppost = new PostMethod("/stuff");
    
    httppost.setRequestEntity(new FileRequestEntity(myfile));
    


    如果客户端和服务器的通讯不需要保持会话状态的话,可以通过禁用Cookie来提高一点点性能,比如蜘蛛爬虫之类应用。如下:

    HttpMethod method = new GetMethod();
    
    method.getParams().setCookiePolicy(CookiePolicy.IGNORE_COOKIES);
    
    method.setRequestHeader("Cookie", "special-cookie=value");
    
  • 相关阅读:
    初学网络编程
    Python模块(三)(正则,re,模块与包)
    Python模块(二)(序列化)
    idea2020.2安装与破解
    部署-jenkins发布项目到linux环境
    部署-jenkins发布项目到windows环境
    部署-jenkins与gitlab结合使用
    部署-docker推送镜像到远程仓库
    部署-gitlab克隆地址踩坑
    部署-docker安装jenkins
  • 原文地址:https://www.cnblogs.com/weixiao870428/p/3766124.html
Copyright © 2011-2022 走看看