简体   繁体   English

使用Netty的异步HTTP客户端

[英]Asynchronous HTTP client with Netty

I'm new to netty and still strugling to find my way. 我是netty的新手,仍然喜欢找我的路。 I'm looking to create an http client that works asynchronously. 我正在寻找创建一个异步工作的http客户端。 The netty examples of http only show how to wait for IO operations, and not how to use addListener , and so I've been trying to figure this out for the last few days. http的netty示例仅显示如何等待IO操作,而不是如何使用addListener ,因此我一直试图在过去几天解决这个问题。

I'm trying to create a request class that will handle all of the different states of a request, from connecting, sending the data, handling the response and then closing of the connection. 我正在尝试创建一个请求类,它将处理请求的所有不同状态,包括连接,发送数据,处理响应,然后关闭连接。 In order to do that my class extends SimpleChannelUpstreamHandler and implements ChannelFutureListener . 为了做到这一点,我的类扩展了SimpleChannelUpstreamHandler并实现了ChannelFutureListener I use a ChannelPipelineFactory which adds the (this) instance the class (as a SimpleChannelUpstreamHandler ) to the pipeline as a handler. 我使用ChannelPipelineFactory将类(作为SimpleChannelUpstreamHandler )的(this)实例作为处理程序添加到管道。

The connection is created like this: 连接创建如下:

this.state = State.Connecting;
this.clientBootstrap.connect(this.address).addListener(this);

Then the operationComplete method: 然后是operationComplete方法:

@Override
public void operationComplete(ChannelFuture future) throws Exception {
    State oldState = this.state;

    if (!future.isSuccess()) {
        this.status = Status.Failed;
        future.getChannel().disconnect().addListener(this);
    }
    else if (future.isCancelled()) {
        this.status = Status.Canceled;
        future.getChannel().disconnect().addListener(this);
    }
    else switch (this.state) {
        case Connecting:
            this.state = State.Sending;
            Channel channel = future.getChannel();
            channel.write(this.createRequest()).addListener(this);
            break;

        case Sending:
            this.state = State.Disconnecting;
            future.getChannel().disconnect().addListener(this);
            break;

        case Disconnecting:
            this.state = State.Closing;
            future.getChannel().close().addListener(this);
            break;

        case Closing:
            this.state = State.Finished;
            break;
    }
    System.out.println("request operationComplete start state: " + oldState + ", end state: " + this.state + ", status: " + this.status);
}

private HttpRequest createRequest() {
    String url = this.url.toString();

    HttpRequest request = new DefaultHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, url);
    request.setHeader(HttpHeaders.Names.HOST, this.url.getHost());
    request.setHeader(HttpHeaders.Names.CONNECTION, HttpHeaders.Values.CLOSE);
    request.setHeader(HttpHeaders.Names.ACCEPT_ENCODING, HttpHeaders.Values.GZIP);

    return request;
}

The class also overrides the messageReceived method: 该类还会覆盖messageReceived方法:

@Override
public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) throws Exception {
    System.out.println("messageReceived");
    HttpResponse response = (HttpResponse) e.getMessage();

    ChannelBuffer content = response.getContent();
    if (content.readable()) {
        System.out.println("CONTENT: " + content.toString(CharsetUtil.UTF_8));
    }
}

The problem is that I get this output: 问题是我得到了这个输出:

request operationComplete start state: Connecting, end state: Sending, status: Unknown
request operationComplete start state: Sending, end state: Disconnecting, status: Unknown
request operationComplete start state: Closing, end state: Finished, status: Unknown
request operationComplete start state: Disconnecting, end state: Finished, status: Unknown

As you can see the messageReceived of the is not being executed for some reason, even though the pipeline factory adds the instance of this class to the pipeline. 正如您所看到的,由于某种原因,即使管道工厂将此类的实例添加到管道中,因此仍未执行messageReceived

Any ideas what I'm missing here? 我在这里缺少什么想法? Thanks. 谢谢。


Edit 编辑

I managed to finally get this working thanks to the help of @JestanNirojan, in case someone will be interested in the solution: 在@JestanNirojan的帮助下,我终于得到了这个工作,以防有人对该解决方案感兴趣:

public class ClientRequest extends SimpleChannelUpstreamHandler {

    ....

    public void connect() {
        this.state = State.Connecting;
        System.out.println(this.state);
        this.clientBootstrap.connect(this.address);
    }

    @Override
    public void channelConnected(ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception {
        this.state = State.Sending;
        System.out.println(this.state);
        ctx.getChannel().write(this.createRequest());
    }

    @Override
    public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) throws Exception {
        HttpResponse response = (HttpResponse) e.getMessage();

        ChannelBuffer content = response.getContent();
        if (content.readable()) {
            System.out.println("CONTENT: " + content.toString(CharsetUtil.UTF_8));
        }

        this.state = State.Disconnecting;
        System.out.println(this.state);
    }

    @Override
    public void channelDisconnected(ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception {
        this.state = State.Closing;
        System.out.println(this.state);
    }

    @Override
    public void channelClosed(ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception {
        this.state = State.Finished;
        System.out.println(this.state);
    }

    private HttpRequest createRequest() {
        String url = this.url.toString();

        HttpRequest request = new DefaultHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, url);
        request.setHeader(HttpHeaders.Names.HOST, this.url.getHost());
        request.setHeader(HttpHeaders.Names.CONNECTION, HttpHeaders.Values.CLOSE);
        request.setHeader(HttpHeaders.Names.ACCEPT_ENCODING, HttpHeaders.Values.GZIP);

        return request;
    }
}

You are using a ChannelFutureListener to do all operations in the channel (which is bad), and the future listener will be executed right after calling those channel operations. 您正在使用ChannelFutureListener来执行通道中的所有操作(这是错误的),并且将在调用这些通道操作后立即执行将来的侦听器。

The problem is, After sending the message, channel is disconnected immediately and the handler can not receive the response message which comes later. 问题是,在发送消息后,通道立即断开,处理程序无法接收稍后出现的响应消息。

        ........
    case Sending:
        this.state = State.Disconnecting;
        future.getChannel().disconnect().addListener(this);
        break;
        ........

you should not block the channel future thread at all. 你不应该阻止频道未来的线程。 The best approach is extend the SimpleChannelUpstreamHandler's 最好的方法是扩展SimpleChannelUpstreamHandler

    channelConnected(..) {} 
    messageReceived(..) {} 
    channelDisconnected(..) {} 

methods and react to those events. 方法并对这些事件做出反应。 you can keep the state in that handler too. 你也可以把状态保持在那个处理程序中。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM