Netty Springboot 实战项目,适合新手学习

微信交流社区

  • 添加微信 372787553 备注 csdn
  • 项目源码地址在文末

Netty 是什么?

Netty是 一个异步事件驱动的网络应用程序框架,用于快速开发可维护的高性能协议服务器和客户端。
Netty是一个NIO客户端服务器框架,可以快速轻松地开发网络应用程序,例如协议服务器和客户端。它极大地简化和简化了诸如TCP和UDP套接字服务器之类的网络编程。
“快速简便”并不意味着最终的应用程序将遭受可维护性或性能问题的困扰。Netty经过精心设计,结合了许多协议(例如FTP,SMTP,HTTP以及各种基于二进制和文本的旧式协议)的实施经验。结果,Netty成功地找到了一种无需妥协即可轻松实现开发,性能,稳定性和灵活性的方法。
在这里插入图片描述

概念性的在这里就不做过多的说明了,本文将围绕实战进行

Spring boot + Netty

模块介绍

  • javayh-health-monitor-server
    • 心跳检测的服务中心
  • javayh-health-monitor-client
    • 心跳检测的客户端,需要作为jar 进行引入
  • javayh-health-monitor-demo
    • 示例的demo

服务端创建

  • 创建启动类
public class HeartBeatServer {

	/**
	 * NioEventLoopGroup是一个处理I / O操作的多线程事件循环。 Netty为不同类型的传输提供各种EventLoopGroup实现。
	 * 我们在此示例中实现了服务器端应用程序,因此将使用两个NioEventLoopGroup。 第一个,通常称为“老板”,接受传入连接。第二个,通常称为“工人”,
	 * 一旦老板接受连接并将接受的连接注册到工作人员,就处理被接受连接的流量。
	 * 使用了多少个线程以及它们如何映射到创建的Channels取决于EventLoopGroup实现,甚至可以通过构造函数进行配置。
	 */
	private EventLoopGroup boss = new NioEventLoopGroup();

	private EventLoopGroup work = new NioEventLoopGroup();



	/**
	 * 启动 Netty
	 * @return
	 * @throws InterruptedException
	 * @PostConstruct
	 */
	public void start(int port) throws InterruptedException {
		ServerBootstrap bootstrap = new ServerBootstrap().group(boss, work)
				.channel(NioServerSocketChannel.class)
				.localAddress(new InetSocketAddress(port))
				// 保持长连接
				.childOption(ChannelOption.SO_KEEPALIVE, true)
				.childHandler(new HeartbeatInitializer());
		// 绑定并开始接受传入的连接。
		ChannelFuture future = bootstrap.bind(port).sync();
		if (future.isSuccess()) {
			log.info("启动 Java有货 Health Monitor Server 成功");
		}
		future.channel().closeFuture().sync();
	}

	/**
	 * 销毁
	 * @PreDestroy
	 */
	public void destroy() {
		boss.shutdownGracefully().syncUninterruptibly();
		work.shutdownGracefully().syncUninterruptibly();
		log.info("关闭 Java有货 Health Monitor Server 成功");
	}

}
  • 创建Handler
public class HeartBeatSimpleHandler extends SimpleChannelInboundHandler<MessageBody> {

    private static final ByteBuf HEART_BEAT = Unpooled.unreleasableBuffer(
            Unpooled.copiedBuffer(
                    MessageBody.builder()
                            .msgId(20200202L)
                            .msg("pong")
                            .appName("Health-Monitor-Server")
                            .createDate(new Date()).build().toString(),
                    CharsetUtil.UTF_8));

    private static final String YMS = "yyyy-MM-dd HH:mm:ss";

    /**
     * <p>
     * 链接验证
     * </p>
     *
     * @param context
     * @param messageBody
     * @return void
     * @version 1.0.0
     * @author Dylan-haiji
     * @since 2021/4/13
     */
    @Override
    protected void channelRead0(ChannelHandlerContext context, MessageBody messageBody)
            throws Exception {
        StringBuilder sb = new StringBuilder();
        sb.append("服务端消息接收成功==>").append(messageBody);
        log.info(sb.toString());
        // 保存客户端与 Channel 之间的关系
        NettySocketHolder.put(messageBody.getAppName(),(NioSocketChannel) context.channel());
    }

