最近在总结项目中,其中包含调用第三方做HTTP通讯的处理,主要涉及了两类通讯框架的依赖,即HttpClient和OkHttp;
虽然,HTTP协议目前已经进化到HTTP2了,但有没有发现,在HTTP客户端调用这一层很少用到HTTP2的特性;
下面就HttpClient及OkHttp在项目中的使用、及遇到的问题做简单说明。
HttpClient
传送门
https://hc.apache.org
说明
HttpClient被用来提供高效的、最新的、功能丰富的支持HTTP协议的客户端编程工具包,并且它支持HTTP协议最新的版本和建议。
特性:
基于标准、纯净的java语言。实现了Http1.0和Http1.1
以可扩展的面向对象的结构实现了Http全部的方法(GET, POST, PUT, DELETE, HEAD, OPTIONS, and TRACE)。
支持HTTPS协议。
通过Http代理建立透明的连接。
利用CONNECT方法通过Http代理建立隧道的https连接。
Basic, Digest, NTLMv1, NTLMv2, NTLM2 Session, SNPNEGO/Kerberos认证方案。
插件式的自定义认证方案。
便携可靠的套接字工厂使它更容易地使用第三方解决方案。
连接管理器支持多线程应用。支持设置最大连接数,同时支持设置每个主机的最大连接数,发现并关闭过期的连接。
自动处理Set-Cookie中的Cookie。
插件式的自定义Cookie策略。
Request的输出流可以避免流中内容直接缓冲到socket服务器。
Response的输入流可以有效的从socket服务器直接读取相应内容。
在http1.0和http1.1中利用KeepAlive保持持久连接。
直接获取服务器发送的response code和 headers。
设置连接超时的能力。
实验性的支持http1.1 response caching。
源代码基于Apache License 可免费获取。
使用步骤:
创建HttpClient对象
创建请求方法的实例,并指定请求URL。如果需要发送GET请求,创建HttpGet对象;如果需要发送POST请求,创建HttpPost对象
如果需要发送请求参数,可调用HttpGet、HttpPost共同的setParams(HttpParams params)方法来添加请求参数;对于HttpPost对象而言,也可调用setEntity(HttpEntity entity)方法来设置请求参数
调用HttpClient对象的execute(HttpUriRequest request)方法发送请求,该方法返回一个HttpResponse
调用HttpResponse的getAllHeaders()、getHeaders(String name)等方法可获取服务器的响应头;调用HttpResponse的getEntity()方法可获取HttpEntity对象,该对象包装了服务器的响应内容。程序可通过该对象获取服务器的响应内容
释放连接。无论执行方法是否成功,都必须释放连接
Get
public void get() {
CloseableHttpClient httpclient = HttpClients.createDefault();
try {
// 创建httpget.
HttpGet httpget = new HttpGet("http://www.baidu.com/");
logger.info("executing request " + httpget.getURI());
// 执行get请求.
CloseableHttpResponse response = httpclient.execute(httpget);
try {
// 获取响应实体
HttpEntity entity = response.getEntity();
System.out.println("--------------------------------------");
// 打印响应状态
System.out.println(response.getStatusLine());
if (entity != null) {
// 打印响应内容长度
System.out.println("Response content length: " + entity.getContentLength());
// 打印响应内容
System.out.println("Response content: " + EntityUtils.toString(entity));
}
System.out.println("------------------------------------");
} finally {
response.close();
}
} catch (ClientProtocolException e) {
e.printStackTrace();
} catch (ParseException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
// 关闭连接,释放资源
try {
httpclient.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
Post
/**
* 发送 post请求访问本地应用并根据传递参数不同返回不同结果
*/
public void post() {
// 创建默认的httpClient实例.
CloseableHttpClient httpclient = HttpClients.createDefault();
// 创建httppost
HttpPost httppost = new HttpPost("http://localhost:8080/myDemo/Ajax/serivceJ.action");
// 创建参数队列
List<NameValuePair> formparams = new ArrayList<NameValuePair>();
formparams.add(new BasicNameValuePair("type", "house"));
UrlEncodedFormEntity uefEntity;
try {
uefEntity = new UrlEncodedFormEntity(formparams, "UTF-8");
httppost.setEntity(uefEntity);
System.out.println("executing request " + httppost.getURI());
CloseableHttpResponse response = httpclient.execute(httppost);
try {
HttpEntity entity = response.getEntity();
if (entity != null) {
System.out.println("--------------------------------------");
System.out.println("Response content: " + EntityUtils.toString(entity, "UTF-8"));
System.out.println("--------------------------------------");
}
} finally {
response.close();
}
} catch (ClientProtocolException e) {
e.printStackTrace();
} catch (UnsupportedEncodingException e1) {
e1.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
// 关闭连接,释放资源
try {
httpclient.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
文件处理
上传文件
/**
* 发送 post请求访问本地应用并根据传递参数不同返回不同结果
*/
public void post() {
// 创建默认的httpClient实例.
CloseableHttpClient httpclient = HttpClients.createDefault();
// 创建httppost
HttpPost httppost = new HttpPost("http://localhost:8080/myDemo/Ajax/serivceJ.action");
// 创建参数队列
List<NameValuePair> formparams = new ArrayList<NameValuePair>();
formparams.add(new BasicNameValuePair("type", "house"));
UrlEncodedFormEntity uefEntity;
try {
uefEntity = new UrlEncodedFormEntity(formparams, "UTF-8");
httppost.setEntity(uefEntity);
System.out.println("executing request " + httppost.getURI());
CloseableHttpResponse response = httpclient.execute(httppost);
try {
HttpEntity entity = response.getEntity();
if (entity != null) {
System.out.println("--------------------------------------");
System.out.println("Response content: " + EntityUtils.toString(entity, "UTF-8"));
System.out.println("--------------------------------------");
}
} finally {
response.close();
}
} catch (ClientProtocolException e) {
e.printStackTrace();
} catch (UnsupportedEncodingException e1) {
e1.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
// 关闭连接,释放资源
try {
httpclient.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
下载文件
public class FileFetchEngine {
public String[] fetch(String url,String sysId, String fileType, String filepath, String date)
throws Exception {
String tmpfilepath = filepath ;
CloseableHttpClient httpclient = HttpClients.createDefault();
FileOutputStream out = null;
try {
HttpPost httppost = new HttpPost(url);
HttpEntity reqEntity = MultipartEntityBuilder.create().build();
httppost.setEntity(reqEntity);
CloseableHttpResponse response = httpclient.execute(httppost);
try {
String ret = "" ;
HttpEntity resEntity = response.getEntity();
if (resEntity == null) {
return null ;
}
if(resEntity.getContentType()==null) {
return null ;
}
String contentType = resEntity.getContentType().getValue().split(";")[0] ;
if("text/xml".equals(contentType)) {
byte[] content = new byte[(int) resEntity.getContentLength()];
resEntity.getContent().read(content);
ret = new String(content, "UTF-8");
JSONObject retJson = JSONObject.fromObject(ret) ;
String retCode = retJson.getString("retCode") ;
String retMsg = retJson.getString("retMsg") ;
return new String[]{retCode,retMsg};
} else {
File dir = new File(filepath);
if(!dir.exists()) {
dir.mkdirs() ;
}
filepath += File.separator+fileType+"_"+date+".txt.gz" ;
out = new FileOutputStream(filepath);
int c;
while ((c = resEntity.getContent().read()) != -1) {
out.write(c);
}
}
try {
TarAndGzUtil.unGz(filepath, tmpfilepath);
} catch (Exception e) {
throw new Exception("文件解压失败!"+e) ;
}
EntityUtils.consume(resEntity);
return new String[]{"0000","下载成功!",filepath};
} finally {
response.close();
}
} finally {
try {
if (out != null)
out.close();
} catch (IOException e) {
Enviroment.getEnv().getLogBean().error(e);
}
httpclient.close();
}
}
}
参考资料
https://blog.csdn.net/w372426096/article/details/82713315
OkHttp
传送门
传送门:https://square.github.io/okhttp/
代码片段:https://square.github.io/okhttp/recipes/
说明
OkHttp是一个高效的HTTP客户端
特性:
支持HTTP/2,允许所有同一个主机地址的请求共享同一个socket连接
连接池减少请求延时
透明的GZIP压缩减少响应数据的大小
缓存响应内容,避免出现一些完全重复的请求
使用步骤:
创建OkHttpClient对象
创建Request对象
将Request 对象封装为Call
通过Call 来执行同步或异步请求,调用execute方法同步执行,调用enqueue方法异步执行
maven坐标
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp</artifactId>
<version>3.12.13</version>
</dependency>
Get(同步)
为什么写异步的处理?因为我在实际业务开发中始终没有遇到异步HTTP处理的场景;
以下是官网的例子
private final OkHttpClient client = new OkHttpClient();
public void run() throws Exception {
Request request = new Request.Builder()
.url("https://publicobject.com/helloworld.txt")
.build();
try (Response response = client.newCall(request).execute()) {
if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);
Headers responseHeaders = response.headers();
for (int i = 0; i < responseHeaders.size(); i++) {
System.out.println(responseHeaders.name(i) + ": " + responseHeaders.value(i));
}
System.out.println(response.body().string());
IOUtils.closeQuietly(response);
}
}
Post(项目中用得最多的)
String url = "" ;
String content = "{}";
OkHttpClient client = new OkHttpClient.Builder()
.connectTimeout(10, TimeUnit.SECONDS)
.writeTimeout(10, TimeUnit.SECONDS)
.readTimeout(30, TimeUnit.SECONDS)
.build();
MediaType mediaType = MediaType.parse("application/json;charset=utf-8");
RequestBody body = RequestBody.create(mediaType, content.toString());
Request request = new Request.Builder()
.url(url)
.method("POST", body)
.addHeader("Content-Type", "application/json;charset=utf-8")
.build();
try {
Response response = client.newCall(request).execute();
String responseMsg = "";
if (response.isSuccessful()) {
responseMsg = response.body() == null ? "" : response.body().string();
JsonObject respJsonObject = new JsonParser()
.parse(responseMsg)
.getAsJsonObject();
// 业务处理
} else {
responseMsg = "交易端通讯失败!";
// 错误处理
}
IOUtils.closeQuietly(response);
} catch (Throwable e) {
// 异常处理
}
注意
- 每次response都必须关闭掉,否则会报警,告诉你太多响应没有关闭;
- OkHttpClient可以为单例模式,也可以不是;
- 非单例模式下,OkHttpClient会维护一个单独的线程池,高并发下会出现连接池线程增多;因此OkHttpClient可以采用同一个连接池的管理对象;或直接使用单例模式;
- OkHttpClient中的超时时间设置也很有必要,默认超时时间往往会影响业务效率;
文件处理
上传文件
public static final MediaType MEDIA_TYPE_MARKDOWN
= MediaType.parse("text/x-markdown; charset=utf-8");
private final OkHttpClient client = new OkHttpClient();
public void run() throws Exception {
File file = new File("README.md");
Request request = new Request.Builder()
.url("https://api.github.com/markdown/raw")
.post(RequestBody.create(MEDIA_TYPE_MARKDOWN, file))
.build();
try (Response response = client.newCall(request).execute()) {
if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);
System.out.println(response.body().string());
}
}
下载文件
也可查看Get方式的处理,本身就是一个文件下载的例子;
在get的基础上,获取到response后将response返回的stream写到文件里面即可
private final OkHttpClient client = new OkHttpClient();
public void run() throws Exception {
Request request = new Request.Builder()
.url("https://publicobject.com/helloworld.txt")
.build();
try (Response response = client.newCall(request).execute()) {
if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);
File file = new File("");
writeFile(file,response);
}
}
private void writeFile(File file,Response response) {
OutputStream outputStream = null;
InputStream inputStream = response.body().byteStream();
try {
outputStream = new FileOutputStream(file);
int len = 0;
byte[] buffer = new byte[1024*10];
while ((len = inputStream.read(buffer))!=-1){
outputStream.write(buffer,0,len);
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}finally {
try {
if(inputStream != null)
inputStream.close();
if(outputStream != null)
outputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
你看,奇怪的知识是不是又增加了!