爱因斯坦说:所有的伟大都来源于简单的细节。Netty为我们提供了如此强大的事件循环和通道。通过有效利用这些简单的东西,我们可以获得非常强大的应用程序,比如今天的代理。
代理和反向代理相信只要程序员听说过nginx服务器,这个超级优秀的nginx最重要的功能之一就是充当反向代理。然后有朋友问。有反向剂,就一定有正向剂。那么它们之间有什么区别呢?
先说正面代理。比如最近流量明星受到重创。虽然被打压过,但明星就是明星,一般人是看不到的。如果有人需要和明星说话,需要先经过明星的经纪人,会有经纪人把话转达给他们。这个经纪人是一个积极的代理人。我们通过转发代理访问被访问的对象。
那么什么是反向代理呢?比如现在有很多人工智能。如果我们和智能机器人A对话,然后A把我们之间的对话传递给我们背后隐藏的人,这个人用他的智慧回答我们的对话,交给智能机器人A输出,最终实现人工智能。这个过程称为反向代理。
netty实现代理的原理那么如何在netty中实现这个代理服务器呢?
我们的第一个首选代理服务器是一个服务器,因此我们需要使用ServerBootstrap在netty中创建一个服务器:
EventLoopGroup boss group = new NioEventLoopGroup(1); EventLoopGroup worker group = new NioEventLoopGroup(); try { server bootstrap b = new server bootstrap(); b.group(bossGroup,workerGroup) 。通道(nioserversocketchannel . class) 。处理程序(新的LoggingHandler(LogLevel。INFO)) 。child handler(new SimpleDumpProxyInitializer(REMOTE _ HOST,REMOTE_PORT)) 。childOption(ChannelOption。AUTO_READ,false) 。bind(LOCAL_PORT)。同步()。频道()。closeFuture()。sync(); 在这个本地服务器中,我们传入ProxyInitializer。在这个处理程序初始化器中,我们传入一个自定义处理程序:
public void init channel(socket channel ch){ ch . pipeline()。add last( new logging handler(log level。INFO), new SimpleDumpProxyInboundHandler(remote host,remote port)); } 在用户定义的处理程序中,我们使用Bootstrap创建一个连接到远程服务器的客户端作为代理,并将该客户端的创建放在channelActive方法中:
//打开出站连接 Bootstrap b = new Bootstrap(); b . group(inbound channel . event loop()) 。频道(ctx.channel()。getClass()) 。handler(new SimpleDumpProxyOutboundHandler(inboundChannel)) 。选项(ChannelOption。AUTO_READ,false); channel future f = b . connect(remote host,remote port); 然后,在客户端建立连接之后,您可以从inboundChannel中读取数据:
outbound channel = f . channel(); f . addlistener(future-& gt;{ if(未来。is success()){ //连接建立后,读取入站数据 inbound channel . read(); } else { //关闭入站通道 Inbound channel . Close(); } }); 因为是代理服务,所以需要将inboundChannel读取的数据转发给outboundChannel,所以我们需要在channelRead中写这个:
公共通道读取(最终通道处理程序上下文CTX,对象消息){ //读取inboundChannel中的消息,并写入出站通道 if(outbound channel . is active()){ outbound channel . writeandflush(msg)。addlistener((channel future listen)future-->;{ if(未来。issuccess()){ //刷新成功,读取下一条消息 ctx.channel()。read(); } else { future.channel()。close(); } }); } } 成功写入outboundChannel后,继续读取inboundChannel。
同样,对于客户端的outboundChannel,也有一个处理程序。在这个处理程序中,我们需要将outboundChannel读取的数据写回inboundChannel:
公共通道read(最终通道处理程序上下文CTX,对象消息){ //读取outboundChannel中的消息,并将其写入inboundChannel 入站通道。writeandflush (msg)。addlistener((通道未来监听器)future-->:{ if(future . is success()){ CTX . channel()。read(); } else { future.channel()。close(); } }); } 成功写入inboundChannel后,继续读取outboundChannel。
这样简单的代理服务器就完成了。
实战如果我们将本地8000端口代理到www.163.com的80端口,会发生什么情况?运行我们的程序并访问http://localhost:8000,我们将看到以下页面:
为什么没有像我们预期的那样显示正常页面?那是因为我们代理后的域名以前是localhost,不是正常的www.163.com,所以服务器端不知道我们的请求,报错了。
总结本文中代理服务器之间简单的转发请求无法处理上述场景,那么如何解决上述问题呢?敬请期待我的后续文章!
这篇文章的例子可以参考:learn-netty4。
本文已被http://www.flydean.com/35-netty-simple-proxy/.收录
最通俗的解读,最深刻的干货,最简洁的教程,还有很多你不知道的小技巧等着你去发现!
欢迎关注我的微信官方账号:“程序那些事”,懂技术更懂你!