3.完成netty相关的代码结构

1. netty介绍

netty官网:Netty: Home

Netty是一个基于Java NIO的网络编程框架,提供了一套高效的、事件驱动的异步网络通信机制。简化了网络应用程序的开发过程,提供了可靠的、高性能的网络传输。

netty的详细说明,网上资料很多,大家感兴趣自己学习哈。

2. 完成初始化netty

本过程只完成项目和netty的结合,定义好心跳检测handler,业务处理handler,服务端启动。

注意:从现在开始代码量将增多

2.1 启动Netty

实现spring提供的 CommandLineRunner接口,run方法中启动Netty服务端,考虑到后面会有存储Channel和serverId的关系以及节点信息等情况,我定义一个主信息类ServerInfo

2.2 ServerInfo -- 主信息类

@Slf4j
public class ServerInfo {
    
    private ServerInfo() {}
    private static NioEventLoopGroup bossGroup;
    public static void setBossGroup(NioEventLoopGroup bg) {
        bossGroup = bg;
    }
    public static NioEventLoopGroup getBossGroup() {
        return bossGroup;
    }
    private static NioEventLoopGroup workerGroup;
    private static Channel serverChannel;
    private static Bootstrap connectOtherNodeBootStrap;
    private static ServerBootstrap bootstrapForClient;
    public static void setWorkerGroup(NioEventLoopGroup bg) {
        workerGroup = bg;
    }
    public static NioEventLoopGroup getWorkerGroup() {
        return workerGroup;
    }

    public static void setServerChannel(Channel ch) {
        serverChannel = ch;
    }
    public static Channel getServerChannel() {
        return serverChannel;
    }
    public static void setConnectOtherNodeBootStrap(Bootstrap bs) {
        connectOtherNodeBootStrap = bs;
    }
    public static Bootstrap getConnectOtherNodeBootStrap() {
        return connectOtherNodeBootStrap;
    }

    public static void setBootstrapForClient(ServerBootstrap sbs) {
        bootstrapForClient = sbs;
    }
    public static ServerBootstrap getBootstrapForClient() {
        return bootstrapForClient;
    }

}

2.3 SpringInitRunner -- 启动netty的Runner

@Component
@Slf4j
public class SpringInitRunner implements CommandLineRunner {

    @Autowired
    private DttaskServerConfig dttaskServerConfig;

    @PostConstruct
    public void init() {
        initServerBootStrap();
        initConnectOtherNodeBootStrap();
    }

    private void initConnectOtherNodeBootStrap() {
        ServerInfo.setConnectOtherNodeBootStrap(new Bootstrap());
        ServerInfo.getConnectOtherNodeBootStrap().group(new NioEventLoopGroup(4))
                .channel(NioSocketChannel.class)
                .handler(new ChannelInitializer<SocketChannel>() {
                    @Override
                    protected void initChannel(SocketChannel socketChannel) {
                        socketChannel.pipeline().addLast(
                                new CrawlerMessageDecoder(MESSAGE_MAX_SIZE, MESSAGE_LENGTH_FILED_OFFSET, MESSAGE_LENGTH_FILED_LENGTH));
                        socketChannel.pipeline().addLast(
                                new IdleStateHandler(dttaskServerConfig.getReadIdleSecondTime(),
                                        dttaskServerConfig.getWriteIdleSecondTime(),
                                        dttaskServerConfig.getAllIdleSecondTime()));
                        socketChannel.pipeline().addLast(new CrawlerMessageEncoder());
                        socketChannel.pipeline().addLast(
                                new ServerClientChannelHandler());
                    }
                });
    }

    private void initServerBootStrap() {
        ServerInfo.setBossGroup(new NioEventLoopGroup(4));
        ServerInfo.setWorkerGroup(new NioEventLoopGroup(8));
        ServerInfo.setBootstrapForClient(new ServerBootstrap());
        ServerInfo.getBootstrapForClient().group(ServerInfo.getBossGroup(), ServerInfo.getWorkerGroup())
                .channel(NioServerSocketChannel.class)
                .childHandler(new ChannelInitializer<SocketChannel>() {
                    @Override
                    protected void initChannel(SocketChannel socketChannel) {
                        socketChannel.pipeline().addLast(
                                new CrawlerMessageDecoder(MESSAGE_MAX_SIZE, MESSAGE_LENGTH_FILED_OFFSET, MESSAGE_LENGTH_FILED_LENGTH));
                        socketChannel.pipeline().addLast(new CrawlerMessageEncoder());
                        IdleStateHandler idleStateHandler = new IdleStateHandler(dttaskServerConfig.getReadIdleSecondTime(), dttaskServerConfig.getWriteIdleSecondTime(), dttaskServerConfig.getAllIdleSecondTime());
                        socketChannel.pipeline().addLast(idleStateHandler);
                        socketChannel.pipeline().addLast(new HeartBeatServerHandler());
                        socketChannel.pipeline().addLast(
                                new ServerClientChannelHandler());
                    }
                });
    }

    @Override
    public void run(String... args) {
        log.info("spring启动完成,接下来启动 netty");
        try {
            log.info("启动监听其它节点端请求的服务端...");
            ServerInfo.setServerChannel(ServerInfo.getBootstrapForClient().bind(dttaskServerConfig.listenerPort()).sync().channel());
            
            // 下面连接其它节点...
        } catch (Exception e) {
            log.error("启动 监听其它节点请求的服务端出现异常", e);
            System.exit(-1);
        }
        log.info("netty 启动成功...");
    }

