前言
大家好,我是老馬。很高興遇到你。
我們為 java 開發者實現了 java 版本的 nginx
https://github.com/houbb/nginx4j
如果你想知道 servlet 如何處理的,可以參考我的另一個專案:
手寫從零實現簡易版 tomcat minicat
手寫 nginx 系列
如果你對 nginx 原理感興趣,可以閱讀:
從零手寫實現 nginx-01-為什麼不能有 java 版本的 nginx?
從零手寫實現 nginx-02-nginx 的核心能力
從零手寫實現 nginx-03-nginx 基於 Netty 實現
從零手寫實現 nginx-04-基於 netty http 出入參最佳化處理
從零手寫實現 nginx-05-MIME型別(Multipurpose Internet Mail Extensions,多用途網際網路郵件擴充套件型別)
從零手寫實現 nginx-06-資料夾自動索引
從零手寫實現 nginx-07-大檔案下載
從零手寫實現 nginx-08-範圍查詢
從零手寫實現 nginx-09-檔案壓縮
從零手寫實現 nginx-10-sendfile 零複製
從零手寫實現 nginx-11-file+range 合併
從零手寫實現 nginx-12-keep-alive 連線複用
從零手寫實現 nginx-13-nginx.conf 配置檔案介紹
從零手寫實現 nginx-14-nginx.conf 和 hocon 格式有關係嗎?
從零手寫實現 nginx-15-nginx.conf 如何透過 java 解析處理?
從零手寫實現 nginx-16-nginx 支援配置多個 server
從零手寫實現 nginx-17-nginx 預設配置最佳化
從零手寫實現 nginx-18-nginx 請求頭+響應頭操作
從零手寫實現 nginx-19-nginx cors
從零手寫實現 nginx-20-nginx 佔位符 placeholder
從零手寫實現 nginx-21-nginx modules 模組資訊概覽
從零手寫實現 nginx-22-nginx modules 分模組載入最佳化
從零手寫實現 nginx-23-nginx cookie 的操作處理
從零手寫實現 nginx-24-nginx IF 指令
從零手寫實現 nginx-25-nginx map 指令
從零手寫實現 nginx-26-nginx rewrite 指令
從零手寫實現 nginx-27-nginx return 指令
從零手寫實現 nginx-28-nginx error_pages 指令
從零手寫實現 nginx-29-nginx try_files 指令
從零手寫實現 nginx-30-nginx proxy_pass upstream 指令
從零手寫實現 nginx-31-nginx load-balance 負載均衡
從零手寫實現 nginx-32-nginx load-balance 演算法 java 實現
從零手寫實現 nginx-33-nginx http proxy_pass 測試驗證
從零手寫實現 nginx-34-proxy_pass 配置載入處理
從零手寫實現 nginx-35-proxy_pass netty 如何實現?
netty 如何實現反向代理?
整體思路
-
根據原始的 request 請求,構建新的請求物件 forwardedRequest
-
根據指定的路由策略,獲取一個目標伺服器。
-
根據目標伺服器的 host+port,用 netty 直接模擬 http 客戶端,直接訪問遠端服務端,然後把遠端的響應寫回到當前的客戶端 resp
實現程式碼
核心實現如下:
/**
* netty 實現反向代理
*
* @since 0.27.0
* @author 老馬嘯西風
*/
public class NginxRequestDispatchProxyPass extends AbstractNginxRequestDispatch {
private static final Log logger = LogFactory.getLog(NginxRequestDispatchProxyPass.class);
@Override
public void doDispatch(NginxRequestDispatchContext context) {
// 原始的請求
final FullHttpRequest request = context.getRequest();
final ChannelHandlerContext ctx = context.getCtx();
// 建立一個新的 FullHttpRequest 轉發到目標伺服器
FullHttpRequest forwardedRequest = new DefaultFullHttpRequest(
request.protocolVersion(), request.method(), request.uri(), request.content().retainedDuplicate());
forwardedRequest.headers().set(request.headers());
final NginxLoadBalanceConfig nginxLoadBalanceConfig = context.getBalanceConfig();
// 建立一個新的 Bootstrap 進行 HTTP 請求
Bootstrap b = new Bootstrap();
b.group(ctx.channel().eventLoop())
.channel(NioSocketChannel.class)
.handler(new ChannelInitializer<Channel>() {
@Override
protected void initChannel(Channel ch) throws Exception {
//...
ch.pipeline().addLast(new SimpleChannelInboundHandler<FullHttpResponse>() {
@Override
protected void channelRead0(ChannelHandlerContext clientCtx, FullHttpResponse response) throws Exception {
// 將目標伺服器的響應寫回到客戶端
FullHttpResponse clientResponse = new DefaultFullHttpResponse(
response.protocolVersion(), response.status(), response.content().retainedDuplicate());
clientResponse.headers().set(response.headers());
ctx.writeAndFlush(clientResponse).addListener(ChannelFutureListener.CLOSE);
clientCtx.close();
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
logger.error("exceptionCaught meet ex", cause);
ctx.close();
}
});
}
});
// 連線到目標伺服器併傳送請求
final IServer server = getActualServer(nginxLoadBalanceConfig);
b.connect(server.host(), server.port()).addListener((ChannelFutureListener) future -> {
if (future.isSuccess()) {
future.channel().writeAndFlush(forwardedRequest);
} else {
ctx.close();
}
});
}
}
負載均衡
負載均衡策略,可以看我以前的文章:
從零手寫實現 nginx-32-load balance 負載均衡演算法 java 實現
小結
到這裡開始,我們基本實現了反向代理。
當然,其中還有很多細節需要處理。