用 POJO 代替 ByteBuf

380 阅读1分钟

这是我参与更文挑战的第 17 天,活动详情查看


theme: channing-cyan highlight: a11y-dark

到目前为止,我们回顾的所有示例都使用 一个ByteBuf作为协议消息的主要数据结构。在本节中,我们将改进TIME协议客户端和服务器示例以使用 POJO 而不是ByteBuf.

在你的ChannelHandlers中使用 POJO 的优势是显而易见的;通过分离ByteBuf从处理程序中提取信息的代码,您的处理程序变得更易于维护和重用。在TIME客户端和服务器示例中,我们只读取一个 32 位整数,ByteBuf直接使用不是主要问题。但是,您会发现在实现实际协议时有必要进行分离。

首先,让我们定义一个名为 的新类型UnixTime。

package io.netty.example.time;

import java.util.Date;

public class UnixTime {

    private final long value;
    
    public UnixTime() {
        this(System.currentTimeMillis() / 1000L + 2208988800L);
    }
    
    public UnixTime(long value) {
        this.value = value;
    }
        
    public long value() {
        return value;
    }
        
    @Override
    public String toString() {
        return new Date((value() - 2208988800L) * 1000L).toString();
    }
}

我们现在可以修改TimeDecoder以生成 UnixTime而不是ByteBuf

@Override
protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) {
    if (in.readableBytes() < 4) {
        return;
    }

    out.add(new UnixTime(in.readUnsignedInt()));
}

使用更新的解码器,TimeClientHandler不再使用ByteBuf:

@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) {
    UnixTime m = (UnixTime) msg;
    System.out.println(m);
    ctx.close();
}

更简单和优雅,对吧?可以在服务器端应用相同的技术。TimeServerHandler这次我们先更新一下:

@Override
public void channelActive(ChannelHandlerContext ctx) {
    ChannelFuture f = ctx.writeAndFlush(new UnixTime());
    f.addListener(ChannelFutureListener.CLOSE);
}

现在,唯一缺少的部分是编码器,它是ChannelOutboundHandler将 aUnixTime转换为ByteBuf. 它比编写解码器简单得多,因为在编码消息时无需处理数据包碎片和组装。

package io.netty.example.time;

public class TimeEncoder extends ChannelOutboundHandlerAdapter {
    @Override
    public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) {
        UnixTime m = (UnixTime) msg;
        ByteBuf encoded = ctx.alloc().buffer(4);
        encoded.writeInt((int)m.value());
        ctx.write(encoded, promise); // (1)
    }
}

在这一行中有很多重要的事情。

  • 首先,我们按原样传递原始数据ChannelPromise,以便 Netty 在编码数据实际写入线路时将其标记为成功或失败。
  • 其次,我们没有调用ctx.flush(). 有一个单独的处理程序方法void lush(ChannelHandlerContext ctx)用于覆盖flush()操作。

为了进一步简化,您可以使用MessageToByteEncoder:

public class TimeEncoder extends MessageToByteEncoder<UnixTime> {
    @Override
    protected void encode(ChannelHandlerContext ctx, UnixTime msg, ByteBuf out) {
        out.writeInt((int)msg.value());
    }
}

剩下的最后一个任务是插入TimeEncoder到ChannelPipeline之前的服务器端TimeServerHandler,并留作一件容易的事。

关闭您的应用程序 关闭 Netty 应用程序通常就像关闭EventLoopGroup您通过shutdownGracefully(). Future当sEventLoopGroup已完全终止并且Channel属于该组的所有s 已关闭时,它会返回通知您。