Netty学习三:Netty框架之引导器

一、基于Netty搭建简单的Http服务器

基于Netty搭建简单得Http服务器,只需要创建服务启动类和业务逻辑处理类即可

服务启动类
public class HttpServer {
  public void start(int port) throws Exception {
    EventLoopGroup bossGroup = new NioEventLoopGroup();
    EventLoopGroup workerGroup = new NioEventLoopGroup();
    try {
      ServerBootstrap b = new ServerBootstrap();
      b.group(bossGroup, workerGroup)
          .channel(NioServerSocketChannel.class)
          .localAddress(new InetSocketAddress(port))
          .childHandler(new ChannelInitializer<SocketChannel>() {
            @Override
            public void initChannel(SocketChannel ch) {
              ch.pipeline()
                  .addLast("codec", new HttpServerCodec())         // HTTP 编解码
                  .addLast("compressor", new HttpContentCompressor())   // HttpContent 压缩
                  .addLast("aggregator", new HttpObjectAggregator(65536)) // HTTP 消息聚合
                  .addLast("handler", new HttpServerHandler());      // 自定义业务逻辑处理器
            }
          })
          .childOption(ChannelOption.SO_KEEPALIVE, true);
      ChannelFuture f = b.bind().sync();
      System.out.println("Http Server started, Listening on " + port);
      f.channel().closeFuture().sync();
    } finally {
      workerGroup.shutdownGracefully();
      bossGroup.shutdownGracefully();
    }
  }
  
  public static void main(String[] args) throws Exception {
    new HttpServer().start(8088);
  }
}
业务逻辑处理类
public class HttpServerHandler extends SimpleChannelInboundHandler&lt;FullHttpRequest&gt; {
  @Override
  protected void channelRead0(ChannelHandlerContext ctx, FullHttpRequest msg) {
    String content = String.format("Receive http request, uri: %s, method: %s, content: %s%n", msg.uri(), msg.method(), msg.content().toString(CharsetUtil.UTF_8));
    FullHttpResponse response = new DefaultFullHttpResponse(
        HttpVersion.HTTP_1_1,
        HttpResponseStatus.OK,
        Unpooled.wrappedBuffer(content.getBytes()));
    ctx.writeAndFlush(response).addListener(ChannelFutureListener.CLOSE);
  }
}
测试:
  • 启动 HttpServer 的 main 函数。
  • 终端或浏览器发起 HTTP 请求。
$ curl http://localhost:8088/abc
$ Receive http request, uri: /abc, method: GET, content:

二、引导器实践指南

Netty服务端的启动过程大致分为:

  • 配置线程池
  • Channel初始化
  • 端口绑定
1. 配置线程池

通过参数设置,可以支持三种Reactor模式:单线程模型、多线程模型、主从多线程模型

  • 单线程模型:所有I/O操作由一个线程完成,只启动一个EventLoopGroup线程池,线程池类只有一个EventLoop线程
EventLoopGroup group = new NioEventLoopGroup(1);
ServerBootstrap b = new ServerBootstrap();
b.group(group)
  • 多线程模型:与单线程类似,但EventLoopGroup不需要参数,默认启动2倍CPU核数的EventLoop线程,也至此自定义线程数
EventLoopGroup group = new NioEventLoopGroup();
ServerBootstrap b = new ServerBootstrap();
b.group(group)
  • 主从多线程模型:Boss 是主 Reactor,Worker 是从 Reactor。它们分别使用不同的 NioEventLoopGroup,主 Reactor 负责处理 Accept,然后把 Channel 注册到从 Reactor 上,从 Reactor 主要负责 Channel 生命周期内的所有 I/O 事件。
EventLoopGroup bossGroup = new NioEventLoopGroup();
EventLoopGroup workerGroup = new NioEventLoopGroup();
ServerBootstrap b = new ServerBootstrap();
b.group(bossGroup, workerGroup)
2. Channel初始化

NIO 模型是 Netty 中最成熟且被广泛使用的模型。因此,推荐 Netty 服务端采用NioServerSocketChannel 作为 Channel 的类型,客户端采用 NioSocketChannel。

通过childHandler()方法注册ChannelHandler。ChannelInitializer是实现了 ChannelHandler接口的匿名类,通过实例化 ChannelInitializer 作为 ServerBootstrap 的参数。

ChannelInitializer的initChannel()绑定提个PipeLine,用于服务编排。向PipeLine中按顺序添加多个ChannelHandler。

通过option()和childOption()方法设置Channel属性,option 主要负责设置 Boss 线程组,而 childOption 对应的是 Worker 线程组。Netty 提供了默认参数设置ChannelOption,具体参数参考:
https://www.cnblogs.com/googlemeoften/p/6082785.html

3. 端口绑定

完成Netty配置后,bind() 方法会真正触发启动,sync() 方法则会阻塞,直至整个启动过程完成。

三、Netty实现HTTP客户端

public class HttpClient {
  public void connect(String host, int port) throws Exception {
    EventLoopGroup group = new NioEventLoopGroup();
    try {
      Bootstrap b = new Bootstrap();
      b.group(group);
      b.channel(NioSocketChannel.class);
      b.option(ChannelOption.SO_KEEPALIVE, true);
      b.handler(new ChannelInitializer&lt;SocketChannel&gt;() {
        @Override
        public void initChannel(SocketChannel ch) {
          ch.pipeline().addLast(new HttpResponseDecoder());
          ch.pipeline().addLast(new HttpRequestEncoder());
          ch.pipeline().addLast(new HttpClientHandler());
        }
      });
      ChannelFuture f = b.connect(host, port).sync();
      URI uri = new URI("http://127.0.0.1:8088");
      String content = "hello world";
      DefaultFullHttpRequest request = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET,
          uri.toASCIIString(), Unpooled.wrappedBuffer(content.getBytes(StandardCharsets.UTF_8)));
      request.headers().set(HttpHeaderNames.HOST, host);
      request.headers().set(HttpHeaderNames.CONNECTION, HttpHeaderValues.KEEP_ALIVE);
      request.headers().set(HttpHeaderNames.CONTENT_LENGTH, request.content().readableBytes());
      f.channel().write(request);
      f.channel().flush();
      f.channel().closeFuture().sync();
    } finally {
      group.shutdownGracefully();
    }
  }
  
  public static void main(String[] args) throws Exception {
    HttpClient client = new HttpClient();
    client.connect("127.0.0.1", 8088);
  }
}

public class HttpClientHandler extends ChannelInboundHandlerAdapter {
  @Override
  public void channelRead(ChannelHandlerContext ctx, Object msg) {
    if (msg instanceof HttpContent) {
      HttpContent content = (HttpContent) msg;
      ByteBuf buf = content.content();
      System.out.println(buf.toString(io.netty.util.CharsetUtil.UTF_8));
      buf.release();
    }
  }
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值