使用 Netty 和 NIO 实现高并发 HTTP

发布于 2024-10-26 12:47:46 字数 848 浏览 5 评论 0原文

我正在研究 示例 Netty HTTP 客户端代码,以便在并发、线程环境中发出 http 请求。

然而,我的系统在吞吐量相当低的情况下完全崩溃了(有很多例外)。

几乎用伪代码:

ClientBootstrap bootstrap = new ClientBootstrap(new NioClientSocketChannelFactory()) 
bootstrap.setPipelineFactory(new HttpClientPipelineFactory());

ChannelFuture future = bootstrap.connect(new InetSocketAddress(host, port));
Channel channel = future.awaitUninterruptibly().getChannel();

HttpRequest request = new DefaultHttpRequest();
channel.write(request);

在示例中,为了发出请求,我创建了一个 ClientBootstrap,并从那里(通过几个圈)创建一个 Channel 来编写 HTTPRequest。

这一切都有效并且很好。

然而,在并发情况下,每个请求都应该经历相同的循环吗?我认为这就是目前对我来说最糟糕的事情。我应该重用连接还是以完全不同的方式构建我的客户端?

另外:我正在 Clojure 中执行此操作,如果这有任何区别的话。

I am working through the example Netty HTTP Client code in order to make http requests within a concurrent, threaded environment.

However, my system breaks completely (with a slew of exceptions) at fairly low throughput.

In almost pseudo-code:

ClientBootstrap bootstrap = new ClientBootstrap(new NioClientSocketChannelFactory()) 
bootstrap.setPipelineFactory(new HttpClientPipelineFactory());

ChannelFuture future = bootstrap.connect(new InetSocketAddress(host, port));
Channel channel = future.awaitUninterruptibly().getChannel();

HttpRequest request = new DefaultHttpRequest();
channel.write(request);

In the example, to make a request I create a ClientBootstrap, and from there (through a few hoops) a Channel to write the HTTPRequest.

This all works and is good.

However, in a concurrent situation, should every request be going through the same hoops? I think that is what's breaking things for me at the moment. Should I be reusing the connection or structuring my client in an entirely different way?

Also: I am doing this in Clojure, if that makes any difference at all.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

画▽骨i 2024-11-02 12:47:46

不,你做的事情是对的。但是,您必须保留对 Channel 实例的引用。一旦您拥有该通道,只要它处于打开状态,您就不需要创建另一个引导程序。 (如果这就是你正在做的事情。)

这是我在最近的项目中使用的:

class ClientConnection (构造函数)

// Configure the client.
bootstrap = new ClientBootstrap(
    new NioClientSocketChannelFactory(
        Executors.newCachedThreadPool(),
        Executors.newCachedThreadPool()
    )
);

// Set up the pipeline factory.
bootstrap.setPipelineFactory(
    new ChannelPipelineFactory() {
        public ChannelPipeline getPipeline() throws Exception {
            return Channels.pipeline(
                // put your handlers here
            );
        }
    }
);

class ClientConnection.connect(String host, int port)

if (isConnected()) {
    throw new IllegalStateException("already connected");
}

// Start the connection attempt.
ChannelFuture future = bootstrap.connect(new InetSocketAddress(host, port));

channel = future.awaitUninterruptibly().getChannel();

// Wait until the connection is closed or the connection attempt fails.
channel.getCloseFuture().addListener(new ChannelFutureListener() {
    @Override
    public void operationComplete(ChannelFuture future) throws Exception {
        new Thread(new Runnable() {
            public void run() {
                // Shut down thread pools to exit
                // (cannot be executed in the same thread pool!
                bootstrap.releaseExternalResources();

                LOG.log(Level.INFO, "Shutting down");
            }
        }).start();
    }
});

所以,基本上,我只保留对 bootstrapchannel 的引用,但是前者在这些代码行之外几乎不使用。

注意:当应用程序退出时,您应该只执行一次bootstrap.releaseExternalResources();。就我而言,客户端发送一些文件,然后关闭通道并退出。

一旦您拥有一个已连接的 Channel 实例,您只需使用该实例即可,直到您再次将其关闭为止。关闭后,您可以调用 bootstrap 来再次创建新的 Channel

就我个人而言,我觉得 Netty 一开始有点难以理解,但是一旦你掌握了它的工作原理,它就是 Java 中最好的 NIO 框架。国际海事组织。

No, you're doing things right. You must, however, keep a reference to your Channel instance. Once you have that channel, as long as it is open, you don't need to create another bootstrap. (If that's what you're doing.)

This is what I used in a recent project :

class ClientConnection (constructor)

// Configure the client.
bootstrap = new ClientBootstrap(
    new NioClientSocketChannelFactory(
        Executors.newCachedThreadPool(),
        Executors.newCachedThreadPool()
    )
);

// Set up the pipeline factory.
bootstrap.setPipelineFactory(
    new ChannelPipelineFactory() {
        public ChannelPipeline getPipeline() throws Exception {
            return Channels.pipeline(
                // put your handlers here
            );
        }
    }
);

class ClientConnection.connect(String host, int port)

if (isConnected()) {
    throw new IllegalStateException("already connected");
}

// Start the connection attempt.
ChannelFuture future = bootstrap.connect(new InetSocketAddress(host, port));

channel = future.awaitUninterruptibly().getChannel();

// Wait until the connection is closed or the connection attempt fails.
channel.getCloseFuture().addListener(new ChannelFutureListener() {
    @Override
    public void operationComplete(ChannelFuture future) throws Exception {
        new Thread(new Runnable() {
            public void run() {
                // Shut down thread pools to exit
                // (cannot be executed in the same thread pool!
                bootstrap.releaseExternalResources();

                LOG.log(Level.INFO, "Shutting down");
            }
        }).start();
    }
});

So, basically, I only keep a reference to bootstrap and channel, however the former is pretty much not used outside of these lines of code.

Note: you should only execute bootstrap.releaseExternalResources(); once, when the application is exiting. In my case, the client sends some files then close the channel and exit.

Once you have a connected Channel instance, you need only to use that one until you close it again. Once it is closed, you can recall the bootstrap to create a new Channel again.

Personally, I find Netty a little bit hard to understand at first, but once you grasp how it works, it is simply the best NIO framework in Java. IMO.

绝影如岚 2024-11-02 12:47:46

Netty 是在 JVM 中编写高并发 HTTP 服务的最佳方法,但它非常复杂且难以直接互操作,特别是在使用 Clojure 时。看一下 Donkey,它提供与 Vert.x 的互操作,Vert.x 使用 Netty 作为后端。这种分层抽象了许多细微的低级细节,如果处理不当,可能会导致服务不稳定。 Donkey 相对较新,因此还没有很多关于它的文档。查看这个博客,其中包含一个实现基本新闻提要的开源项目Clojure 中使用 Donkey 的微服务。它详细介绍了架构、设计、编码和负载下的性能。

Netty is the best approach to writing highly concurrent HTTP services in the JVM but it is very complicated and hard to interop with directly, especially if using Clojure. Take a look at Donkey which provides interop with Vert.x which uses Netty as the backend. This layering abstracts away a lot of the nuanced low level details that, if done wrong, can result in an unstable service. Donkey is relatively new so there isn't a lot of documentation about it yet. Check out this blog that features an open source project which implements a rudimentary news feed microservice in Clojure using Donkey. It goes into details on architecture, design, coding, and performance under load.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文