Java 誕生於網絡時代,其網絡能力始終伴隨著架構模式的變遷:
| 階段 | 架構模式 | Java 角色 | 核心技術棧 |
|---|---|---|---|
| 1.0 時代 (90s 末) | C/S (Client/Server) 胖客戶端 | Applet/Swing 客戶端 + Socket 直連數據庫/應用服務器 | java.net.Socket, RMI, JDBC, Swing/AWT |
| J2EE 時代 (00s 初) | B/S (Browser/Server) 三層架構 | Servlet/JSP 生成 HTML,EJB 承載業務邏輯,瀏覽器瘦客戶端 | Servlet API, EJB, JSP, JMS, JNDI |
| Spring 時代 (00s 中-10s) | 輕量級容器 + SOA | POJO 開發,依賴注入,AOP,聲明式事務,集成各類中間件 | Spring MVC, Hibernate/MyBatis, Dubbo, ZooKeeper |
| 雲原生時代 (10s 末-今) | 微服務 / Serverless / Service Mesh | 容器化部署,配置中心,網關路由,熔斷限流,可觀測性,gRPC/REST 通信 | Spring Boot/Cloud, Kubernetes, Istio, gRPC, Netty, GraalVM |
無論架構如何變遷,java.net 及其擴展包始終提供基礎原語:
InetAddress, InetSocketAddress, URL, URI (JDK 20+ 更完善)Socket (TCP), ServerSocket, DatagramSocket (UDP), SocketChannel (NIO)URLStreamHandlerFactory, ContentHandlerFactory 實現協議擴展 (如 jar:, jrt:)SSLSocket, SSLServerSocket, SSLContext, KeyManager/TrustManager (JSSE)原文給出的「文本反轉服務器」展示了基礎交互模型。現代化改造要點:
// 長度前綴 + JSON 體 (4 字節大端長度 + UTF-8 JSON)
public class FrameCodec {
public static void write(OutputStream out, String json) throws IOException {
byte[] body = json.getBytes(StandardCharsets.UTF_8);
ByteBuffer buf = ByteBuffer.allocate(4 + body.length);
buf.putInt(body.length).put(body);
out.write(buf.array());
out.flush();
}
public static String read(InputStream in) throws IOException {
byte[] lenBytes = new byte[4];
readFully(in, lenBytes);
int len = ByteBuffer.wrap(lenBytes).getInt();
if (len > 1024*1024) throw new IOException("Frame too large");
byte[] body = new byte[len];
readFully(in, body);
return new String(body, StandardCharsets.UTF_8);
}
private static void readFully(InputStream in, byte[] buf) throws IOException {
int off = 0;
while (off < buf.length) {
int r = in.read(buf, off, buf.length - off);
if (r == -1) throw new EOFException();
off += r;
}
}
}
public class ModernServer {
private final ExecutorService workerPool = new ThreadPoolExecutor(
4, 200, 60L, TimeUnit.SECONDS, new LinkedBlockingQueue<>(10000),
new ThreadFactoryBuilder().setNameFormat("socket-worker-%d").build(),
new ThreadPoolExecutor.AbortPolicy() // 拒絕策略:拋異常觸發熔斷
);
private final ServerSocket serverSocket;
public ModernServer(int port) throws IOException {
this.serverSocket = new ServerSocket(port);
this.serverSocket.setReuseAddress(true);
this.serverSocket.setSoTimeout(1000); // accept 超時,定期檢查關閉標誌
}
public void start() {
new Thread(this::acceptLoop, "acceptor").start();
}
private void acceptLoop() {
while (!Thread.currentThread().isInterrupted()) {
try {
Socket client = serverSocket.accept();
client.setSoTimeout(30000); // 讀超時
client.setTcpNoDelay(true);
workerPool.submit(() -> handleClient(client));
} catch (SocketTimeoutException ignored) { /* 定期檢查中斷 */ }
catch (IOException e) { if (!serverSocket.isClosed()) log.error("Accept error", e); }
}
}
private void handleClient(Socket socket) {
try (socket;
InputStream in = socket.getInputStream();
OutputStream out = socket.getOutputStream()) {
while (true) {
String req = FrameCodec.read(in);
String resp = processRequest(req); // 業務邏輯
FrameCodec.write(out, resp);
}
} catch (IOException e) {
log.debug("Client disconnected: {}", e.getMessage());
}
}
private String processRequest(String req) {
// 解析 JSON, 路由到 Service Bean, 返回響應
return "{\"result\":\"reversed: " + new StringBuilder(req).reverse() + "\"}";
}
public void stop() throws IOException {
serverSocket.close();
workerPool.shutdown();
workerPool.awaitTermination(30, TimeUnit.SECONDS);
}
}
public class PooledClient {
private final BlockingQueue pool = new LinkedBlockingQueue<>();
private final String host; private final int port;
private final int maxSize = 50;
public PooledClient(String host, int port) { this.host = host; this.port = port; }
private Socket createSocket() throws IOException {
Socket s = new Socket();
s.connect(new InetSocketAddress(host, port), 3000);
s.setSoTimeout(5000);
s.setTcpNoDelay(true);
return s;
}
public String sendRequest(String request) {
Socket socket = null;
try {
socket = pool.poll(100, TimeUnit.MILLISECONDS);
if (socket == null || socket.isClosed() || !socket.isConnected()) {
socket = createSocket();
}
FrameCodec.write(socket.getOutputStream(), request);
return FrameCodec.read(socket.getInputStream());
} catch (Exception e) {
closeQuietly(socket);
socket = null;
throw new RuntimeException("Request failed", e);
} finally {
if (socket != null && !socket.isClosed() && pool.size() < maxSize) {
pool.offer(socket); // 歸還池
} else {
closeQuietly(socket);
}
}
}
}
| 維度 | 手寫 Socket 時代 | 微服務框架時代 |
|---|---|---|
| 協議定義 | 隱性、文檔、口頭傳承 | 顯性 IDL (Protobuf/Avro/Thrift/OpenAPI),代碼生成,契約測試 |
| 服務發現 | 硬編碼 IP:Port / 配置文件 | 註冊中心 (Nacos/Eureka/Consul) + 客戶端負載均衡 |
| 通信韌性 | 手寫重試/超時/連接池 | 框架內建 (Resilience4j/Sentinel/Envoy):熔斷、限流、重試、超時、隔離 |
| 可觀測性 | 日誌打印、抓包分析 | 分布式追蹤 (SkyWalking/Zipkin/Jaeger)、指標 (Prometheus)、日誌聚合 (ELK/Loki) |
| 安全 | 自定義握手/加密 | mTLS (Service Mesh/OAuth2/JWT),零信任網絡 |
| 部署單元 | Fat JAR / WAR | Container Image / JLink Runtime / GraalVM Native Image |
grpc-java (Netty 底層)。Spring RestClient / OpenFeign / Java 11 HttpClient。網關:Spring Cloud Gateway / Kong / Envoy。Spring Cloud Stream / @RabbitListener。Socket/HttpClient 為 **Anti-Corruption Layer (防腐層)**,隔離協議差異,內部暴露領域模型接口。Java 的網絡編程能力從 Socket 原語出發,經歷了 RMI、Hessian、Dubbo、gRPC、Service Mesh 的層層封裝與抽象。今日架構師面臨的不是「如何寫 Socket」,而是**在何處劃定邊界、選擇何種協議、如何治理通信韌性**。理解 Socket 的三次握手、滑動窗口、Nagle 算法、TIME_WAIT、NIO 事件循環,是為了在框架洩漏抽象時能精準診斷;拥抱高層框架,是為了將有限認知帶寬投入到業務創新與系統穩定性建設中。**網絡是分布式系統的血管,協議是血液,框架是心臟——守護好它們的健康運行,才是 Java 工程師在 Client/Server 演進長河中的核心使命。**