Copyright ? 2015 深圳市鑫惠廣網絡科技有限公司 粵ICP備2023111395號
愛因斯坦說過:所有的偉大,都產生于簡單的細節中。netty為我們提供了如此強大的eventloop、channel通過對這些簡單東西的有效利用,可以得到非常強大的應用程序,比如今天要講的代理。
相信只要是程序員應該都聽過nginx服務器了,這個超級優秀nginx一個很重要的功能就是做反向代理。那么有小伙伴要問了,有反向代理肯定就有正向代理,那么他們兩個有什么區別呢?
先講一下正向代理,舉個例子,最近流量明星備受打擊,雖然被打壓,但是明星就是明星,一般人是見不到的,如果有人需要跟明星對話的話,需要首先經過明星的經紀人,有經紀人將話轉達給明星。這個經紀人就是正向代理。我們通過正向代理來訪問要訪問的對象。
那么什么是反向代理呢?比如現在出現了很多人工智能,假如我們跟智能機器人A對話,然后A把我們之間的對話轉給了后面的藏著的人,這個人用他的智慧,回答了我們的對話,交由智能機器人A輸出,最終實現了人工智能。這個過程就叫做反向代理。
那么在netty中怎么實現這個代理服務器呢?
首選我們首先代理服務器是一個服務器,所以我們需要在netty中使用ServerBootstrap創建一個服務器:
EventLoopGroup bossGroup = newNioEventLoopGroup(1); EventLoopGroup workerGroup = newNioEventLoopGroup(); try{ ServerBootstrap b = newServerBootstrap(); b.group(bossGroup, workerGroup) .channel(NioServerSocketChannel.class).handler(newLoggingHandler(LogLevel.INFO)) .childHandler(newSimpleDumpProxyInitializer(REMOTE_HOST, REMOTE_PORT)) .childOption(ChannelOption.AUTO_READ, false) .bind(LOCAL_PORT).sync().channel().closeFuture().sync();
在這個local服務器中,我們傳入ProxyInitializer。在這個handler初始化器中,我們傳入自定義的handler:
publicvoidinitChannel(SocketChannel ch){ ch.pipeline().addLast( newLoggingHandler(LogLevel.INFO), newSimpleDumpProxyInboundHandler(remoteHost, remotePort)); }
在自定義的handler中,我們使用Bootstrap創建一個client,用來連接遠程要代理的服務器,我們將這個client端的創建放在channelActive方法中:
// 開啟outbound連接Bootstrap b = newBootstrap(); b.group(inboundChannel.eventLoop()) .channel(ctx.channel().getClass()) .handler(newSimpleDumpProxyOutboundHandler(inboundChannel)) .option(ChannelOption.AUTO_READ, false); ChannelFuture f = b.connect(remoteHost, remotePort);
然后在client建立好連接之后,就可以從inboundChannel中讀取數據了:
outboundChannel = f.channel(); f.addListener(future-> { if(future.isSuccess()) { // 連接建立完畢,讀取inbound數據inboundChannel.read(); } else{ // 關閉inbound channelinboundChannel.close(); } });
因為是代理服務,所以需要將inboundChannel讀取的數據,轉發給outboundChannel,所以在channelRead中我們需要這樣寫:
publicvoidchannelRead(finalChannelHandlerContext ctx, Object msg){ // 將inboundChannel中的消息讀取,并寫入到outboundChannelif(outboundChannel.isActive()) { outboundChannel.writeAndFlush(msg).addListener((ChannelFutureListener) future-> { if(future.isSuccess()) { // flush成功,讀取下一個消息ctx.channel().read(); } else{ future.channel().close(); } }); } }
當outboundChannel寫成功之后,再繼續inboundChannel的讀取工作。
同樣對于client的outboundChannel來說,也有一個handler,在這個handler中,我們需要將outboundChannel讀取到的數據反寫會inboundChannel中:
publicvoidchannelRead(finalChannelHandlerContext ctx, Object msg){ // 將outboundChannel中的消息讀取,并寫入到inboundChannel中inboundChannel.writeAndFlush(msg).addListener((ChannelFutureListener) future-> { if(future.isSuccess()) { ctx.channel().read(); } else{ future.channel().close(); } }); }
當inboundChannel寫成功之后,再繼續outboundChannel的讀取工作。
如此一個簡單的代理服務器就完成了。
如果我們將本地的8000端口,代理到www.163.com的80端口,會發生什么情況呢?運行我們的程序,訪問http://localhost:8000, 我們會看到下面的頁面:
為什么沒有如我們想象的那樣展示正常的頁面呢?那是因為我們代理過去之后的域名是localhost,而不是正常的www.163.com, 所以服務器端不認識我們的請求,從而報錯。
本文的代理服務器之間簡單的轉發請求,并不能夠處理上述的場景,那么該怎么解決上面的問題呢? 敬請期待我的后續文章!
Copyright ? 2015 深圳市鑫惠廣網絡科技有限公司 粵ICP備2023111395號