gip*_*ani 12 performance spring android httpurlconnection android-networking
我正在尝试将测试文件发布到使用Android部署在tomcat上的spring rest servlet.我正在开发Android 4.1.2,但我已经在4.0.3上验证了同样的问题.
问题是文件上传需要很长时间(对于4MB文件大约需要70秒),也在本地网络中.使用3g连接可以节省时间.我已经排除它可能是服务器问题:使用curl执行相同的调用需要1/2秒,并且使用apache作为后端结果是相同的.
使用HttpClient工作正常.
我正在使用Spring Android RestClient 1.0.1.RELEASE,并且考虑到Android版本以及我没有覆盖默认行为的事实,它使用HttpUrlConnection而不是HttpClient来发出http请求.
我还实现了我的自定义ClientHttpRequestFactory,以便操纵SSL连接的一些细节,我已经定义了自己的ClientHttpRequestInterceptor实现,以便修改身份验证头.
我也设置setBufferRequestBody(false)为了避免OutOfMemoryException大文件.但是这个属性对所需的时间没有影响.
MyClientHttpRequestFactory:
public class MyClientHttpRequestFactory extends SimpleClientHttpRequestFactory{
@Override
protected void prepareConnection(HttpURLConnection connection, String httpMethod) throws IOException {
super.prepareConnection(connection, httpMethod);
connection.setConnectTimeout(240 * 1000);
connection.setReadTimeout(240 * 1000);
if ("post".equals(httpMethod.toLowerCase())) {
setBufferRequestBody(false);
}else {
setBufferRequestBody(true);
}
}
@Override
protected HttpURLConnection openConnection(URL url, Proxy proxy) throws IOException {
final HttpURLConnection httpUrlConnection = super.openConnection(url, proxy);
if (url.getProtocol().toLowerCase().equals("https")
&&
settings.selfSignedCert().get())
{
try {
((HttpsURLConnection)httpUrlConnection).setSSLSocketFactory(getSSLSocketFactory());
((HttpsURLConnection)httpUrlConnection).setHostnameVerifier(new NullHostnameVerifier());
} catch (Exception e) {
MyLog.e(LOG_TAG, "OpenConnection", e);
}
}
return httpUrlConnection;
}
Run Code Online (Sandbox Code Playgroud)
MyClientHttpRequestInterceptor:
public class MyClientHttpRequestInterceptor implements ClientHttpRequestInterceptor {
@Override
public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException {
final HttpHeaders headers = request.getHeaders();
headers.setAuthorization(new HttpBasicAuthentication( settings.username().get(), settings.password().get()));
if (settings.enable_gzip().get()) {
headers.setAcceptEncoding(ContentCodingType.GZIP);
}
return execution.execute(request, body);
}
}
Run Code Online (Sandbox Code Playgroud)
在这里我的休息电话:
List<ClientHttpRequestInterceptor> interceptors = Arrays.asList((ClientHttpRequestInterceptor)myClientHttpRequestInterceptor);
RestTemplate restTemplate = new RestTemplate();
restTemplate.getMessageConverters().add(new FormHttpMessageConverter());
restTemplate.getMessageConverters().add(new StringHttpMessageConverter());
restTemplate.getMessageConverters().add(new MappingJackson2HttpMessageConverter());
restTemplate.setInterceptors(interceptors);
MultiValueMap<String, Object> parts = new LinkedMultiValueMap<String, Object>();
parts.add("file", new FileSystemResource("/sdcard/test/4MB_file"));
HttpEntity<MultiValueMap> requestEntity = new HttpEntity<MultiValueMap>(parts);
restTemplate.exchange(myUrl, HttpMethod.POST, requestEntity, Integer.class).getBody();
}
Run Code Online (Sandbox Code Playgroud)
看看Spring的Android源代码,我的请求传递的下一行代码是:
public ClientHttpRequest createRequest(URI uri, HttpMethod httpMethod) throws IOException {
HttpURLConnection connection = openConnection(uri.toURL(), this.proxy);
prepareConnection(connection, httpMethod.name());
if (this.bufferRequestBody) {
return new SimpleBufferingClientHttpRequest(connection);
} else {
return new SimpleStreamingClientHttpRequest(connection, this.chunkSize);
}
}
Run Code Online (Sandbox Code Playgroud)
因为this.bufferRequestBody是false,return new SimpleStreamingClientHttpRequest(connection, this.chunkSize);被执行(用chunkSize = 0)
SimpleStreamingClientHttpRequest(HttpURLConnection connection, int chunkSize) {
this.connection = connection;
this.chunkSize = chunkSize;
// Bugs with reusing connections in Android versions older than Froyo (2.2)
if (olderThanFroyo) {
System.setProperty("http.keepAlive", "false");
}
}
Run Code Online (Sandbox Code Playgroud)
然后:
ClientHttpRequest delegate = requestFactory.createRequest(request.getURI(), request.getMethod());
delegate.getHeaders().putAll(request.getHeaders());
if (body.length > 0) {
FileCopyUtils.copy(body, delegate.getBody());
}
return delegate.execute();
Run Code Online (Sandbox Code Playgroud)
从这里我认为所有的android子系统..
我已经转储了tcp流量并进行了分析:
POST /urlWherePost HTTP/1.1
Content-Type: multipart/form-data;boundary=nKwsP85ZyyzSDuAqozCTuZOSxwF1jLAtd0FECUPF
Authorization: Basic xxxxxxxxxxxxxxxxxxxxxxxxxx=
Accept-Encoding: gzip
User-Agent: Dalvik/1.6.0 (Linux; U; Android 4.1.2; sdk Build/MASTER)
Host: 192.168.168.225:8080
Connection: Keep-Alive
Content-Length: 4096225
--nKwsP85ZyyzSDuAqozCTuZOSxwF1jLAtd0FECUPF
Content-Disposition: form-data; name="file"; filename="4MB_file"
Content-Type: application/octet-stream
Content-Length: 4096000
Run Code Online (Sandbox Code Playgroud)
我试着用curl重新创建类似的请求:
curl --verbose
-H "Connection: Keep-Alive"
-H "Content-Type: multipart/form-data"
-H "Accept-Encoding: gzip"
-H "Content-Disposition: form-data; name=\"file\"; filename=\"4MB_file\""
-H "Content-Type: application/octet-stream"
--user xxx:xxx
-X POST
--form file=@4MB_file
http://192.168.168.225:8080/urlWherePost
Run Code Online (Sandbox Code Playgroud)
但随着卷曲,帖子还可以.
发布json数据不是问题(可能是小体型).但是当我尝试发送"大"文件时,时间会增加.
查看DDMS shell,在网络统计上,我还发现TX的网络吞吐量从未超过250kb.似乎有一个bootleneck,但如何调查呢?在哪里可以查看,我可以更改哪个参数?
谢谢你的任何建议!
小智 1
您是否尝试过使用 MultipartEntity 方法?当我从服务器下载大量 JSON 数据时遇到了同样的问题,但我切换到这种方法并捕获了服务器提供给我的所有数据。
HttpClient httpclient = new DefaultHttpClient();
HttpPost httppost = new HttpPost("http://myurl.com");
try {
MultipartEntity entity = new MultipartEntity();
entity.addPart("type", new StringBody("json"));
entity.addPart("data", new JSONObject(data));
httppost.setEntity(entity);
HttpResponse response = httpclient.execute(httppost);
} catch (ClientProtocolException e) {
} catch (IOException e) {
} catch (JSONException e){
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
4018 次 |
| 最近记录: |