    @PreDestroy
    public void shutdown() {
        try {
            ServerInfo.getServerChannel().close().sync();
        } catch (InterruptedException e) {
            log.error("crawler-server netty shutdown 出现异常", e);
            Thread.currentThread().interrupt();
        } finally {
            ServerInfo.getWorkerGroup().shutdownGracefully();
            ServerInfo.getBossGroup().shutdownGracefully();
        }
    }
}

2.4 ServerClientChannelHandler -- netty处理请求的handler

@Slf4j
public class ServerClientChannelHandler extends SimpleChannelInboundHandler<DttaskMessage> {


    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        log.info("channelActive={}", ctx.channel().id());
    }

    @Override
    protected void channelRead0(ChannelHandlerContext ctx, DttaskMessage message) throws Exception {
        log.info("收到客户端的请求:{}", message);
    }

    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
        log.warn("channelInactive...");
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        log.warn("exceptionCaught...", cause);
    }


}

2.5 HeartBeatServerHandler -- 处理心跳的handler

@Slf4j
public class HeartBeatServerHandler extends ChannelInboundHandlerAdapter {  
    
    @Override  
    public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {  
        if (evt instanceof IdleStateEvent) {  
            IdleStateEvent event = (IdleStateEvent) evt;  
            if (event.state() == IdleState.READER_IDLE) {  
                log.warn("读取空闲...");
            } else if (event.state() == IdleState.WRITER_IDLE) {
                log.warn("写入空闲...");
            } else if (event.state() == IdleState.ALL_IDLE) {
                log.warn("serverId={}与server通信读取或写入空闲...");
            }
        }  
    }  
}

2.6 DttaskMessageDecoder -- 消息编码的handler

public class DttaskMessageDecoder extends LengthFieldBasedFrameDecoder {
    public DttaskMessageDecoder(int maxFrameLength, int lengthFieldOffset, int lengthFieldLength) {
        super(maxFrameLength, lengthFieldOffset, lengthFieldLength);
    }

    @Override
    protected Object decode(ChannelHandlerContext ctx, ByteBuf in) throws Exception {
        ByteBuf newIn = (ByteBuf) super.decode(ctx, in);
        if (newIn == null) {
            return null;
        }

        if (newIn.readableBytes() < MESSAGE_TOTAL_SIZE) {
            return null;
        }

        int frameLength = newIn.readInt();
        if (newIn.readableBytes() < frameLength) {
            return null;
        }
        DttaskMessage dttaskMessage = new DttaskMessage();
        byte type = newIn.readByte();
        dttaskMessage.setType(type);

        int infoLength = newIn.readInt();
        byte[] infoBytes = new byte[infoLength];
        newIn.readBytes(infoBytes);
        dttaskMessage.setInfo(new String(infoBytes));


        newIn.release();
        return dttaskMessage;
    }
}

2.7 DttaskMessageEncoder -- 消息解码的handler

public class DttaskMessageEncoder extends MessageToByteEncoder<DttaskMessage> {

    @Override
    protected void encode(ChannelHandlerContext channelHandlerContext, DttaskMessage dttaskMessage, ByteBuf byteBuf) throws Exception {
        int bodyLength = MESSAGE_TYPE_SIZE + MESSAGE_INFO_SIZE;
        byte[] infoBytes = null;
        if (dttaskMessage.getInfo() != null) {
            infoBytes = dttaskMessage.getInfo().getBytes();
            bodyLength += infoBytes.length;
        }

        byteBuf.writeInt(bodyLength);

        byteBuf.writeByte(dttaskMessage.getType());

        if (infoBytes != null) {
            byteBuf.writeInt(infoBytes.length);
            byteBuf.writeBytes(infoBytes);
        } else {
            byteBuf.writeInt(0x00);
        }

    }
}

2.8 DttaskMessage -- netty消息的封装类

目前定义了3种消息类型,COMMON_RESP、PING、PONG,后面会根据业务需要进行扩展

@Data
public class DttaskMessage {

    public static final byte COMMON_RESP = 0X00;
    public static final byte PING = 0X01;
    public static final byte PONG = 0X02;

    // 类型
    private byte type;
    // 消息实际信息
    private String info;
    
    public static DttaskMessage buildPingMessage(long serverId) {
        DttaskMessage dttaskMessage = new DttaskMessage();
        dttaskMessage.setType(PING);
        dttaskMessage.setInfo(JSON.toJSONString(new PingMessage(serverId)));
        return dttaskMessage;
    }

    public static DttaskMessage buildPongMessage(long serverId) {
        DttaskMessage dttaskMessage = new DttaskMessage();
        dttaskMessage.setType(PONG);
        dttaskMessage.setInfo(JSON.toJSONString(new PongMessage(serverId)));
        return dttaskMessage;
    }
    
    
    public static DttaskMessage buildCommonRespMessage(String message, boolean successFlag) {
        DttaskMessage dttaskMessage = new DttaskMessage();
        dttaskMessage.setType(COMMON_RESP);
        dttaskMessage.setInfo(JSON.toJSONString(new CommonRespMessage(message, successFlag)));
        return dttaskMessage;
    }
    
}

2.9 每种消息

2.9.1 CommonRespMessage

@Data
@AllArgsConstructor
@NoArgsConstructor
public class CommonRespMessage {
    
    private String message;
    private Boolean successFlag;
    
}

2.9.2 PingMessage

@Data
@AllArgsConstructor
@NoArgsConstructor
public class PingMessage {
    
    private Long serverId;
    
}

2.9.3 PongMessage

@Data
@AllArgsConstructor
@NoArgsConstructor
public class PongMessage {
    
    private Long serverId;
    
}

2.10 辅助类

考虑到后面要进行投票,每个节点会确定角色,程序中定义了ServerRole枚举类,每个节点的信息类NodeInfo,这里就不一一列出了,可以在源码处查看

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

lswsmj

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值