Introduction

Einstein said: All greatness comes from simple details. Netty provides us with such a powerful eventloop and channel. Through the effective use of these simple things, we can get very powerful applications, such as the proxy we will talk about today.

Proxy and reverse proxy

I believe that as long as programmers have heard of nginx server, a very important function of this super excellent nginx is to be a reverse proxy. Then some friends have to ask, if there is a reverse proxy, there must be a forward proxy, so what is the difference between the two?

Let’s talk about positive agents first. For example, traffic stars have been hit recently. Although they have been suppressed, they are celebrities and they cannot be seen by ordinary people. If someone needs to talk to a celebrity, they need to go through the celebrity’s agent first. An agent relayed the words to the celebrities. This broker is the forward agent. We access the object to be accessed through the forward proxy.

So what is a reverse proxy? For example, there are many artificial intelligences now. If we talk to intelligent robot A, then A transfers the conversation between us to the hidden person behind. This person uses his wisdom to answer our conversation and hand it over to the smart robot. Robot A outputs and finally realizes artificial intelligence. This process is called reverse proxy.

The principle of netty to achieve proxy

So how to implement this proxy server in netty?

First of all, our proxy server is a server, so we need to create a server using ServerBootstrap in netty:

EventLoopGroup bossGroup = new NioEventLoopGroup(1);
        EventLoopGroup workerGroup = new NioEventLoopGroup();
        try {
            ServerBootstrap b = new ServerBootstrap();
            b.group(bossGroup, workerGroup)
             .channel(NioServerSocketChannel.class)
             .handler(new LoggingHandler(LogLevel.INFO))
             .childHandler(new SimpleDumpProxyInitializer(REMOTE_HOST, REMOTE_PORT))
             .childOption(ChannelOption.AUTO_READ, false)
             .bind(LOCAL_PORT).sync().channel().closeFuture().sync();

In this local server, we pass in ProxyInitializer. In this handler initializer, we pass in a custom handler:

    public void initChannel(SocketChannel ch) {
        ch.pipeline().addLast(
                new LoggingHandler(LogLevel.INFO),
                new SimpleDumpProxyInboundHandler(remoteHost, remotePort));
    }

In the custom handler, we use Bootstrap to create a client to connect to the remote server to be proxied. We put the creation of the client in the channelActive method:

// 开启outbound连接
        Bootstrap b = new Bootstrap();
        b.group(inboundChannel.eventLoop())
         .channel(ctx.channel().getClass())
         .handler(new SimpleDumpProxyOutboundHandler(inboundChannel))
         .option(ChannelOption.AUTO_READ, false);
        ChannelFuture f = b.connect(remoteHost, remotePort);

Then after the client establishes a connection, you can read data from the inboundChannel:

outboundChannel = f.channel();
        f.addListener(future -> {
            if (future.isSuccess()) {
                // 连接建立完毕,读取inbound数据
                inboundChannel.read();
            } else {
                // 关闭inbound channel
                inboundChannel.close();
            }
        });

Because it is a proxy service, we need to forward the data read by inboundChannel to outboundChannel, so in channelRead we need to write:

    public void channelRead(final ChannelHandlerContext ctx, Object msg) {
        // 将inboundChannel中的消息读取,并写入到outboundChannel
        if (outboundChannel.isActive()) {
            outboundChannel.writeAndFlush(msg).addListener((ChannelFutureListener) future -> {
                if (future.isSuccess()) {
                    // flush成功,读取下一个消息
                    ctx.channel().read();
                } else {
                    future.channel().close();
                }
            });
        }
    }

After the outboundChannel is successfully written, continue reading the inboundChannel.

For the client's outboundChannel, there is also a handler. In this handler, we need to reverse the data read by the outboundChannel to the inboundChannel:

    public void channelRead(final ChannelHandlerContext ctx, Object msg) {
        // 将outboundChannel中的消息读取,并写入到inboundChannel中
        inboundChannel.writeAndFlush(msg).addListener((ChannelFutureListener) future -> {
            if (future.isSuccess()) {
                ctx.channel().read();
            } else {
                future.channel().close();
            }
        });
    }

After the inboundChannel is successfully written, the reading of the outboundChannel is continued.

Such a simple proxy server is complete.

Actual combat

What happens if we proxy the local port 8000 to port 80 of www.163.com? Run our program, visit http://localhost:8000, we will see the following page:

Why is it not displaying the normal page as we imagined? That's because the domain name after our proxy was localhost, not the normal www.163.com, so the server did not recognize our request and reported an error.

Summarize

The simple forwarding requests between the proxy servers in this article cannot handle the above scenarios, so how to solve the above problems? Stay tuned for my follow-up article!

For the examples in this article, please refer to: learn-netty4

This article has been included in http://www.flydean.com/35-netty-simple-proxy/

The most popular interpretation, the most profound dry goods, the most concise tutorial, and many tips you don't know are waiting for you to discover!

Welcome to pay attention to my official account: "programs, those things", know the technology, know you better!


flydean
890 声望437 粉丝

欢迎访问我的个人网站:www.flydean.com