ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Java HTTP客户端选型指南:从HttpURLConnection到WebClient

Java HTTP客户端选型指南:从HttpURLConnection到WebClient 干Java后端这行几乎没人敢说自己没写过HTTP请求调用。对接第三方支付接口、调云服务商OpenAPI、微服务之间互相拉数据哪一样都逃不开这事儿。但真到了要写代码的时候不少人反而会懵一下——Java生态里能发HTTP请求的玩意儿实在太多了自带的HttpURLConnection、老牌的Apache HttpClient、轻巧的OkHttp、Spring生态里的RestTemplate后面又冒出个响应式的WebClient再加上Hutool里那个一行搞定一切的HttpUtil。到底用哪个它们之间有什么区别性能差多少这篇文章就把我在实际项目里用过的几种方式挨个拆一遍从代码写法到底层原理从选型逻辑到踩坑记录一次性说透。文章适合刚毕业准备面试的Java新人也适合工作两三年想搞明白技术选型的老手更欢迎那些被线上HTTP调用坑过的同学来对号入座。1. 常见方式总览与选型思路1.1 Java世界里HTTP客户端是怎么一步步卷起来的很多人不理解为什么一种语言里会有这么多种HTTP客户端。答案很简单需求变了技术就跟着变。最早的JDK 1.1时代Java连HTTP客户端都没有想发请求得自己用Socket拼报文那叫一个原始。后来JDK 1.4推出了HttpURLConnection总算是官方给了一个能用的方案。但这个方案设计得并不顺手API啰嗦不说功能也简陋连接池更是想都别想。你去翻早期Java项目的代码几乎每个发请求的方法都要重复几十行样板代码写起来特别痛苦。Apache HttpClient就是在这样的背景下出现的。它把连接管理、重定向、Cookie、代理这些乱七八糟的细节全都封装好了还支持连接池性能一下子就上来了。2005年前后它发布1.0版本之后十几年里一直是Java后端的主流选择。直到今天你去看Maven中央仓库的下载量HttpClient仍然是Top级别的。OkHttp是Square公司2013年开源的主打一个轻巧高性能。它原生支持HTTP/2、自动gzip压缩、连接池复用API设计得也特别现代链式调用写起来非常舒服。这几年Android开发几乎被OkHttp统治Java后端项目里用它的也越来越多。RestTemplate是Spring家族推出的HTTP调用神器解决的痛点和前面几个不一样——它不是在网络层做文章而是让调用方完全不用关心HTTP细节。你传一个对象进去它自动帮你序列化成JSON发出去收到响应再自动反序列化成对象。这种贴合业务开发者的设计让它在Spring项目里迅速普及。再往后Spring WebClient横空出世基于Reactor的响应式编程模型彻底抛弃了“一个请求占一个线程”的老思路用少量线程支撑海量并发连接。加上Hutool这种极简折腾式工具类的出现Java发HTTP请求这件事已经从“能用”卷到了“怎么方便怎么来”。1.2 这么多方案到底怎么选我给的实用建议选型这事没有标准答案但我可以给你一个非常实战的思路先看你的项目用了什么框架再看你的并发量级最后看你愿不愿意多引入依赖。举几个具体场景老项目、没用什么框架的纯Servlet应用或者你就是想快速写个验证脚本那HttpURLConnection和Hutool的HttpUtil是最省心的选择零依赖或者近零依赖。Spring Boot项目那天然就推荐RestTemplate或WebClient。因为Spring Boot的自动配置里已经帮你想好了大部分事情你只需要注册一个Bean就能用。追求极致性能、项目本来就有网络中间层需求比如要加拦截器统一处理日志和鉴权那OkHttp的拦截器机制会让你用着很顺手。公司已有技术栈里如果已经用了Apache HttpClient没必要强行换它能干的事情也足够多。这里我给一个对比表方便你一眼看清每种方式的特点调用方式依赖成本连接池性能代码简洁度适用场景HttpURLConnection无无一般差零依赖环境、学习原理Apache HttpClient高有较好一般老项目、复杂连接管理OkHttp中有优秀好高性能、拦截器场景RestTemplate中Spring自带可配置一般到较好极好Spring Boot项目WebClient中Spring自带内置高并发优秀好响应式架构Hutool HttpUtil低有可配较好极高快速开发、小工具提示不要单纯为了“用最新的”而选WebClient如果你的团队没人写过响应式代码学习成本和维护成本会直接吃掉性能收益。技术选型永远要兼顾团队能力。2. 原生HttpURLConnection最朴素的方案2.1 基本用法与完整代码示例先说JDK自带的HttpURLConnection。很多工作了几年的人可能都没用过它但这东西是理解后面所有高级client的底层基础——不管HttpClient还是OkHttp它们最终都要通过Socket发起TCP连接只不过把细节封装好了而已。先看最简单的GET请求怎么写URL url new URL(https://api.example.com/users?page1size10); HttpURLConnection conn (HttpURLConnection) url.openConnection(); conn.setRequestMethod(GET); conn.setConnectTimeout(5000); conn.setReadTimeout(5000); conn.setRequestProperty(User-Agent, Mozilla/5.0); int code conn.getResponseCode(); System.out.println(HTTP状态码: code); // 响应体读取状态码400时错误信息在getErrorStream()里 InputStream is code 400 ? conn.getErrorStream() : conn.getInputStream(); try (BufferedReader reader new BufferedReader(new InputStreamReader(is, StandardCharsets.UTF_8))) { StringBuilder response new StringBuilder(); String line; while ((line reader.readLine()) ! null) { response.append(line); } System.out.println(响应内容: response); } finally { conn.disconnect(); }再来看POST JSON的写法注意几个关键点必须设置setDoOutput(true)表示这个请求要往服务端写数据必须手动设置Content-Type不然服务端收到的可能是application/x-www-form-urlencodedURL url new URL(https://api.example.com/users); HttpURLConnection conn (HttpURLConnection) url.openConnection(); conn.setRequestMethod(POST); conn.setDoOutput(true); conn.setConnectTimeout(5000); conn.setReadTimeout(5000); conn.setRequestProperty(Content-Type, application/json;charsetUTF-8); conn.setRequestProperty(Accept, application/json); String jsonBody {\name\:\张三\,\age\:25}; try (OutputStream os conn.getOutputStream()) { os.write(jsonBody.getBytes(StandardCharsets.UTF_8)); os.flush(); } int code conn.getResponseCode(); // 后续读取逻辑与GET一致...2.2 为什么原生方案在生产环境里不讨喜说实话HttpURLConnection能在生产环境里活到今天主要原因是它零依赖。除此之外它身上的毛病一抓一大把。最要命的是没有连接池。每发一次请求底层都要重新经历一次完整的TCP三次握手如果是HTTPS还得再做TLS握手。一两次无所谓一天几百万次调用的时候这些握手的时间开销和系统资源消耗是实打实的浪费。我见过一个老项目用原生方式调接口压测到每秒500的QPS就撑不住了换成带连接池的HttpClient之后同样配置直接翻倍。其次是API设计太反人类。请求头设置、连接参数配置全靠一堆setXxx方法糊上去代码逻辑一旦复杂可读性很快就崩了。而且它还有不少历史遗留的坑比如在某些JDK版本里服务端返回404或500时调用getInputStream()会直接抛FileNotFoundException你得先判断状态码再决定读哪个流不然异常处理逻辑很容易写歪。还有一个容易踩的坑是编码问题。HttpURLConnection默认使用ISO-8859-1读取响应流服务端返回UTF-8编码的中文你不手动指定UTF-8读出来的就是一堆乱码。这一点在后面讲的几个封装库里都被自动处理掉了但原生的没有。所以我的建议是学习期或者写一次性工具可以用它但正式项目里能不用尽量不用。它对理解HTTP协议调用的底层细节很有帮助但让它在生产环境扛流量纯属给自己找不痛快。3. Apache HttpClient老牌稳定派3.1 依赖引入与基础调用写法Apache HttpClient是Java生态里资历最老、使用最广泛的HTTP客户端之一。它的完整依赖长这样dependency groupIdorg.apache.httpcomponents/groupId artifactIdhttpclient/artifactId version4.5.14/version /dependency注意这是4.x版本包名是org.apache.http。如果你看到org.apache.hc开头的包名那是5.x版本API略有不同。简单GET请求CloseableHttpClient httpClient HttpClients.createDefault(); HttpGet request new HttpGet(https://api.example.com/users?page1); request.setHeader(Accept, application/json); try (CloseableHttpResponse response httpClient.execute(request)) { int statusCode response.getStatusLine().getStatusCode(); String body EntityUtils.toString(response.getEntity(), StandardCharsets.UTF_8); System.out.println(状态码: statusCode); System.out.println(响应体: body); }POST JSONCloseableHttpClient httpClient HttpClients.createDefault(); HttpPost request new HttpPost(https://api.example.com/users); request.setHeader(Content-Type, application/json;charsetUTF-8); String jsonBody {\name\:\李四\,\age\:30}; request.setEntity(new StringEntity(jsonBody, StandardCharsets.UTF_8)); try (CloseableHttpResponse response httpClient.execute(request)) { // 处理响应... }3.2 连接池配置高并发下的保命操作HttpClient最值钱的能力就是连接池。它默认使用的PoolingHttpClientConnectionManager可以把连接复用来发多个请求避免每次都重新握手。我通常都会自定义一个连接池配置而不是直接用HttpClients.createDefault()因为默认配置没有设置连接池上限也没有主动清理空闲连接跑久了很容易把资源耗光。PoolingHttpClientConnectionManager connectionManager new PoolingHttpClientConnectionManager(); connectionManager.setMaxTotal(200); // 连接池最大连接数 connectionManager.setDefaultMaxPerRoute(50); // 每个路由域名最大连接数 RequestConfig requestConfig RequestConfig.custom() .setConnectTimeout(5000) // 建立连接超时 .setConnectionRequestTimeout(3000) // 从连接池获取连接超时 .setSocketTimeout(10000) // 读取响应超时 .build(); CloseableHttpClient httpClient HttpClientBuilder.create() .setConnectionManager(connectionManager) .setDefaultRequestConfig(requestConfig) .evictExpiredConnections() // 淘汰过期连接 .evictIdleConnections(60, TimeUnit.SECONDS) // 60秒清理一次空闲连接 .build();几个参数我解释一下为什么这么设MaxTotal200这个值不是拍脑袋定的。需要根据你机器的硬件配置和下游服务的承受能力来评估一般一台4C8G的应用服务器200左右比较稳妥配大了一旦下游响应慢200个连接全占住你的线程池也会跟着被拖垮。setConnectionRequestTimeout(3000)是很多人容易忽略的点。当连接池里的连接全被占满新请求想在池子里拿连接时就会等待这个参数就是等待的最长时间。设太短高峰时期容易拿不到连接直接报错设太长接口响应时间会被无限拉长。空闲连接清理很重要。下游服务端可能会在空闲一段时间后断开连接如果客户端不知道还继续用就会撞上Connection reset异常。定时清理能规避这个坑。3.3 实际使用中的性能心得HttpClient在并发场景下的线程安全做得很好同一个CloseableHttpClient实例可以被多个线程安全共享所以你在项目里应该把它声明成单例而不是每次请求都新建一个。还有两个常见问题我得专门提一下。第一EntityUtils.toString()不能对同一个HttpEntity调用两次。原因是HttpEntity底层是个一次性消费的流读完就没了。所以你要么把body存到变量里后面所有逻辑都基于这个字符串要么就明确知道只需要用一次。第二请求重试不是默认安全的。HttpClient默认会对ConnectTimeoutException自动重试3次但对SocketTimeoutException不重试。如果你自己加了重试逻辑一定要考虑接口幂等性——发出去的POST请求如果其实已经到达服务端但因为网络问题响应丢了服务端可能已经执行了操作你再重试一次就产生了重复数据。这是所有HTTP客户端都逃不掉的问题只能说在非幂等接口上重试要极其谨慎。4. OkHttp高性能与优雅并存4.1 依赖引入与链式调用体验OkHttp是Square家的开源项目在Android圈子里是事实标准在Java后端也慢慢火起来了。它能有今天的地位靠的是两件事性能和API设计。dependency groupIdcom.squareup.okhttp3/groupId artifactIdokhttp/artifactId version4.12.0/version /dependency看一段完整的GET调用代码OkHttpClient client new OkHttpClient.Builder() .connectTimeout(5, TimeUnit.SECONDS) .readTimeout(10, TimeUnit.SECONDS) .writeTimeout(10, TimeUnit.SECONDS) .build(); Request request new Request.Builder() .url(https://api.example.com/users?page1) .header(Accept, application/json) .build(); try (Response response client.newCall(request).execute()) { if (!response.isSuccessful()) { System.err.println(请求失败: response.code()); } String body response.body().string(); System.out.println(body); }POST JSON的写法也相当干脆OkHttpClient client new OkHttpClient.Builder().build(); MediaType JSON MediaType.parse(application/json; charsetutf-8); String jsonBody {\name\:\王五\,\age\:28}; RequestBody requestBody RequestBody.create(JSON, jsonBody); Request request new Request.Builder() .url(https://api.example.com/users) .post(requestBody) .build(); try (Response response client.newCall(request).execute()) { // ... }注意try (Response response ...)的写法。OkHttp的Response是实现了Closeable接口的用try-with-resources能确保响应体被正确关闭避免连接泄漏。4.2 HTTPS证书校验与拦截器链OkHttp最打动我的两个功能一个是拦截器一个是它对连接池和HTTP/2的一等支持。先说过滤器Interceptor。你可以通过拦截器在请求发出之前、响应返回之前插入自定义逻辑。最常见的用法是统一打日志、自动附加token、全局超时控制甚至做自动重试。class LoggingInterceptor implements Interceptor { Override public Response intercept(Chain chain) throws IOException { Request original chain.request(); long startTime System.currentTimeMillis(); // 发出请求前可以修改Request Request request original.newBuilder() .header(X-Trace-Id, UUID.randomUUID().toString()) .build(); Response response chain.proceed(request); // 响应返回后可以记录耗时 long duration System.currentTimeMillis() - startTime; System.out.println(请求URL: request.url() , 耗时: duration ms, 状态: response.code()); return response; } } OkHttpClient client new OkHttpClient.Builder() .addInterceptor(new LoggingInterceptor()) .build();这种链路式的处理模型比HttpClient的HttpRequestInterceptor直观得多你可以在任意环节做拦截而且不会和业务代码纠缠在一起。OkHttp的连接池是内置的不需要像HttpClient那样手动创建ConnectionPool。默认配置是每个目标主机保留5个空闲连接空闲超过5分钟会被回收。如果你觉得5个不够可以自己调OkHttpClient client new OkHttpClient.Builder() .connectionPool(new ConnectionPool(10, 10, TimeUnit.MINUTES)) .build();关于证书校验OkHttp默认信任系统内置的CA证书。如果你要调的是自签名证书的测试环境就不能直接信任了需要自定义TrustManager。但这个过程容易踩坑我自己倾向于只在测试环境用而且会把校验逻辑写得非常明确避免误把生产环境的校验也关掉。4.3 同步与异步调用的正确姿势OkHttp支持execute()同步和enqueue()异步两种模式。异步调用基于它的内置线程池不需要你自己维护线程client.newCall(request).enqueue(new Callback() { Override public void onFailure(Call call, IOException e) { e.printStackTrace(); } Override public void onResponse(Call call, Response response) throws IOException { try (ResponseBody responseBody response.body()) { String body responseBody.string(); // 注意这里已经在OkHttp的子线程中了不能直接更新UI或操作非线程安全的对象 } } });一个小提醒onResponse回调里拿到的是子线程如果你用的是Spring这种容器管理线程的框架需要自己做好线程切换。我之前见过有人在回调里直接调MyBatis的Mapper结果因为它用的是非Spring管理的线程拿到的东西完全不对排查了半天才发现是线程上下文丢失的问题。5. Spring生态下的RestTemplate与WebClient5.1 RestTemplateSpring项目里最直接的HTTP调用方式如果你的项目是Spring Boot那RestTemplate是你最不需要费脑子就能上手的方案。它把HTTP调用的复杂度降到了最低你甚至不需要了解底层是怎么发请求的只要传URL和参数类型就能拿到反序列化后的对象。先把Bean定义出来Configuration public class RestTemplateConfig { Bean public RestTemplate restTemplate() { return new RestTemplateBuilder() .setConnectTimeout(Duration.ofSeconds(5)) .setReadTimeout(Duration.ofSeconds(10)) .build(); } }然后在业务代码里直接注入使用Service public class UserService { Autowired private RestTemplate restTemplate; // GET请求并反序列化成对象 public User getUser(Long id) { String url https://api.example.com/users/{id}; return restTemplate.getForObject(url, User.class, id); } // POST请求把对象序列化成JSON发送 public User createUser(User user) { String url https://api.example.com/users; return restTemplate.postForObject(url, user, User.class); } }这里的{id}是占位符RestTemplate会自动做URL模板替换和参数绑定你完全不用自己拼字符串。这种体验对业务开发来说是真的舒服。5.2 给RestTemplate换底层的坑与收益很多人不知道RestTemplate本身并不是一个HTTP客户端它只是一个门面。真正发请求的是底层封装的那个ClientHttpRequestFactory。默认情况下RestTemplate用的是SimpleClientHttpRequestFactory底层走的就是JDK自带的HttpURLConnection。这就意味着默认的RestTemplate没有连接池。高并发场景下性能会明显拉胯和你直接用HttpURLConnection没什么两样。解决办法是让RestTemplate底层用Apache HttpClient或者OkHttp。方法很简单先确保项目里有对应依赖然后在配置类里指定工厂Configuration public class RestTemplateConfig { Bean public RestTemplate restTemplate() { // 使用Apache HttpClient作为底层实现 HttpClient httpClient HttpClientBuilder.create() .setMaxConnTotal(200) .setMaxConnPerRoute(50) .build(); HttpComponentsClientHttpRequestFactory factory new HttpComponentsClientHttpRequestFactory(httpClient); factory.setConnectTimeout(5000); factory.setConnectionRequestTimeout(3000); factory.setReadTimeout(10000); return new RestTemplate(factory); } }这个配置一出RestTemplate就从“能用”变成了“耐操”。我在实际项目里做过压测同样的接口默认RestTemplate QPS大概能到800换底成HttpClient之后能跑到2000多翻了一倍还多。如果你看到线上接口用RestTemplate调服务老超时第一个就该查是不是没用连接池。5.3 WebClient从阻塞到响应式的转型最后提一下WebClient。它是Spring WebFlux推出的响应式HTTP客户端和RestTemplate的最大区别是RestTemplate是同步阻塞的发一个请求当前线程就等着WebClient是异步非阻塞的发完请求不阻塞线程响应回来了再回调。对于一个HTTP调用来说最明显的差异是性能模型。同步阻塞模型下每来一个请求就要占用一个线程线程会一直卡在读响应上。响应式模型则让少量线程就能支撑大量并发连接线程不再被闲置占用。基础写法Service public class UserService { private final WebClient webClient; public UserService(WebClient.Builder webClientBuilder) { this.webClient webClientBuilder.baseUrl(https://api.example.com).build(); } // 异步非阻塞 public MonoUser getUser(Long id) { return webClient.get() .uri(/users/{id}, id) .retrieve() .bodyToMono(User.class); } }如果你是在一个传统的Spring MVC项目里强行用WebClient要注意它默认是异步的你需要通过.block()来拿到结果但这样会让响应式带来的并发优势大打折扣。所以我个人的评价是全异步架构直接用WebClient传统同步架构还是优先RestTemplate 连接池。6. 老牌与新生代的补充Hutool与JDK自带的另一面6.1 Hutool HttpUtil五分钟上手的极简方案Hutool是一个Java工具类库其中封装好的HttpUtil让HTTP请求调用变得异常简单。它最大的价值是不需要你刻意去管理连接不需要写一堆样板代码三五行就能搞定一个请求。dependency groupIdcn.hutool/groupId artifactIdhutool-all/artifactId version5.8.25/version /dependencyGET请求String result HttpUtil.get(https://api.example.com/users?page1); System.out.println(result);POST JSONString jsonBody {\name\:\赵六\,\age\:22}; String result HttpUtil.post(https://api.example.com/users, jsonBody);带请求头、带表单参数的写法HttpResponse response HttpRequest.post(https://api.example.com/users) .header(Authorization, Bearer eyJhbGciOi...) .form(name, 张三) .form(age, 25) .execute(); int status response.getStatus(); String body response.body();Hutool底层默认使用HttpURLConnection但你可以通过配置切换到OkHttp或者Apache HttpClient。在快速原型开发、写数据同步脚本、做个简单的爬虫工具时它是效率最高的选择。不过你要是搞大型高并发系统我还是建议走正规的HttpClient或OkHttp方案毕竟Hutool封装的灵活性和底层调优空间有限。6.2 Java 11的HttpClient新选择最后顺便说一嘴其实从JDK 11开始官方也推出了一套现代化的HttpClient API包名是java.net.http。它支持HTTP/2和异步调用看起来是想摘掉“原生不好用”这顶帽子。基本的GET请求长这样HttpClient client HttpClient.newBuilder() .connectTimeout(Duration.ofSeconds(5)) .build(); HttpRequest request HttpRequest.newBuilder() .uri(URI.create(https://api.example.com/users)) .header(Accept, application/json) .GET() .build(); HttpResponseString response client.send(request, HttpResponse.BodyHandlers.ofString()); System.out.println(response.statusCode()); System.out.println(response.body());如果你用的是Java 17并且不想引入任何第三方依赖这个也是一个值得考虑的方案。它的性能和设计都还不错只是生态积累上还比不上OkHttp和HttpClient。7. 常见问题与故障排查实录7.1 连接超时与读超时的正确配置方式HTTP调用的超时和线程池、连接池的超时不是一回事这里最容易犯的错是把“连接超时”和“读超时”当成同一个概念。连接超时ConnectTimeout从发起TCP连接到建立连接的最长等待时间。网络不通、IP错误、防火墙拦截时这个值会生效。读取超时ReadTimeout每次从响应流里读取数据时等待数据到达的最长时间。服务端处理慢、网络抖动时这个值会生效。我见过最经典的线上事故就是把ReadTimeout设成30秒结果下游接口数据库锁表每个请求都卡住30秒才返回几十个线程全部打满新请求全部排队。当你的服务端接口需要调外部服务时超时时间应该小于你的线程池监控告警时间这样出了问题你能尽快感知而不是把故障拖成灾难。常见的合理配置是内网调用ConnectTimeout 3-5秒、ReadTimeout 5-10秒跨公网调用可以适当放宽但也不要超过15秒。如果超过这个阈值还没返回大概率不是网络问题而是下游服务本身有问题。7.2 中文乱码、编码问题的排查思路HTTP请求中文乱码90%的原因是在写响应体或读响应体时没指定UTF-8。排查流程很简单先在代码里看有没有设置请求头Content-Type的charset再检查读取响应时有没有指定StandardCharsets.UTF_8最后用Postman之类工具直接调一次对比返回的编码格式是否和代码设置一致。如果是HttpClient读响应体尤其要注意String body EntityUtils.toString(response.getEntity(), StandardCharsets.UTF_8);不要用这个重载String body EntityUtils.toString(response.getEntity()); // 默认按ISO-8859-1这一行代码的差距就是“接口返回中文全部正常”和“接口返回中文全部乱码”的差距。7.3 连接泄漏为什么接口跑着跑着就假死了连接泄漏是HTTP客户端使用中最隐蔽也最致命的问题。症状是接口刚开始一切正常跑一段时间后突然全部卡死重启后又恢复过段时间又卡死。原因通常是响应体没有被关闭。不管是HttpClient的CloseableHttpResponse还是OkHttp的Response底层都持有一个Socket连接。你不关闭响应体连接就永远回不到连接池里。当所有连接都被“借走”不归还后面来的请求拿不到连接只能无限等待。我自己吃过一次大亏。当时用OkHttp调第三方接口响应拿到后直接通过response.body().string()取了字符串然后自以为已经消费完了但实际上Response对象没有关闭。压测到第20分钟连接池被耗尽线上所有调用卡死。后来排查时把代码改成try-with-resources才彻底解决。7.4 TLS/SSL握手失败的处理经验自签名证书、私有CA签发的证书都会让Java客户端在TLS握手阶段抛出SSLHandshakeException或CertPathValidatorException。这个问题的标准做法是在客户端构建一个SSLContext把信任的证书加进去而不是一关了之。一个比较稳妥的参考实现只建议测试环境用TrustManager[] trustAllCerts new TrustManager[]{ new X509TrustManager() { public X509Certificate[] getAcceptedIssuers() { return new X509Certificate[0]; } public void checkClientTrusted(X509Certificate[] certs, String authType) {} public void checkServerTrusted(X509Certificate[] certs, String authType) {} } }; SSLContext sslContext SSLContext.getInstance(TLS); sslContext.init(null, trustAllCerts, new SecureRandom());生产环境请一定用正经的CA证书不要图省事。7.5 代理环境下调外部接口的隐藏问题在公司内网开发时访问外部API经常要走代理。如果你用HttpURLConnection或HttpClient没设置代理就会直接超时。而更隐蔽的是代理设置偶尔会“误伤”内网调用——你设置了全局代理结果连内网地址也走了代理导致请求变慢甚至失败。HttpClient设置代理的办法是在构建客户端时指定HttpHostHttpHost proxy new HttpHost(proxy.company.com, 8080); CloseableHttpClient httpClient HttpClientBuilder.create() .setProxy(proxy) .build();OkHttp也支持OkHttpClient client new OkHttpClient.Builder() .proxy(new Proxy(Proxy.Type.HTTP, new InetSocketAddress(proxy.company.com, 8080))) .build();我的经验是把代理配置做成可配置项至少用环境变量或配置文件区分内外网不然每次上线前忘记改代理配置线上就会出一轮莫名其妙的网络超时。8. 最后想说的几句实在话我在项目里最常用的组合是Spring Boot项目用RestTemplate配HttpClient连接池项目里有大量小请求且要加拦截器的场景用OkHttp写一次性工具或测试脚本用Hutool。至于HttpURLConnection和WebClient前者是我面试聊底层原理时的必讲内容后者则是我评估团队是否有响应式诉求时的首选。工程的本质是在约束条件下做权衡。HTTP客户端的选择也一样——没有最好的只有最适合当前场景的。这篇文章除了让你知道这几种方式怎么写、怎么选更希望你把底层那些连接、超时、编码、重试的机制搞清楚这样遇到线上故障时你就能很快定位到问题而不是对着日志发呆。技术这东西见多识广永远比只会一招来得踏实。
返回列表