从入门到精通:HttpClient深度剖析与实战指南
一、引言
1.1 背景引入
在当今数字化时代,网络编程已成为软件开发中不可或缺的一部分。而 HTTP 通信作为网络编程的核心,承担着客户端与服务器之间数据传输的重任。无论是 Web 应用、移动应用,还是分布式系统,HTTP 协议都扮演着关键角色,它使得不同设备、不同平台之间能够高效地进行数据交互。
在 Java 开发领域,为了实现 HTTP 通信,我们有众多工具可供选择,其中 Apache HttpClient 脱颖而出,成为开发者们的得力助手。HttpClient 以其强大的功能、丰富的特性以及高度的可定制性,在 Java 的 HTTP 通信场景中占据着举足轻重的地位。它不仅支持各种 HTTP 请求方法,如 GET、POST、PUT、DELETE 等,还能轻松处理复杂的请求头、响应体以及各种网络异常情况,极大地简化了 Java 开发者在 HTTP 通信方面的编程工作。
1.2 目标读者
本文主要面向对 HttpClient 感兴趣的 Java 开发者,无论你是刚刚踏入 Java 编程世界的新手,还是已经具备一定开发经验,希望深入了解 HttpClient 的进阶开发者,都能从本文中获取有价值的知识和实用的技巧。对于新手来说,本文将从基础知识入手,逐步引导你掌握 HttpClient 的使用方法;而对于有经验的开发者,本文将深入剖析 HttpClient 的原理、高级特性以及实际应用中的优化策略,帮助你进一步提升 HTTP 通信编程能力。
1.3 预期收获
通过阅读本文,读者将全面掌握 HttpClient 的基本原理,包括其工作流程、核心组件以及与 HTTP 协议的交互机制。在使用方法上,读者将学会如何创建 HttpClient 实例、发送各种类型的 HTTP 请求(GET、POST、PUT、DELETE 等),并正确处理服务器返回的响应。同时,还将了解如何设置请求头、请求体,以及处理常见的网络异常情况。
此外,本文还将深入探讨 HttpClient 在实际应用中的常见问题及解决方法,如连接超时、重定向处理、认证授权等。通过学习这些内容,读者能够在实际项目中更加灵活、高效地运用 HttpClient,提升 HTTP 通信编程的质量和效率,为开发出稳定、可靠的网络应用奠定坚实的基础。
二、HttpClient 基础认知
2.1 是什么
HttpClient 是 Apache HttpComponents 项目的重要组成部分,它是专门为创建 HTTP 客户端程序而设计的强大工具包。在 Java 开发中,当我们需要与 HTTP 服务器进行交互,发送请求并接收响应时,HttpClient 就派上了用场。它就像是一个专业的 HTTP 通信使者,能够准确无误地将我们的请求发送到服务器,并把服务器的响应带回来。
从本质上讲,HttpClient 是对 HTTP 协议的一层封装,它将 HTTP 协议中复杂的操作和细节进行了抽象,为开发者提供了一套简洁、易用的 API。通过这些 API,我们可以轻松地构建各种类型的 HTTP 请求,无论是简单的 GET 请求获取网页内容,还是复杂的 POST 请求提交表单数据、上传文件等,都能轻松实现。
2.2 为什么要用
在 Java 中,JDK 自带了一些 HTTP 访问的功能,比如HttpURLConnection。但是,与 HttpClient 相比,它就显得有些力不从心了。JDK 自带的 HTTP 访问功能虽然能够实现基本的 HTTP 请求和响应操作,但功能相对单一,使用起来也不够灵活。例如,在处理复杂的请求头设置、请求参数传递、响应数据解析等方面,HttpURLConnection的代码编写会比较繁琐,而且对于一些高级特性,如连接池管理、自动重定向处理、认证授权等,支持得也不够完善。
而 HttpClient 则弥补了这些不足。它提供了丰富的功能和灵活的配置选项,使得 HTTP 通信变得更加高效和便捷。使用 HttpClient,我们可以轻松地设置各种请求头信息,如Content-Type、Authorization等,以满足不同的业务需求。在处理请求参数时,无论是简单的键值对参数,还是复杂的 JSON、XML 格式的数据,HttpClient 都能提供方便的方法进行设置。同时,HttpClient 还内置了强大的连接池管理功能,能够有效地复用 HTTP 连接,减少连接建立和销毁的开销,提高系统的性能和稳定性。
2.3 主要功能
-
支持所有 HTTP 方法:HttpClient 支持 HTTP 协议中定义的所有方法,包括 GET、POST、PUT、DELETE、HEAD、OPTIONS、TRACE 等。这使得我们可以根据具体的业务需求,选择合适的 HTTP 方法来与服务器进行交互。例如,当我们需要获取服务器上的资源时,可以使用 GET 方法;当我们需要向服务器提交数据时,可以使用 POST 方法;当我们需要更新服务器上的资源时,可以使用 PUT 方法;当我们需要删除服务器上的资源时,可以使用 DELETE 方法。
-
自动转向:在 HTTP 通信中,服务器有时会返回重定向响应,指示客户端将请求发送到另一个 URL。HttpClient 能够自动处理这种重定向情况,按照服务器的指示自动将请求发送到新的 URL,无需我们手动编写重定向逻辑。这大大简化了我们的开发工作,确保了请求能够顺利地到达最终的目标地址。
-
HTTPS 协议支持:随着网络安全的重要性日益凸显,HTTPS 协议被广泛应用于保障数据传输的安全。HttpClient 对 HTTPS 协议提供了全面的支持,它能够识别和验证服务器的证书,确保通信的安全性。同时,HttpClient 还支持自定义 SSL 上下文,允许我们根据具体的安全需求进行灵活配置,如信任自定义的证书颁发机构、使用双向认证等。
-
代理服务器支持:在一些网络环境中,我们可能需要通过代理服务器来访问外部资源。HttpClient 支持设置代理服务器,我们只需要配置代理服务器的地址和端口信息,HttpClient 就会通过代理服务器转发请求。这在企业内部网络、网络爬虫等场景中非常有用,可以帮助我们突破网络限制,实现对目标资源的访问。
三、HttpClient 的使用
3.1 环境搭建
在使用 HttpClient 之前,我们需要先将其引入到项目中。如果使用 Maven 项目管理工具,只需要在pom.xml文件中添加以下依赖:
<dependency><groupId>org.apache.httpcomponents</groupId><artifactId>httpclient</artifactId><version>4.5.13</version>
</dependency>
在上述代码中,指定了依赖的组 ID,这里是org.apache.httpcomponents,表示这是 Apache HttpComponents 项目的依赖;指定了依赖的工件 ID,httpclient表示我们要引入的是 HttpClient 库;指定了依赖的版本号,这里使用的是4.5.13版本,你可以根据实际情况选择合适的版本。添加完依赖后,Maven 会自动下载 HttpClient 及其相关的依赖包到项目中。
3.2 基本使用步骤
以发送 GET 请求为例,展示 HttpClient 的基本使用步骤:
import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;public class HttpClientExample {public static void main(String[] args) {// 创建HttpClient实例HttpClient httpClient = HttpClients.createDefault();// 创建HttpGet请求HttpGet httpGet = new HttpGet("http://example.com");try {// 执行请求HttpResponse response = httpClient.execute(httpGet);// 处理响应if (response.getStatusLine().getStatusCode() == 200) {String responseBody = EntityUtils.toString(response.getEntity());System.out.println("响应内容:" + responseBody);} else {System.out.println("请求失败,状态码:" + response.getStatusLine().getStatusCode());}} catch (Exception e) {e.printStackTrace();} finally {// 释放资源(这里httpClient在实际应用中可能会被复用,不一定每次都关闭)((CloseableHttpClient) httpClient).close();}}
}
在这段代码中,首先通过HttpClients.createDefault()方法创建了一个默认配置的HttpClient实例,这个实例就像是我们的 HTTP 通信使者,负责与服务器进行交互。接着,创建了一个HttpGet请求对象,指定了请求的 URL 为http://example.com,这个 URL 就像是我们要访问的目的地地址。然后,使用httpClient.execute(httpGet)方法执行请求,这一步就像是使者带着请求出发去访问目的地,服务器会根据请求返回相应的响应。如果响应的状态码为 200,表示请求成功,我们通过EntityUtils.toString(response.getEntity())方法获取响应体的内容,并打印出来;如果状态码不为 200,则表示请求失败,打印出失败的状态码。最后,在finally块中关闭HttpClient,释放资源,确保程序的资源管理合理。
3.3 常见请求示例
- GET 请求:
import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;public class GetRequestExample {public static void main(String[] args) {HttpClient httpClient = HttpClients.createDefault();HttpGet httpGet = new HttpGet("http://example.com/api/data");try {HttpResponse response = httpClient.execute(httpGet);if (response.getStatusLine().getStatusCode() == 200) {String responseBody = EntityUtils.toString(response.getEntity());System.out.println("GET请求响应: " + responseBody);} else {System.out.println("GET请求失败,状态码:" + response.getStatusLine().getStatusCode());}} catch (Exception e) {e.printStackTrace();} finally {try {((CloseableHttpClient) httpClient).close();} catch (IOException e) {e.printStackTrace();}}}
}
- POST 请求:
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
import org.apache.http.client.HttpClient;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;import java.util.ArrayList;
import java.util.List;public class PostRequestExample {public static void main(String[] args) {HttpClient httpClient = HttpClients.createDefault();HttpPost httpPost = new HttpPost("http://example.com/api/upload");List<NameValuePair> params = new ArrayList<>();params.add(new BasicNameValuePair("key1", "value1"));params.add(new BasicNameValuePair("key2", "value2"));try {httpPost.setEntity(new UrlEncodedFormEntity(params));HttpResponse response = httpClient.execute(httpPost);if (response.getStatusLine().getStatusCode() == 200) {String responseBody = EntityUtils.toString(response.getEntity());System.out.println("POST请求响应: " + responseBody);} else {System.out.println("POST请求失败,状态码:" + response.getStatusLine().getStatusCode());}} catch (Exception e) {e.printStackTrace();} finally {try {((CloseableHttpClient) httpClient).close();} catch (IOException e) {e.printStackTrace();}}}
}
- PUT 请求:
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpPut;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;public class PutRequestExample {public static void main(String[] args) {HttpClient httpClient = HttpClients.createDefault();HttpPut httpPut = new HttpPut("http://example.com/api/update");String json = "{\"key\":\"value\"}";try {httpPut.setEntity(new StringEntity(json));httpPut.setHeader("Content-Type", "application/json");HttpResponse response = httpClient.execute(httpPut);if (response.getStatusLine().getStatusCode() == 200) {String responseBody = EntityUtils.toString(response.getEntity());System.out.println("PUT请求响应: " + responseBody);} else {System.out.println("PUT请求失败,状态码:" + response.getStatusLine().getStatusCode());}} catch (Exception e) {e.printStackTrace();} finally {try {((CloseableHttpClient) httpClient).close();} catch (IOException e) {e.printStackTrace();}}}
}
- DELETE 请求:
import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpDelete;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;public class DeleteRequestExample {public static void main(String[] args) {HttpClient httpClient = HttpClients.createDefault();HttpDelete httpDelete = new HttpDelete("http://example.com/api/delete");try {HttpResponse response = httpClient.execute(httpDelete);if (response.getStatusLine().getStatusCode() == 200) {String responseBody = EntityUtils.toString(response.getEntity());System.out.println("DELETE请求响应: " + responseBody);} else {System.out.println("DELETE请求失败,状态码:" + response.getStatusLine().getStatusCode());}} catch (Exception e) {e.printStackTrace();} finally {try {((CloseableHttpClient) httpClient).close();} catch (IOException e) {e.printStackTrace();}}}
}
3.4 参数传递
- GET 请求参数传递:GET 请求通过 URL 拼接参数,例如:
String baseUrl = "http://example.com/api/search";
String param1 = "value1";
String param2 = "value2";
String charset = "UTF-8";
String query = String.format("param1=%s¶m2=%s",URLEncoder.encode(param1, charset),URLEncoder.encode(param2, charset));
String completeUrl = baseUrl + "?" + query;
HttpGet httpGet = new HttpGet(completeUrl);
在这段代码中,首先定义了基础 URLbaseUrl,然后准备了两个参数param1和param2。通过URLEncoder.encode方法对参数进行 URL 编码,以确保特殊字符能够正确传输。接着,使用String.format方法将参数拼接成查询字符串query,格式为param1=value1¶m2=value2。最后,将查询字符串拼接到基础 URL 后面,形成完整的请求 URLcompleteUrl,并创建HttpGet请求对象。
- POST 请求参数传递:
-
- 通过表单传递参数:
List<NameValuePair> params = new ArrayList<>();
params.add(new BasicNameValuePair("username", "testuser"));
params.add(new BasicNameValuePair("password", "testpass"));
httpPost.setEntity(new UrlEncodedFormEntity(params));
这段代码创建了一个List对象params,用于存储表单参数。通过BasicNameValuePair类将参数名和参数值封装成键值对,然后添加到params列表中。最后,使用UrlEncodedFormEntity将参数列表转换为适合 HTTP POST 请求的实体,并设置到HttpPost请求对象中。
- 通过 JSON 传递参数:
String json = "{\"name\":\"John\",\"age\":30}";
httpPost.setEntity(new StringEntity(json));
httpPost.setHeader("Content-Type", "application/json");
这里直接定义了一个 JSON 格式的字符串json,表示请求体的数据。使用StringEntity将 JSON 字符串转换为请求实体,并设置到HttpPost请求对象中。同时,设置请求头的Content-Type为application/json,告诉服务器请求体的数据格式是 JSON。
3.5 响应处理
- 获取响应状态码:
HttpResponse response = httpClient.execute(httpGet);
int statusCode = response.getStatusLine().getStatusCode();
System.out.println("响应状态码:" + statusCode);
通过response.getStatusLine().getStatusCode()方法可以获取服务器返回的响应状态码,状态码是一个三位数,用于表示请求的处理结果。例如,200 表示请求成功,404 表示资源未找到,500 表示服务器内部错误等。
- 获取响应头:
Header[] headers = response.getAllHeaders();
for (Header header : headers) {System.out.println(header.getName() + ": " + header.getValue());
}
使用response.getAllHeaders()方法可以获取响应头的所有信息,返回一个Header数组。通过遍历这个数组,可以获取每个响应头的名称和值,并进行相应的处理。
- 获取响应体:
if (response.getStatusLine().getStatusCode() == 200) {String responseBody = EntityUtils.toString(response.getEntity());System.out.println("响应内容:" + responseBody);
}
当响应状态码为 200 时,表示请求成功,可以通过EntityUtils.toString(response.getEntity())方法获取响应体的内容。EntityUtils类是 HttpClient 提供的工具类,用于处理响应实体,将其转换为字符串形式以便于处理。
- 处理不同类型的响应:
-
- JSON 响应处理:可以使用 JSON 解析库,如 Jackson、Gson 等,将响应体的 JSON 字符串解析为 Java 对象。例如,使用 Gson 库:
import com.google.gson.Gson;if (response.getStatusLine().getStatusCode() == 200) {String responseBody = EntityUtils.toString(response.getEntity());Gson gson = new Gson();MyResponseObject obj = gson.fromJson(responseBody, MyResponseObject.class);System.out.println("解析后的对象:" + obj);
}
这里首先获取响应体的 JSON 字符串responseBody,然后创建一个Gson对象。使用gson.fromJson方法将 JSON 字符串解析为MyResponseObject类型的 Java 对象,MyResponseObject是根据响应数据结构定义的 Java 类,用于映射 JSON 数据。
- XML 响应处理:可以使用 XML 解析库,如 JAXB、DOM4J 等,将响应体的 XML 字符串解析为 Java 对象或文档对象模型(DOM)。例如,使用 JAXB 库:
import javax.xml.bind.JAXBContext;
import javax.xml.bind.Unmarshaller;if (response.getStatusLine().getStatusCode() == 200) {String responseBody = EntityUtils.toString(response.getEntity());JAXBContext jaxbContext = JAXBContext.newInstance(MyResponseXml.class);Unmarshaller jaxbUnmarshaller = jaxbContext.createUnmarshaller();MyResponseXml xmlObj = (MyResponseXml) jaxbUnmarshaller.unmarshal(new StringReader(responseBody));System.out.println("解析后的XML对象:" + xmlObj);
}
这段代码首先获取响应体的 XML 字符串responseBody,然后创建JAXBContext对象,指定要解析的 Java 类MyResponseXml。通过JAXBContext创建Unmarshaller对象,使用unmarshal方法将 XML 字符串解析为MyResponseXml类型的 Java 对象。
四、HttpClient 原理剖析
4.1 核心组件
-
HttpClient 实例:它是整个 HttpClient 框架的核心,负责与服务器进行通信。通过HttpClient实例,我们可以发送各种类型的 HTTP 请求,如 GET、POST、PUT、DELETE 等。HttpClient实例就像是一个经验丰富的探险家,能够根据我们的指示,准确地前往服务器获取或提交数据。在实际应用中,我们通常会创建一个HttpClient实例,并在多个请求中复用它,以减少资源的消耗。
-
HttpRequest:它代表一个 HTTP 请求,包括请求的方法(GET、POST 等)、URL、请求头和请求体等信息。HttpRequest就像是我们给探险家的任务清单,明确了需要访问的地址、使用的方法以及携带的参数等信息。例如,HttpGet和HttpPost都是HttpRequest的具体实现类,分别用于表示 GET 请求和 POST 请求。
-
HttpResponse:它表示 HTTP 响应,包含了服务器返回的状态码、响应头和响应体等信息。HttpResponse就像是探险家从服务器带回的 “宝藏”,我们可以从中获取服务器对请求的处理结果,如状态码 200 表示请求成功,404 表示资源未找到等。通过解析响应头和响应体,我们可以获取服务器返回的数据、处理结果以及其他相关信息。
-
HttpClient 执行器:它负责执行HttpRequest,并将服务器返回的响应封装成HttpResponse。HttpClient执行器就像是探险家的 “交通工具”,负责将请求发送到服务器,并将响应带回给我们。在执行请求的过程中,它会处理请求的发送、接收以及连接管理等工作,确保请求能够顺利完成。
4.2 请求执行流程
- 创建 HttpClient 实例:
HttpClient httpClient = HttpClients.createDefault();
这一步创建了一个默认配置的HttpClient实例,它就像是为我们的 HTTP 通信之旅准备了一艘坚固的 “船只”,具备基本的航行能力。
\2. 创建请求对象:以 GET 请求为例:
HttpGet httpGet = new HttpGet("http://example.com");
这里创建了一个HttpGet请求对象,指定了请求的 URL 为http://example.com,就像是为船只设定了航行的目的地。
\3. 执行请求:
HttpResponse response = httpClient.execute(httpGet);
通过httpClient.execute(httpGet)方法执行请求,此时HttpClient实例就像船长一样,指挥着船只朝着目的地前进,将请求发送到服务器,并等待服务器返回响应。在这个过程中,HttpClient会处理一系列的底层操作,如建立 TCP 连接、发送 HTTP 请求报文、接收 HTTP 响应报文等。
\4. 处理响应:
if (response.getStatusLine().getStatusCode() == 200) {String responseBody = EntityUtils.toString(response.getEntity());System.out.println("响应内容:" + responseBody);
} else {System.out.println("请求失败,状态码:" + response.getStatusLine().getStatusCode());
}
当接收到服务器的响应后,首先检查响应的状态码。如果状态码为 200,表示请求成功,通过EntityUtils.toString(response.getEntity())方法获取响应体的内容,并进行相应的处理,就像是打开探险家带回的宝藏,查看其中的内容;如果状态码不为 200,则表示请求失败,打印出失败的状态码,以便我们了解请求失败的原因。
4.3 连接管理
-
连接池概念和作用:连接池是一种缓存机制,它可以预先创建并管理一定数量的 HTTP 连接。当我们需要发送 HTTP 请求时,不需要每次都重新建立连接,而是从连接池中获取一个已有的连接,使用完毕后再将连接放回连接池。连接池就像是一个停车场,里面停放着许多可用的 “车辆”(连接),我们可以随时从停车场中租用车辆,使用完后再归还,这样可以避免频繁地创建和销毁连接,减少资源的开销,提高系统的性能和效率。在高并发的场景下,连接池的作用尤为明显,它可以有效地复用连接,减少连接建立的时间和资源消耗,从而提高系统的吞吐量。
-
配置和使用连接池:
PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager();
cm.setMaxTotal(200); // 设置最大连接数
cm.setDefaultMaxPerRoute(20); // 设置每个路由的最大连接数
CloseableHttpClient httpClient = HttpClients.custom().setConnectionManager(cm).build();
在这段代码中,首先创建了一个PoolingHttpClientConnectionManager对象cm,它是HttpClient连接池的管理器。通过cm.setMaxTotal(200)方法设置连接池的最大连接数为 200,这意味着连接池中最多可以同时存在 200 个连接;通过cm.setDefaultMaxPerRoute(20)方法设置每个路由的最大连接数为 20,路由可以理解为目标服务器的地址,每个目标服务器最多可以使用 20 个连接。然后,使用HttpClients.custom()创建一个HttpClient构建器,并通过.setConnectionManager(cm)将连接池管理器设置到构建器中,最后通过.build()方法构建出一个使用连接池的CloseableHttpClient实例。这样,在使用这个HttpClient实例发送请求时,就会从连接池中获取连接,实现连接的复用。
五、HttpClient 高级应用
5.1 自定义 HttpClient
在实际应用中,我们常常需要根据具体的业务需求对 HttpClient 进行个性化配置,以满足不同场景下的 HTTP 通信要求。通过 HttpClient.Builder 类,我们可以轻松实现这一目标,下面将详细介绍如何设置超时、代理、重定向策略等。
设置超时:
超时设置是非常重要的,它可以避免请求因为长时间等待而导致程序阻塞。HttpClient 支持设置多种超时时间,包括连接超时、读取超时和从连接池获取连接的超时。
import org.apache.http.client.config.RequestConfig;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;public class CustomHttpClientExample {public static void main(String[] args) {// 设置超时时间RequestConfig requestConfig = RequestConfig.custom().setConnectTimeout(5000) // 连接超时时间,5秒.setSocketTimeout(10000) // 读取超时时间,10秒.setConnectionRequestTimeout(3000) // 从连接池获取连接的超时时间,3秒.build();// 创建自定义的HttpClientCloseableHttpClient httpClient = HttpClients.custom().setDefaultRequestConfig(requestConfig).build();// 这里可以进行请求操作,例如发送GET请求// HttpGet httpGet = new HttpGet("http://example.com");// HttpResponse response = httpClient.execute(httpGet);// 处理响应等操作}
}
在上述代码中,首先通过RequestConfig.custom()创建一个RequestConfig构建器,然后使用.setConnectTimeout(5000)设置连接超时时间为 5000 毫秒,即 5 秒,表示在尝试连接到服务器时,如果超过 5 秒还未建立连接,则抛出连接超时异常;.setSocketTimeout(10000)设置读取超时时间为 10000 毫秒,即 10 秒,意味着在连接建立后,从服务器读取数据时,如果超过 10 秒还未读取到数据,则抛出读取超时异常;.setConnectionRequestTimeout(3000)设置从连接池获取连接的超时时间为 3000 毫秒,即 3 秒,当从连接池中获取连接时,如果等待时间超过 3 秒还未获取到可用连接,则抛出获取连接超时异常。最后,通过HttpClients.custom().setDefaultRequestConfig(requestConfig).build()创建一个使用自定义请求配置的CloseableHttpClient实例。
设置代理:
当我们的应用程序需要通过代理服务器访问目标服务器时,可以通过以下方式进行设置。
import org.apache.http.HttpHost;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;public class ProxyHttpClientExample {public static void main(String[] args) {// 设置代理服务器HttpHost proxy = new HttpHost("proxy.example.com", 8080, "http");// 设置请求配置,包含代理RequestConfig requestConfig = RequestConfig.custom().setProxy(proxy).build();// 创建自定义的HttpClientCloseableHttpClient httpClient = HttpClients.custom().setDefaultRequestConfig(requestConfig).build();// 这里可以进行请求操作,例如发送GET请求// HttpGet httpGet = new HttpGet("http://example.com");// HttpResponse response = httpClient.execute(httpGet);// 处理响应等操作}
}
在这段代码中,首先创建一个HttpHost对象proxy,指定代理服务器的地址为proxy.example.com,端口为 8080,协议为http。然后,在创建RequestConfig时,通过.setProxy(proxy)将代理设置到请求配置中。最后,使用包含代理配置的RequestConfig创建CloseableHttpClient实例。这样,当使用这个HttpClient发送请求时,请求会通过指定的代理服务器转发到目标服务器。
设置重定向策略:
HttpClient 默认会自动处理重定向,但有时我们可能需要自定义重定向策略,以满足特殊的业务需求。
import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.protocol.HttpClientContext;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.protocol.HttpContext;
import org.apache.http.util.EntityUtils;public class CustomRedirectStrategyExample {public static void main(String[] args) {// 创建自定义的重定向策略// 这里只是示例,实际可以根据需求实现更复杂的重定向逻辑// 例如只允许特定域名的重定向,或者根据响应头进行条件重定向等// 下面是一个简单的不允许重定向的示例// 注意,这里只是简单展示自定义重定向策略的设置方式,实际应用中可能需要更复杂的逻辑// 例如根据业务规则判断是否允许重定向,以及如何处理重定向等// 可以参考官方文档和相关资料,深入了解重定向策略的实现和应用RequestConfig requestConfig = RequestConfig.custom().setRedirectsEnabled(false).build();// 创建自定义的HttpClientCloseableHttpClient httpClient = HttpClients.custom().setDefaultRequestConfig(requestConfig).build();HttpGet httpGet = new HttpGet("http://example.com");HttpContext context = HttpClientContext.create();try {HttpResponse response = httpClient.execute(httpGet, context);if (response.getStatusLine().getStatusCode() == 200) {String responseBody = EntityUtils.toString(response.getEntity());System.out.println("响应内容:" + responseBody);} else {System.out.println("请求失败,状态码:" + response.getStatusLine().getStatusCode());}} catch (Exception e) {e.printStackTrace();} finally {try {httpClient.close();} catch (Exception e) {e.printStackTrace();}}}
}
在这个示例中,通过RequestConfig.custom().setRedirectsEnabled(false)设置不允许重定向,即当服务器返回重定向响应时,HttpClient 不会自动进行重定向操作。如果需要实现更复杂的重定向策略,可以自定义实现RedirectStrategy接口,并在创建RequestConfig时通过.setRedirectStrategy(customRedirectStrategy)进行设置。
5.2 异步请求
在某些场景下,同步请求可能会导致线程阻塞,影响程序的性能和响应速度。而异步请求则可以避免这种情况,它允许在发送请求后,程序继续执行其他任务,而无需等待服务器的响应。当服务器响应到达时,通过回调函数或其他机制来处理响应结果。这样可以大大提高程序的并发处理能力和用户体验。
下面展示如何使用 HttpClient 的sendAsync()方法发送异步请求:
import org.apache.http.HttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
import java.io.IOException;
import java.util.concurrent.CompletableFuture;public class AsyncHttpClientExample {public static void main(String[] args) {CloseableHttpClient httpClient = HttpClients.createDefault();HttpGet httpGet = new HttpGet("http://example.com");CompletableFuture<HttpResponse> future = httpClient.executeAsync(httpGet);future.thenApply(response -> {try {return EntityUtils.toString(response.getEntity());} catch (IOException e) {throw new RuntimeException(e);}}).thenAccept(System.out::println).exceptionally(Throwable::printStackTrace);}
}
在上述代码中,首先创建了一个CloseableHttpClient实例和一个HttpGet请求对象。然后,使用httpClient.executeAsync(httpGet)方法发送异步请求,该方法返回一个CompletableFuture对象future,它代表了异步操作的结果。通过future.thenApply(response -> {… })方法,对异步操作的结果(即HttpResponse)进行处理,将响应体转换为字符串。接着,使用thenAccept(System.out::println)方法,将转换后的字符串打印输出。最后,通过exceptionally(Throwable::printStackTrace)方法,处理异步操作过程中可能抛出的异常,将异常堆栈信息打印出来。这样,在发送请求后,主线程不会被阻塞,可以继续执行其他任务,当服务器响应到达时,会自动调用相应的回调函数来处理响应和异常。
5.3 与其他框架集成
以 Spring 框架为例,展示如何在 Spring 项目中集成 HttpClient,实现 HTTP 通信。在 Spring 项目中集成 HttpClient 可以充分利用 Spring 的依赖注入和配置管理功能,使代码更加简洁、可维护。
首先,在pom.xml文件中添加 HttpClient 的依赖:
<dependency><groupId>org.apache.httpcomponents</groupId><artifactId>httpclient</artifactId><version>4.5.13</version>
</dependency>
然后,创建一个服务类,用于发送 HTTP 请求:
import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
import org.springframework.stereotype.Service;@Service
public class HttpService {public String sendGetRequest(String url) {HttpClient httpClient = HttpClients.createDefault();HttpGet httpGet = new HttpGet(url);try {HttpResponse response = httpClient.execute(httpGet);if (response.getStatusLine().getStatusCode() == 200) {return EntityUtils.toString(response.getEntity());} else {return "请求失败,状态码:" + response.getStatusLine().getStatusCode();}} catch (Exception e) {e.printStackTrace();return "请求发生异常:" + e.getMessage();}}
}
在上述代码中,创建了一个HttpService服务类,并使用@Service注解将其标记为一个服务组件,以便 Spring 容器进行管理。在sendGetRequest方法中,创建了一个默认的HttpClient实例和一个HttpGet请求对象,指定请求的 URL。然后,执行请求并处理响应,如果响应状态码为 200,则返回响应体的内容;否则,返回请求失败的状态码信息。如果在请求过程中发生异常,捕获异常并返回异常信息。
最后,在控制器中调用该服务:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;@RestController
public class HttpController {@Autowiredprivate HttpService httpService;@GetMapping("/http/{url}")public String sendHttpGetRequest(@PathVariable String url) {return httpService.sendGetRequest(url);}
}
在HttpController控制器类中,使用@Autowired注解自动注入HttpService服务。通过@GetMapping(“/http/{url}”)注解定义了一个 HTTP GET 请求的映射路径,其中{url}是一个路径变量,表示要请求的 URL。在sendHttpGetRequest方法中,接收路径变量url,并调用httpService.sendGetRequest(url)方法发送 HTTP GET 请求,将请求结果返回给客户端。这样,在 Spring 项目中就实现了 HttpClient 的集成,通过控制器调用服务类中的方法,实现了 HTTP 通信功能。
六、HttpClient 常见问题及解决
6.1 缺少证书问题
在使用 HttpClient 进行 HTTPS 通信时,有时会遇到缺少证书的问题,这通常会导致sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target异常。这个异常的出现是因为 HttpClient 在验证服务器的 SSL 证书时,无法找到有效的证书路径,也就是说,它不信任服务器提供的证书。
解决这个问题可以尝试从网站下载证书,然后将其添加到项目中。具体步骤如下:
-
首先,我们需要找到一个可以下载证书的工具,例如InstallCert.java。你可以从相关的技术网站上获取这个工具,比如从https://confluence.atlassian.com/download/attachments/180292346/InstallCert.java下载。
-
下载完成后,编译InstallCert.java。假设你已经安装了 Java 开发环境,在命令行中进入到InstallCert.java所在的目录,执行javac InstallCert.java命令进行编译。
-
编译成功后,执行java InstallCert hostname,其中hostname是目标服务器的域名,比如java InstallCert www.163.com。按照提示操作,这个过程会在当前目录下生成一个名为 “ssecacerts” 的证书。
-
最后,将生成的证书拷贝到$JAVA_HOME/jre/lib/security目录下,这样 HttpClient 在进行 HTTPS 通信时就能够信任这个证书,从而解决缺少证书的问题。
6.2 上传文件问题
在实际应用中,经常会遇到需要通过 HttpClient 上传文件的场景。使用 post 请求上传文件时,我们可以借助org.apache.httpcomponents的httpmime jar 包来实现。
请求负载是文件的情形:
import org.apache.http.HttpEntity;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.mime.MultipartEntityBuilder;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;import java.io.File;public class FileUploadExample {public static void main(String[] args) {String url = "http://example.com/upload";String fileName = "test.txt";try (CloseableHttpClient httpClient = HttpClients.createDefault()) {HttpPost httpPost = new HttpPost(url);FileBody file = new FileBody(new File(fileName));HttpEntity reqEntity = MultipartEntityBuilder.create().addPart("myfile", file).build();httpPost.setEntity(reqEntity);CloseableHttpResponse response = httpClient.execute(httpPost);try {if (response.getStatusLine().getStatusCode() == 200) {System.out.println("文件上传成功");} else {System.out.println("文件上传失败,状态码:" + response.getStatusLine().getStatusCode());}} finally {response.close();}} catch (Exception e) {e.printStackTrace();}}
}
在这段代码中,我们首先创建了一个HttpPost请求对象,指定了上传的 URL。然后,创建了一个FileBody对象,它代表要上传的文件。接着,使用MultipartEntityBuilder来构建请求实体,将文件添加到请求实体中,这里的 “myfile” 是文件在服务器端接收时的参数名。最后,设置请求实体到HttpPost对象中,并执行请求。
请求负载中有字符串的情形:
import org.apache.http.HttpEntity;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.mime.MultipartEntityBuilder;
import org.apache.http.entity.mime.content.FileBody;
import org.apache.http.entity.mime.content.StringBody;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;import java.io.File;
import java.nio.charset.Charset;public class FileAndStringUploadExample {public static void main(String[] args) {String url = "http://example.com/upload";String fileName = "test.txt";String paramValue = "testParam";try (CloseableHttpClient httpClient = HttpClients.createDefault()) {HttpPost httpPost = new HttpPost(url);FileBody file = new FileBody(new File(fileName));StringBody id = new StringBody(paramValue, Charset.forName("UTF-8"));HttpEntity reqEntity = MultipartEntityBuilder.create().addPart("scheduleId", id).addPart("myfile", file).build();httpPost.setEntity(reqEntity);CloseableHttpResponse response = httpClient.execute(httpPost);try {if (response.getStatusLine().getStatusCode() == 200) {System.out.println("文件和参数上传成功");} else {System.out.println("文件和参数上传失败,状态码:" + response.getStatusLine().getStatusCode());}} finally {response.close();}} catch (Exception e) {e.printStackTrace();}}
}
在这个示例中,除了要上传的文件外,还需要传递一个字符串参数。我们创建了一个StringBody对象来表示这个参数,设置其值和字符编码。然后,将StringBody和FileBody都添加到请求实体中,“scheduleId” 是字符串参数在服务器端接收时的参数名,“myfile” 是文件参数名。这样就可以同时上传文件和字符串参数了。
6.3 POST 请求不是键值对的形式
一般情况下,post 请求的负载中常常是键值对的形式,但在某些特殊场景下,我们可能会遇到 POST 请求不是键值对的情况。比如,当我们需要发送 JSON 格式的数据或者其他自定义格式的数据时,就不能简单地按照键值对的方式来构建请求。
此时,我们可以通过设置请求实体来实现。以发送 JSON 数据为例:
import org.apache.http.HttpEntity;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;public class NonKeyValuePostExample {public static void main(String[] args) {String url = "http://example.com/api";String json = "{\"key\":\"value\",\"name\":\"John\"}";try (CloseableHttpClient httpClient = HttpClients.createDefault()) {HttpPost post = new HttpPost(url);post.setHeader("Accept", "application/json");post.setHeader("Content-Type", "application/json");post.setEntity(new StringEntity(json));CloseableHttpResponse response = httpClient.execute(post);try {if (response.getStatusLine().getStatusCode() == 200) {String responseBody = EntityUtils.toString(response.getEntity());System.out.println("响应内容:" + responseBody);} else {System.out.println("请求失败,状态码:" + response.getStatusLine().getStatusCode());}} finally {response.close();}} catch (Exception e) {e.printStackTrace();}}
}
在这段代码中,首先创建了一个HttpPost请求对象,指定请求的 URL。然后,设置请求头的Accept和Content-Type都为application/json,表示我们期望接收和发送的数据格式都是 JSON。接着,创建一个StringEntity对象,将 JSON 字符串作为参数传入,这个StringEntity就是我们的请求实体。最后,将请求实体设置到HttpPost对象中,并执行请求。通过这种方式,就可以实现发送非键值对形式的 POST 请求,满足不同的业务需求。
七、最佳实践与优化建议
7.1 性能优化
- 使用连接池:在高并发场景下,频繁创建和销毁 HTTP 连接会消耗大量的系统资源和时间。连接池可以预先创建一定数量的连接,并在需要时复用这些连接,从而减少连接建立和销毁的开销,提高系统的性能和响应速度。例如,使用PoolingHttpClientConnectionManager来创建连接池,并设置合适的最大连接数和每个路由的最大连接数:
PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager();
cm.setMaxTotal(200); // 设置最大连接数为200
cm.setDefaultMaxPerRoute(20); // 设置每个路由的默认最大连接数为20
CloseableHttpClient httpClient = HttpClients.custom().setConnectionManager(cm).build();
- 设置合理的超时时间:合理设置连接超时、读取超时和从连接池获取连接的超时时间非常重要。如果超时时间设置过长,可能会导致请求长时间等待,影响系统的响应性能;如果设置过短,可能会导致一些正常的请求因为短暂的网络延迟而失败。例如:
RequestConfig requestConfig = RequestConfig.custom().setConnectTimeout(5000) // 连接超时时间为5秒.setSocketTimeout(10000) // 读取超时时间为10秒.setConnectionRequestTimeout(3000) // 从连接池获取连接的超时时间为3秒.build();
CloseableHttpClient httpClient = HttpClients.custom().setDefaultRequestConfig(requestConfig).build();
- 优化请求参数:在发送请求时,尽量减少不必要的请求参数,避免传输大量无用的数据。同时,对于一些需要频繁发送的请求,可以考虑对请求参数进行缓存,避免重复计算和生成。例如,如果某个请求的参数在一段时间内不会发生变化,可以将这些参数缓存起来,下次请求时直接使用缓存的参数,而不需要重新计算和设置。
7.2 代码规范
- 异常处理:在使用 HttpClient 发送请求时,可能会遇到各种异常,如网络异常、连接超时、协议异常等。为了保证程序的稳定性和可靠性,需要对这些异常进行妥善处理。使用try-catch块捕获异常,并根据不同的异常类型进行相应的处理。例如:
try {HttpResponse response = httpClient.execute(httpGet);// 处理响应
} catch (IOException e) {System.err.println("请求发生I/O异常:" + e.getMessage());
} catch (Exception e) {System.err.println("请求发生其他异常:" + e.getMessage());
}
- 资源释放:在使用完 HttpClient 相关资源后,一定要及时释放,避免资源泄漏。对于CloseableHttpClient、CloseableHttpResponse等实现了Closeable接口的对象,使用try-with-resources语句或在finally块中调用close()方法来关闭资源。例如:
try (CloseableHttpClient httpClient = HttpClients.createDefault();CloseableHttpResponse response = httpClient.execute(httpGet)) {// 处理响应
} catch (IOException e) {e.printStackTrace();
}
- 代码复用:将常用的 HttpClient 操作封装成独立的方法或类,提高代码的复用性。比如,创建一个专门的 HttpUtil 类,将发送 GET 请求、POST 请求等操作封装成静态方法,在其他地方需要使用时直接调用这些方法,避免重复编写相同的代码。这样不仅可以减少代码量,还便于维护和修改。
7.3 安全注意事项
- 认证和授权:在进行 HTTP 通信时,如果涉及到敏感数据或需要保护的资源,一定要进行认证和授权。可以使用基本认证、摘要认证、OAuth 等方式来实现认证和授权。例如,使用基本认证:
CredentialsProvider credsProvider = new BasicCredentialsProvider();
credsProvider.setCredentials(new AuthScope("example.com", 80),new UsernamePasswordCredentials("username", "password"));
CloseableHttpClient httpClient = HttpClients.custom().setDefaultCredentialsProvider(credsProvider).build();
-
防止 SQL 注入:如果 HttpClient 请求的数据会用于数据库操作,一定要注意防止 SQL 注入。避免直接将用户输入的数据拼接到 SQL 语句中,而是使用参数化查询或预编译语句。例如,在 Java 中使用PreparedStatement来执行 SQL 查询,将用户输入的数据作为参数传递,而不是直接拼接到 SQL 语句中,这样可以有效防止 SQL 注入攻击。
-
防止 XSS 攻击:如果 HttpClient 请求返回的数据会在网页上展示,要注意防止跨站脚本(XSS)攻击。对返回的数据进行严格的过滤和转义,避免恶意脚本注入。可以使用一些安全的 HTML 解析库或工具,对返回的数据进行过滤和净化,确保数据在展示时不会被浏览器解析为恶意脚本。例如,使用 OWASP 的 Java Encoder 库对数据进行编码,将特殊字符进行转义,防止 XSS 攻击。
八、总结与展望
8.1 总结回顾
在本文中,我们深入探讨了 HttpClient 这一强大的 Java HTTP 客户端工具。从基础认知出发,了解到 HttpClient 是 Apache HttpComponents 项目的重要组成部分,专门用于创建 HTTP 客户端程序,它支持所有 HTTP 方法,具备自动转向、HTTPS 协议支持以及代理服务器支持等强大功能。在使用方面,我们详细学习了其环境搭建、基本使用步骤、常见请求示例、参数传递以及响应处理等内容。通过实际代码示例,我们掌握了如何创建 HttpClient 实例,发送 GET、POST、PUT、DELETE 等请求,并对服务器返回的响应进行有效的处理。
在原理剖析部分,我们深入研究了 HttpClient 的核心组件,包括 HttpClient 实例、HttpRequest、HttpResponse 以及 HttpClient 执行器,了解了它们在 HTTP 通信中的各自职责。同时,详细解析了请求执行流程,从创建 HttpClient 实例、创建请求对象、执行请求到处理响应,每一个步骤都至关重要。此外,还探讨了连接管理中的连接池概念和作用,以及如何配置和使用连接池来提高系统性能。
在高级应用方面,我们学习了如何自定义 HttpClient,包括设置超时、代理、重定向策略等,以满足不同场景下的业务需求。同时,还掌握了异步请求的使用方法,通过sendAsync()方法实现了非阻塞的 HTTP 请求,提高了程序的并发处理能力。此外,以 Spring 框架为例,展示了如何在实际项目中集成 HttpClient,实现与其他框架的协同工作。
针对常见问题,我们也进行了详细的分析和解决。在缺少证书问题上,通过从网站下载证书并添加到项目中的方法,解决了 HttpClient 在 HTTPS 通信中遇到的证书信任问题。在上传文件问题上,根据请求负载的不同情况,分别展示了如何上传文件以及同时上传文件和字符串参数的方法。对于 POST 请求不是键值对的形式,通过设置请求实体,成功实现了发送 JSON 格式数据等非键值对形式的 POST 请求。
在最佳实践与优化建议部分,我们从性能优化、代码规范和安全注意事项三个方面入手,提出了一系列的优化建议。在性能优化方面,使用连接池、设置合理的超时时间以及优化请求参数等方法,能够有效提高系统的性能和响应速度。在代码规范方面,合理的异常处理、及时的资源释放以及代码复用,能够提高代码的稳定性和可维护性。在安全注意事项方面,进行认证和授权、防止 SQL 注入以及防止 XSS 攻击等措施,能够确保 HTTP 通信的安全性。
相关文章:
从入门到精通:HttpClient深度剖析与实战指南
一、引言 1.1 背景引入 在当今数字化时代,网络编程已成为软件开发中不可或缺的一部分。而 HTTP 通信作为网络编程的核心,承担着客户端与服务器之间数据传输的重任。无论是 Web 应用、移动应用,还是分布式系统,HTTP 协议都扮演着…...

IoTDB 2025 春节值班与祝福
2025 春节快乐 瑞蛇迎吉庆,祥光映华年,2025 春节已近在眼前。社区祝福 IoTDB 的所有关注者、支持者、使用者 2025 新年快乐,“蛇”来运转! IoTDB 团队的春节放假时间为 2025 年 1 月 27 日至 2 月 4 日,1 月 25 日、26…...

Java 大视界 -- Java 大数据中的隐私增强技术全景解析(64)
💖亲爱的朋友们,热烈欢迎来到 青云交的博客!能与诸位在此相逢,我倍感荣幸。在这飞速更迭的时代,我们都渴望一方心灵净土,而 我的博客 正是这样温暖的所在。这里为你呈上趣味与实用兼具的知识,也…...

【2024年华为OD机试】 (A卷,100分)- 整理扑克牌(JavaScriptJava PythonC/C++)
一、问题描述 题目描述 给定一组数字,表示扑克牌的牌面数字,忽略扑克牌的花色,请按如下规则对这一组扑克牌进行整理: 步骤1:分组形成组合牌 炸弹:当牌面数字相同张数大于等于4时。葫芦:3张相同牌面数字 + 2张相同牌面数字,且3张牌与2张牌不相同。三张:3张相同牌面数…...
周末总结(2024/01/25)
工作 人际关系核心实践: 要学会随时回应别人的善意,执行时间控制在5分钟以内 坚持每天早会打招呼 遇到接不住的话题时拉低自己,抬高别人(无阴阳气息) 朋友圈点赞控制在5min以内,职场社交不要放在5min以外 职场的人际关系在面对利…...
Apache Flink 概述学习笔记
一、引言 在大数据处理领域,Apache Flink 是一个极具影响力的开源流批一体化计算框架,它以其独特的架构和强大的功能,为大规模数据处理提供了高效、灵活的解决方案。 二、基本概念 Flink 是什么:Flink 是一个分布式流批处理框架…...
双足机器人开源项目
双足机器人(也称为人形机器人或仿人机器人)是一个复杂的领域,涉及机械设计、电子工程、控制理论、计算机视觉等多个学科。对于想要探索或开发双足机器人的开发者来说,有许多开源项目可以提供帮助。这些项目通常包括硬件设计文件、…...
Linux 部署 Java 项目:Tomcat、Redis、MySQL 教程
在 Linux 服务器上部署 Java 项目通常需要配置应用服务器(如 Tomcat)、数据库(如 MySQL)和缓存服务器(如 Redis)。本文将详细介绍如何在 Linux 环境中部署一个 Java 项目,涵盖 Tomcat、Redis 和…...
Django 多环境配置实战指南
在现代 Web 开发中,一个项目通常需要在多个环境中运行,例如开发环境、测试环境和生产环境。每个环境的配置可能不同,比如数据库连接、调试模式、密钥等。为了确保项目在不同环境中的灵活性和安全性,我们需要合理地管理多环境配置。 本文将详细介绍如何在 Django 项目中实现…...

【C++高并发服务器WebServer】-6:信号
本文目录 信号的概念1.1 core文件1.2 kill命令1.3 alarm函数1.4 setitimer调用1.5 signal捕捉信号1.6 信号集1.7 内核实现信号捕捉的过程1.8 sigaction1.9 sigchld 信号的概念 信号是 Linux 进程间通信的最古老的方式之一,是事件发生时对进程的通知机制,…...
HBase的原理
一、什么是HBase HBase是一个分布式,版本化,面向列的数据库,依赖Hadoop和Zookeeper (1)HBase的优点 提供高可靠性、高性能、列存储、可伸缩、实时读写的数据库系统 (2) HBase 表的特性 Region包含多行 列族包含多…...

[b01lers2020]Life on Mars1
打开题目页面如下 看了旁边的链接,也没有什么注入点,是正常的科普 利用burp suite抓包,发现传参 访问一下 http://5edaec92-dd87-4fec-b0e3-501ff24d3650.node5.buuoj.cn:81/query?searchtharsis_rise 接下来进行sql注入 方法一…...

Go学习:常量
变量:程序运行期间,可以改变的量,变量声明需要使用 var 常量:程序运行期间,不可以改变的量,常量声明需要使用 const 目录 1. 常量不允许修改 2. 常量赋值不使用 : 3. 常量能够自动推导类型 1. 常量不允许…...
Python 爬虫——爬取Web页面图片
从网页页面上批量下载jpg格式图片,并按照数字递增命名保存到指定的文件夹。 Web地址:http://p.weather.com.cn/2017/06/2720826.shtml#p1 import urllib import urllib.request import re #正则表达式#解析页面 def load_page(url):requesturllib.reque…...

微信小程序1.1 微信小程序介绍
1.1 微信小程序介绍 内容提要 1.1 什么是微信小程序 1.2 微信小程序的功能 1.3 微信小程序使用场景 1.4 微信小程序能取代App吗 1.5 微信小程序的发展历程 1.6微信小程序带来的机会...
记录备战第十六届蓝桥杯的过程
1.学会了原来字符串也有比较方法,也就是字符串987 > 98 等等,可以解决拼最大数问题 题目链接:5.拼数 - 蓝桥云课 (lanqiao.cn) 2.今天又复习了一下bfs,感觉还是很不熟练,可能是那个过程我些许有点不熟悉ÿ…...

AI 编程工具—Cursor进阶使用 Rules for AI
AI 编程工具—Cursor进阶使用 Rules for AI 这里配置是给所有的会话和内嵌模式的,你可以理解为是一个全局的配置 下面的代码是之前Cursor 给我们生成的,下面我们开始配置Rules ,来让Cursor生成的代码更加符合我们的编程习惯 def quick_sort(arr):"""使用快…...

以租赁合同的例子讲清楚 开源协议原理和区别
开源协议通俗易懂的方式介绍清楚原理和区别 开源协议其实就是软件的“使用规则”,决定了别人可以如何使用、修改、分享你的代码。通俗一点说,如果你写了一段代码,开源协议就是告诉别人在什么条件下他们可以使用你的代码,以及他们可…...
mysql如何修改密码
在MySQL中修改密码可以通过多种方式完成,具体取决于你的MySQL版本和你是否有足够的权限。以下是一些常用的方法来修改MySQL用户的密码: 方法1: 使用ALTER USER命令 这是最常用的方法,适用于MySQL 5.7及以上版本。 ALTER USER usernameloca…...

解数独力扣
题目 解题思路 1.双层循环每一个位置都要去判断能不能放数字 2.每到一个位置如果为空,for循环遍历1-9,通过函数判断是否能放这个数字能放开始回溯判断放下这个数字之后 3.不设结束条件,一直循环判断下去知道所有位置全部填满数字然后retur…...

(十)学生端搭建
本次旨在将之前的已完成的部分功能进行拼装到学生端,同时完善学生端的构建。本次工作主要包括: 1.学生端整体界面布局 2.模拟考场与部分个人画像流程的串联 3.整体学生端逻辑 一、学生端 在主界面可以选择自己的用户角色 选择学生则进入学生登录界面…...
React Native 导航系统实战(React Navigation)
导航系统实战(React Navigation) React Navigation 是 React Native 应用中最常用的导航库之一,它提供了多种导航模式,如堆栈导航(Stack Navigator)、标签导航(Tab Navigator)和抽屉…...

dedecms 织梦自定义表单留言增加ajax验证码功能
增加ajax功能模块,用户不点击提交按钮,只要输入框失去焦点,就会提前提示验证码是否正确。 一,模板上增加验证码 <input name"vdcode"id"vdcode" placeholder"请输入验证码" type"text&quo…...

srs linux
下载编译运行 git clone https:///ossrs/srs.git ./configure --h265on make 编译完成后即可启动SRS # 启动 ./objs/srs -c conf/srs.conf # 查看日志 tail -n 30 -f ./objs/srs.log 开放端口 默认RTMP接收推流端口是1935,SRS管理页面端口是8080,可…...

Cloudflare 从 Nginx 到 Pingora:性能、效率与安全的全面升级
在互联网的快速发展中,高性能、高效率和高安全性的网络服务成为了各大互联网基础设施提供商的核心追求。Cloudflare 作为全球领先的互联网安全和基础设施公司,近期做出了一个重大技术决策:弃用长期使用的 Nginx,转而采用其内部开发…...

2025盘古石杯决赛【手机取证】
前言 第三届盘古石杯国际电子数据取证大赛决赛 最后一题没有解出来,实在找不到,希望有大佬教一下我。 还有就会议时间,我感觉不是图片时间,因为在电脑看到是其他时间用老会议系统开的会。 手机取证 1、分析鸿蒙手机检材&#x…...

Maven 概述、安装、配置、仓库、私服详解
目录 1、Maven 概述 1.1 Maven 的定义 1.2 Maven 解决的问题 1.3 Maven 的核心特性与优势 2、Maven 安装 2.1 下载 Maven 2.2 安装配置 Maven 2.3 测试安装 2.4 修改 Maven 本地仓库的默认路径 3、Maven 配置 3.1 配置本地仓库 3.2 配置 JDK 3.3 IDEA 配置本地 Ma…...
Angular微前端架构:Module Federation + ngx-build-plus (Webpack)
以下是一个完整的 Angular 微前端示例,其中使用的是 Module Federation 和 npx-build-plus 实现了主应用(Shell)与子应用(Remote)的集成。 🛠️ 项目结构 angular-mf/ ├── shell-app/ # 主应用&…...

【Redis】笔记|第8节|大厂高并发缓存架构实战与优化
缓存架构 代码结构 代码详情 功能点: 多级缓存,先查本地缓存,再查Redis,最后才查数据库热点数据重建逻辑使用分布式锁,二次查询更新缓存采用读写锁提升性能采用Redis的发布订阅机制通知所有实例更新本地缓存适用读多…...

【JVM面试篇】高频八股汇总——类加载和类加载器
目录 1. 讲一下类加载过程? 2. Java创建对象的过程? 3. 对象的生命周期? 4. 类加载器有哪些? 5. 双亲委派模型的作用(好处)? 6. 讲一下类的加载和双亲委派原则? 7. 双亲委派模…...