    /**
     * <p>
     * 取消绑定
     * </p>
     *
     * @param ctx
     * @return void
     * @version 1.0.0
     * @author Dylan-haiji
     * @since 2021/4/13
     */
    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
        NettySocketHolder.remove((NioSocketChannel) ctx.channel());
    }

    /**
     * <p>
     * 尝试建立链接
     * </p>
     *
     * @param ctx
     * @param evt
     * @return void
     * @version 1.0.0
     * @author Dylan-haiji
     * @since 2021/4/13
     */
    @Override
    public void userEventTriggered(ChannelHandlerContext ctx, Object evt)
            throws Exception {
        if (evt instanceof IdleStateEvent) {
            IdleStateEvent idleStateEvent = (IdleStateEvent) evt;
            if (idleStateEvent.state() == IdleState.READER_IDLE) {
                log.info("已经5秒没有收到信息!");
                // 向客户端发送消息
                ctx.writeAndFlush(HEART_BEAT)
                        .addListener(ChannelFutureListener.CLOSE_ON_FAILURE);
            }
        }
        super.userEventTriggered(ctx, evt);
    }

    /**
     * <p>
     *      有服务上线通知
     * </p>
     * @version 1.0.0
     * @since 2021/4/14
     * @param ctx
     * @return void
     */
    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        log.info("服务上线时间为: {}, IP 为:{}", DateFormatUtils.format(new Date(),YMS), ctx.channel().remoteAddress());
    }

    /**
     * <p>
     * 异常的处理机制
     * </p>
     *
     * @param ctx
     * @param cause
     * @return void
     * @version 1.0.0
     * @since 2021/4/14
     */
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        throw new HealthMonitorException(cause.getMessage(), cause);
    }
}

客户端创建

  • 启动类
public class HeartbeatClient {

	private final static Logger LOGGER = LoggerFactory.getLogger(HeartbeatClient.class);

	private EventLoopGroup group = new NioEventLoopGroup();

	private SocketChannel socketChannel;

	/**
	 * 启动类
	 * @throws InterruptedException
	 *
	 * @PostConstruct
	 */
	public void start(HeartbeatClientProperties properties) throws InterruptedException {
		Bootstrap bootstrap = new Bootstrap();
		/*
		 * NioSocketChannel用于创建客户端通道,而不是NioServerSocketChannel。
		 * 请注意,我们不像在ServerBootstrap中那样使用childOption(),因为客户端SocketChannel没有父服务器。
		 */
		bootstrap.group(group).channel(NioSocketChannel.class)
				.handler(new ClientHandleInitializer());
		/*
		 * 启动客户端 我们应该调用connect()方法而不是bind()方法。
		 */
		ChannelFuture future = bootstrap
				.connect(properties.getHost(), properties.getPort()).sync();
		if (future.isSuccess()) {
			LOGGER.info("启动 Java有货 Health Monitor Client 成功");
		}

		socketChannel = (SocketChannel) future.channel();
		future.channel().closeFuture().sync();
	}
	/**
	 * 销毁	@PreDestroy
	 */
	public void destroy() {
		group.shutdownGracefully().syncUninterruptibly();
		LOGGER.info("关闭 Java有货 Health Monitor Client 成功");
	}

}

  • 创建Handler
@Slf4j
public class EchoClientHandler extends SimpleChannelInboundHandler<ByteBuf> {

	@Override
	public void userEventTriggered(ChannelHandlerContext ctx, Object evt)
			throws Exception {
		if (evt instanceof IdleStateEvent) {
			IdleStateEvent idleStateEvent = (IdleStateEvent) evt;
			if (idleStateEvent.state() == IdleState.WRITER_IDLE) {
				log.info("已经10秒没收到消息了");
				// 向服务端发送消息
				MessageBody heartBeat = SpringUtils.getBean("heartBeat",
						MessageBody.class);
				ctx.writeAndFlush(heartBeat)
						.addListener(ChannelFutureListener.CLOSE_ON_FAILURE);
			}

		}
		super.userEventTriggered(ctx, evt);
	}

	/**
	 * 每当从服务端接收到新数据时,都会使用收到的消息调用此方法 channelRead0(),在此示例中,接收消息的类型是ByteBuf。
	 * @param channelHandlerContext
	 * @param byteBuf
	 * @throws Exception
	 */
	@Override
	protected void channelRead0(ChannelHandlerContext channelHandlerContext,
			ByteBuf byteBuf) throws Exception {
		// 从服务端收到消息时被调用
        StringBuilder sb = new StringBuilder();
        sb.append("客户端收到消息==>")
                .append(byteBuf.toString(CharsetUtil.UTF_8));
		log.info(sb.toString());
	}

}

最终效果

在这里插入图片描述在这里插入图片描述
由于服务代码过长,这里就不全部黏贴出来了,已经将以上代码放在Github上,并将做成了Springboot 的Starter 可以直接进行使用,有兴趣的小伙伴可以一起进行完善!
项目源码 https://github.com/yanghaiji/health-monitor

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

小杨同学~

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

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

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

打赏作者

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

抵扣说明:

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

余额充值