【Java从入门到入土】50:网络编程:从Socket到HTTP客户端
【Java从入门到入土】50网络编程从Socket到HTTP客户端你写的Java应用是不是经常需要调用别人的接口或者要对外提供API网络编程是后端开发的必修课。从最底层的Socket到封装完善的HTTP客户端Java生态里有一整套解决方案。今天我们就从Socket开始一路走到HTTP客户端最后聊聊RESTful API和第三方接口调用的最佳实践。 Socket编程手撸一个简单的TCP服务Socket是网络编程的基石它封装了TCP/IP协议。虽然日常开发很少直接操作Socket但理解它有助于你理解网络通信的本质。服务端Serverimportjava.io.*;importjava.net.*;publicclassSimpleServer{publicstaticvoidmain(String[]args)throwsIOException{// 创建一个ServerSocket监听8080端口try(ServerSocketserverSocketnewServerSocket(8080)){System.out.println(服务器启动等待连接...);// 接受客户端连接阻塞SocketsocketserverSocket.accept();System.out.println(客户端已连接socket.getInetAddress());// 获取输入流读取客户端消息BufferedReaderinnewBufferedReader(newInputStreamReader(socket.getInputStream()));// 获取输出流发送消息给客户端PrintWriteroutnewPrintWriter(socket.getOutputStream(),true);Stringline;while((linein.readLine())!null){System.out.println(收到line);out.println(服务器已收到line);if(bye.equals(line))break;}socket.close();System.out.println(连接关闭);}}}客户端Clientimportjava.io.*;importjava.net.*;publicclassSimpleClient{publicstaticvoidmain(String[]args)throwsIOException{try(SocketsocketnewSocket(localhost,8080);BufferedReaderinnewBufferedReader(newInputStreamReader(socket.getInputStream()));PrintWriteroutnewPrintWriter(socket.getOutputStream(),true);BufferedReaderconsolenewBufferedReader(newInputStreamReader(System.in))){StringuserInput;while((userInputconsole.readLine())!null){out.println(userInput);Stringresponsein.readLine();System.out.println(服务器响应response);if(bye.equals(userInput))break;}}}}运行服务端再运行客户端就可以聊天了。这虽然简单但展示了TCP通信的核心Socket连接、输入输出流、阻塞等待。注意生产环境不会这么写因为一个连接只能服务一个客户端而且没有线程池。但原理是一样的。 HTTP协议基础你每天都在用的协议HTTP是建立在TCP之上的应用层协议。一个HTTP请求包括请求行方法GET/POST、URL、版本请求头Host、User-Agent、Content-Type等空行请求体POST才有响应类似状态行版本、状态码、短语、响应头、空行、响应体。常见状态码200 OK301/302 重定向400 客户端错误404 Not Found500 服务器内部错误你如果抓包看过HTTP报文会发现和上面的Socket例子很像——其实就是文本协议。 HTTP客户端进化史从古董到现代Java里调用HTTP接口的方式五花八门我们看看它们的演变。1. HttpURLConnectionJDK 1.1 时代URLurlnewURL(http://api.example.com/users);HttpURLConnectionconn(HttpURLConnection)url.openConnection();conn.setRequestMethod(GET);conn.setConnectTimeout(5000);conn.setReadTimeout(5000);intresponseCodeconn.getResponseCode();if(responseCode200){BufferedReaderinnewBufferedReader(newInputStreamReader(conn.getInputStream()));StringinputLine;StringBuildercontentnewStringBuilder();while((inputLinein.readLine())!null){content.append(inputLine);}in.close();System.out.println(content.toString());}else{System.out.println(请求失败responseCode);}conn.disconnect();优点JDK自带无需依赖。缺点API臃肿需要手动处理流连接管理麻烦对HTTPS支持不友好。2. Apache HttpClient第三方的王者Apache HttpClient功能强大支持连接池、重试、各种认证。dependencygroupIdorg.apache.httpcomponents/groupIdartifactIdhttpclient/artifactIdversion4.5.14/version/dependencyCloseableHttpClienthttpClientHttpClients.createDefault();HttpGetrequestnewHttpGet(http://api.example.com/users);request.addHeader(Accept,application/json);try(CloseableHttpResponseresponsehttpClient.execute(request)){intstatusCoderesponse.getStatusLine().getStatusCode();if(statusCode200){StringresultEntityUtils.toString(response.getEntity());System.out.println(result);}}catch(IOExceptione){e.printStackTrace();}优点功能全面配置灵活。缺点需要引入外部依赖API稍显复杂。3. Java 11 HttpClient官方新秀JDK 11开始Java引入了全新的java.net.http.HttpClient支持HTTP/2和WebSocketAPI更现代化。importjava.net.URI;importjava.net.http.HttpClient;importjava.net.http.HttpRequest;importjava.net.http.HttpResponse;HttpClientclientHttpClient.newHttpClient();HttpRequestrequestHttpRequest.newBuilder().uri(URI.create(http://api.example.com/users)).header(Accept,application/json).GET().build();HttpResponseStringresponseclient.send(request,HttpResponse.BodyHandlers.ofString());if(response.statusCode()200){System.out.println(response.body());}优点官方内置API简洁支持异步。缺点JDK 11才能用生态工具不如Apache丰富。4. Spring WebClient响应式编程如果你用Spring BootWebClient是推荐的非阻塞HTTP客户端。WebClientclientWebClient.create(http://api.example.com);Stringresultclient.get().uri(/users).retrieve().bodyToMono(String.class).block();// 阻塞获取也可以用subscribe异步System.out.println(result);优点响应式、非阻塞、链式调用与Spring生态集成好。缺点需要Spring WebFlux学习曲线略高。选型建议新项目JDK 11简单用官方HttpClient。需要连接池、重试、复杂配置用Apache HttpClient或OkHttp。Spring项目追求响应式用WebClient。️ RESTful API设计原则调用别人接口之前先得懂怎么设计自己的接口。RESTful是目前主流的API设计风格核心原则资源通过URI标识如/users/123表示ID为123的用户。HTTP方法表示操作GET获取资源POST创建资源PUT全量更新PATCH部分更新DELETE删除状态码反映结果200成功201创建成功400参数错误401未认证403禁止访问404资源不存在500服务器错误无状态每个请求独立服务器不保存客户端状态。返回JSON格式也可支持XML。反例GET /getUser?id123、POST /deleteUser应该用DELETE。 实战调用第三方API的最佳实践假设我们要调用一个天气API需要考虑什么1. 封装客户端类ComponentpublicclassWeatherClient{privatefinalHttpClienthttpClient;privatefinalStringapiKey;privatefinalStringbaseUrl;publicWeatherClient(Value(${weather.api.key})StringapiKey,Value(${weather.api.url})StringbaseUrl){this.httpClientHttpClient.newBuilder().connectTimeout(Duration.ofSeconds(5)).build();this.apiKeyapiKey;this.baseUrlbaseUrl;}publicWeatherResponsegetWeather(Stringcity)throwsIOException,InterruptedException{HttpRequestrequestHttpRequest.newBuilder().uri(URI.create(baseUrl/weather?qcityappidapiKey)).timeout(Duration.ofSeconds(10)).GET().build();HttpResponseStringresponsehttpClient.send(request,HttpResponse.BodyHandlers.ofString());if(response.statusCode()200){// 假设用Jackson解析JSONObjectMappermappernewObjectMapper();returnmapper.readValue(response.body(),WeatherResponse.class);}else{thrownewRuntimeException(API调用失败状态码response.statusCode());}}}2. 配置超时和重试超时一定要设避免线程被卡死。重试要小心幂等性GET可以重试POST需要判断。可以用Spring Retry或手动实现Retryable(value{IOException.class},maxAttempts3,backoffBackoff(delay1000))publicWeatherResponsegetWeather(Stringcity){...}3. 处理异常和降级调用第三方接口可能失败要有降级方案try{returnweatherClient.getWeather(city);}catch(Exceptione){log.error(天气API调用失败,e);returngetDefaultWeather();// 返回兜底数据}4. 连接池管理如果用Apache HttpClient或OkHttp一定要配置连接池避免频繁创建连接。PoolingHttpClientConnectionManagercmnewPoolingHttpClientConnectionManager();cm.setMaxTotal(200);// 最大连接数cm.setDefaultMaxPerRoute(20);// 每个路由最大连接CloseableHttpClienthttpClientHttpClients.custom().setConnectionManager(cm).build();5. 日志记录记录请求URL、耗时、响应码方便排查问题。可以用过滤器或AOP。6. 异步调用如果不需要立即返回结果可以用异步CompletableFutureWeatherResponsefutureCompletableFuture.supplyAsync(()-weatherClient.getWeather(city)); 总结技术适用场景优点缺点Socket自定义协议、底层通信灵活需要处理细节太多HttpURLConnection简单请求JDK自带无依赖API难用Apache HttpClient复杂场景生产首选功能强大依赖外部Java 11 HttpClient新项目JDK11官方简洁生态略弱Spring WebClientSpring生态响应式非阻塞学习成本网络编程的核心是理解协议和选择合适的工具。下次你要调用第三方API别再傻傻地用HttpURLConnection手写流解析了——封装好的客户端加上超时、重试、降级才是生产级代码。