使用 Netty 写一个 HTTP Server

本文详细介绍了如何使用Netty构建一个高并发的HTTPServer,包括定义线程组、初始化服务、处理HTTP请求等关键步骤,适用于需要处理大量并发连接的场景。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

前一段时间需要写一个 HTTP Server,之前用 Akka 实现了一个,但是 Netty 更能支撑高并发,连 Spark 2.0 也把 Akka 换成了 Netty 进行网络传输,所以现在再撸一个 Netty 的 Server。

闲话少说,直接开搞,首先将 Netty 加入 sbt:

"io.netty" % "netty-all" % "4.1.25.Final"

我们需要定义两个组 bossGroupworkerGroup,这个两个组实际上是两个线程组,负责的不同。bossGroup 负责获取客户端连接,接收到之后会将该连接转发到 workerGroup 进行处理。

// if there are multiple `ServerBootstrap`, we should set more than one thread
val bossGroup = new NioEventLoopGroup(1)
val workerGroup = new NioEventLoopGroup()

然后通过一个启动服务类 ServerBootstrap 进行初始化启动:

val http = new ServerBootstrap()
  .group(bossGroup, workerGroup)
  .channel(classOf[NioServerSocketChannel])
  .childHandler(new HttpServerInitializer(writer))

其中 HttpServerInitializer 是我们自定义实现的一个服务初始化器,继承了 ChannelInitializer 类,这个类通过 ChannelPipieline 设置初始化链,包括超时时间、编解码和处理 HTTP 请求的设置。

class HttpServerInitializer(private val writer: EventWriter) extends ChannelInitializer[SocketChannel] {
  private val logger = Logger(this.getClass)

  override def initChannel(ch: SocketChannel) {
    logger.debug("Initialize http server channel.")
    ch.pipeline.addLast(
      new ReadTimeoutHandler(1),
      new HttpRequestDecoder(8192, 8192, 8192),
      new HttpResponseEncoder(),
      new HttpServerHandler(writer)
    )
  }
}

这里的 HttpServerHandler 就是主要的 HTTP 请求处理类,类似于 Spring 中的拦截器,包含处理请求、转发的相关功能:

class HttpServerHandler(private val writer: EventWriter)
  extends SimpleChannelInboundHandler[Object] {
  private val logger = Logger(this.getClass)

  private def forwardMessage(req: HttpRequest) {
    if (req.decoderResult.isSuccess) {
      val uri = req.uri
      logger.debug(s"Forward message, url=$uri")
      writer.putEvent(System.currentTimeMillis, uri)
    } else {
      logger.error(s"Request decode failure, result=${req.decoderResult}")
    }
  }

  override def channelReadComplete(ctx: ChannelHandlerContext) {
    ctx.flush
  }

  override def channelRead0(ctx: ChannelHandlerContext, msg: Object) {
    if (msg.isInstanceOf[HttpRequest]) {
      logger.debug("Flush and finish requests.")
      val response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.NO_CONTENT, Unpooled.EMPTY_BUFFER)
      ctx.writeAndFlush(response).addListener(ChannelFutureListener.CLOSE)
      forwardMessage(msg.asInstanceOf[HttpRequest])
    }
  }

  override def exceptionCaught(ctx: ChannelHandlerContext, error: Throwable) {
    logger.error(s"Http handle error, error=$error")
    ctx.close
  }

}

注意这里是通过 channelRead0 处理请求并给客户端返回,我在这里的处理是不论什么内容进来,都给客户端返回一个 NO_CONTENT,而实际的处理会进行转发,这样是为了防止阻塞,首先给客户端返回结果,然后具体的一些耗时操作放在 forwardMessage 内。

上面的内容就是一些初始化启动类,准备好这些以后我们需要把初始化服务器绑定到服务端口,这个端口用来接收 HTTP 请求:

val f = http.bind(port).sync().channel()
logger.info(s"Start server, port=$port")
f.closeFuture().sync()

如果接收 HTTP 请求的时候出错,我们还需要释放资源:

bossGroup.shutdownGracefully()
workerGroup.shutdownGracefully()

以上完整实现了一个 HTTP Server,具体的过程是:

  1. 定义两个线程组: bossGroupworkerGroup
  2. 准备一个继承自 ChannelInitializer 服务处理初始化类,这个类设置超时编解码以及具体处理 HTTP 请求的类
  3. 定义一个服务启动类 ServerBootstrap,设置线程组和 handler
  4. 把服务启动类绑定到某个端口号,HTTP Server 启动

完整代码见 producer,我是通过 Netty 的 HTTP Server 接收 GET 请求,处理之后将消息发送到 AWS Kinesis(类似于 Kafka)中。

好的,下面是一个简单的使用Netty的服务器: ```java import io.netty.bootstrap.ServerBootstrap; import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelInitializer; import io.netty.channel.ChannelOption; import io.netty.channel.EventLoopGroup; import io.netty.channel.nio.NioEventLoopGroup; import io.netty.channel.socket.SocketChannel; import io.netty.channel.socket.nio.NioServerSocketChannel; public class NettyServer { private final int port; public NettyServer(int port) { this.port = port; } public void run() throws Exception { EventLoopGroup bossGroup = new NioEventLoopGroup(); EventLoopGroup workerGroup = new NioEventLoopGroup(); try { ServerBootstrap b = new ServerBootstrap(); b.group(bossGroup, workerGroup) .channel(NioServerSocketChannel.class) .childHandler(new ChannelInitializer<SocketChannel>() { @Override public void initChannel(SocketChannel ch) throws Exception { ch.pipeline().addLast(new NettyServerHandler()); } }) .option(ChannelOption.SO_BACKLOG, 128) .childOption(ChannelOption.SO_KEEPALIVE, true); ChannelFuture f = b.bind(port).sync(); f.channel().closeFuture().sync(); } finally { workerGroup.shutdownGracefully(); bossGroup.shutdownGracefully(); } } public static void main(String[] args) throws Exception { int port = 8080; new NettyServer(port).run(); } } ``` 在这个例子中,我们使用了NioEventLoopGroup来处理事件,包括接收连接、读取数据和发送数据。ServerBootstrap是一个用于启动服务器的帮助类。我们使用NioServerSocketChannel作为服务器的通道实现,它允许我们使用NIO来接收新连接。我们还设置了一些选项,如SO_BACKLOG和SO_KEEPALIVE,用于配置服务器。最后,我们将服务器绑定到指定的端口上,并等待直到服务器关闭。在这个例子中,我们使用NettyServerHandler来处理连接,你可以根据需要自己实现。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值