Netty系列(一):Springboot整合Netty,自定义协议实现

news2024/11/17 5:51:28

Netty是由JBOSS提供的一个java开源框架,现为 Github上的独立项目。Netty提供异步的、事件驱动的网络应用程序框架和工具,用以快速开发高性能、高可靠性的网络服务器和客户端程序。

也就是说,Netty 是一个基于NIO的客户、服务器端的编程框架,使用Netty 可以确保你快速和简单的开发出一个网络应用,例如实现了某种协议的客户、服务端应用。Netty相当于简化和流线化了网络应用的编程开发过程。

Springboot整合Netty

新建springboot项目,并在项目以来中导入netty包,用fastjson包处理jsonStr。

		<!-- netty -->
        <dependency>
            <groupId>io.netty</groupId>
            <artifactId>netty-all</artifactId>
            <version>4.1.42.Final</version>
        </dependency>

        <!-- Json处理 -->
        <dependency>
            <groupId>com.alibaba.fastjson2</groupId>
            <artifactId>fastjson2</artifactId>
            <version>2.0.16</version>
        </dependency>

创建netty相关配置信息文件

  1. yml配置文件——application.yml
# netty 配置
netty:
  # boss线程数量
  boss: 4
  # worker线程数量
  worker: 2
  # 连接超时时间
  timeout: 6000
  # 服务器主端口
  port: 18023
  # 服务器备用端口
  portSalve: 18026
  # 服务器地址
  host: 127.0.0.1
  1. netty配置实体类——NettyProperties与yml配置文件绑定
    通过@ConfigurationProperties(prefix = "netty")注解读取配置文件中的netty配置,通过反射注入值,需要在实体类中提供对应的setter和getter方法。

@ConfigurationProperties(prefix = "netty")对应的实体类属性名称不要求一定相同,只需保证“set”字符串拼接配置文件的属性和setter方法名相同即可。

@Configuration
@ConfigurationProperties(prefix = "netty")
public class NettyProperties {

    /**
     * boss线程数量
     */
    private Integer boss;

    /**
     * worker线程数量
     */
    private Integer worker;

    /**
     * 连接超时时间
     */
    private Integer timeout = 30000;

    /**
     * 服务器主端口
     */
    private Integer port = 18023;

    /**
     * 服务器备用端口
     */
    private Integer portSalve = 18026;

    /**
     * 服务器地址 默认为本地
     */
    private String host = "127.0.0.1";
	
	// setter、getter 。。。。
}
  1. 对netty进行配置,绑定netty相关配置设置
    Netty通常由一个Bootstrap开始,主要作用是配置整个Netty程序,串联各个组件,Netty中Bootstrap类是客户端程序的启动引导类,ServerBootstrap是服务端启动引导类。
@Configuration
@EnableConfigurationProperties
public class NettyConfig {
    final NettyProperties nettyProperties;

    public NettyConfig(NettyProperties nettyProperties) {
        this.nettyProperties = nettyProperties;
    }

    /**
     * boss线程池-进行客户端连接
     *
     * @return
     */
    @Bean
    public NioEventLoopGroup boosGroup() {
        return new NioEventLoopGroup(nettyProperties.getBoss());
    }

    /**
     * worker线程池-进行业务处理
     *
     * @return
     */
    @Bean
    public NioEventLoopGroup workerGroup() {
        return new NioEventLoopGroup(nettyProperties.getWorker());
    }

    /**
     * 服务端启动器,监听客户端连接
     *
     * @return
     */
    @Bean
    public ServerBootstrap serverBootstrap() {
        ServerBootstrap serverBootstrap = new ServerBootstrap()
                // 指定使用的线程组
                .group(boosGroup(), workerGroup())
                // 指定使用的通道
                .channel(NioServerSocketChannel.class)
                // 指定连接超时时间
                .option(ChannelOption.CONNECT_TIMEOUT_MILLIS, nettyProperties.getTimeout())
                // 指定worker处理器
                .childHandler(new NettyServerHandler());
        return serverBootstrap;
    }
}
  1. worker处理器,初始化通道以及配置对应管道的处理器
    自定义了##@##分割符,通过DelimiterBasedFrameDecoder来处理拆包沾包问题;
    通过MessageDecodeHandler将接收消息解码处理成对象实例;
    通过MessageEncodeHandler将发送消息增加分割符后并编码;
    最后通过ServerListenerHandler根据消息类型对应处理不同消息。
public class NettyServerHandler extends ChannelInitializer<SocketChannel> {
    @Override
    protected void initChannel(SocketChannel socketChannel) throws Exception {
        // 数据分割符
        String delimiterStr = "##@##";
        ByteBuf delimiter = Unpooled.copiedBuffer(delimiterStr.getBytes());
        ChannelPipeline pipeline = socketChannel.pipeline();
        // 使用自定义处理拆包/沾包,并且每次查找的最大长度为1024字节
        pipeline.addLast(new DelimiterBasedFrameDecoder(1024, delimiter));
        // 将上一步解码后的数据转码为Message实例
        pipeline.addLast(new MessageDecodeHandler());
        // 对发送客户端的数据进行编码,并添加数据分隔符
        pipeline.addLast(new MessageEncodeHandler(delimiterStr));
        // 对数据进行最终处理
        pipeline.addLast(new ServerListenerHandler());
    }
}
  1. 数据解码
    数据解码和编码都采用UTF8格式
public class MessageDecodeHandler extends ByteToMessageDecoder {

    @Override
    protected void decode(ChannelHandlerContext channelHandlerContext, ByteBuf in, List<Object> list) throws Exception {
        ByteBuf frame = in.retainedDuplicate();
        final String content = frame.toString(CharsetUtil.UTF_8);
        Message message = new Message(content);
        list.add(message);
        in.skipBytes(in.readableBytes());
    }
}
  1. 数据解码转换的实例
    Message类用于承载消息、转JsonString

public class Message {
    /**
     * 数据长度
     */
    private Integer len;

    /**
     * 接收的通讯数据body
     */
    private String content;

    /**
     * 消息类型
     */
    private Integer msgType;

    public Message(Object object) {
        String str = object.toString();
        JSONObject jsonObject = JSONObject.parseObject(str);
        msgType = Integer.valueOf(jsonObject.getString("msg_type"));
        content = jsonObject.getString("body");
        len = str.length();
    }

    public String toJsonString() {
        return "{" +
                "\"msg_type\": " + msgType + ",\n" +
                "\"body\": " + content +
                "}";
    }
	// setter、getter 。。。。
}
  1. 数据编码
    netty服务端回复消息时,对消息转JsonString增加分割符,并进行编码。
public class MessageEncodeHandler extends MessageToByteEncoder<Message> {
    // 数据分割符
    String delimiter;

    public MessageEncodeHandler(String delimiter) {
        this.delimiter = delimiter;
    }

    @Override
    protected void encode(ChannelHandlerContext channelHandlerContext, Message message, ByteBuf out) throws Exception {
        out.writeBytes((message.toJsonString() + delimiter).getBytes(CharsetUtil.UTF_8));
    }
}
  1. 数据处理器,针对不同类型数据分类处理
    在处理不同接收数据时使用了枚举类型,在使用switch时可以做下处理,具体参考代码,这里只演示如何操作,并没实现数据处理业务类。

public class ServerListenerHandler extends SimpleChannelInboundHandler<Message> {
    private static final Logger log = LoggerFactory.getLogger(ServerListenerHandler.class);

    /**
     * 设备接入连接时处理
     *
     * @param ctx
     */
    @Override
    public void handlerAdded(ChannelHandlerContext ctx) {
        log.info("有新的连接:[{}]", ctx.channel().id().asLongText());
    }

    /**
     * 数据处理
     *
     * @param ctx
     * @param msg
     */
    @Override
    protected void channelRead0(ChannelHandlerContext ctx, Message msg) {
        // 获取消息实例中的消息体
        String content = msg.getContent();
        // 对不同消息类型进行处理
        MessageEnum type = MessageEnum.getStructureEnum(msg);
        switch (type) {
            case CONNECT:
                // TODO 心跳消息处理
            case STATE:
                // TODO 设备状态
            default:
                System.out.println(type.content + "消息内容" + content);
        }
    }

    /**
     * 设备下线处理
     *
     * @param ctx
     */
    @Override
    public void handlerRemoved(ChannelHandlerContext ctx) {
        log.info("设备下线了:{}", ctx.channel().id().asLongText());
    }

    /**
     * 设备连接异常处理
     *
     * @param ctx
     * @param cause
     */
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
        // 打印异常
        log.info("异常:{}", cause.getMessage());
        // 关闭连接
        ctx.close();
    }
}
  1. 数据类型枚举类

public enum MessageEnum {
    CONNECT(1, "心跳消息"),
    STATE(2, "设备状态");

    public final Integer type;
    public final String content;

    MessageEnum(Integer type, String content) {
        this.type = type;
        this.content = content;
    }

    // case中判断使用
    public static MessageEnum getStructureEnum(Message msg) {
        Integer type = Optional.ofNullable(msg)
                .map(Message::getMsgType)
                .orElse(0);
        if (type == 0) {
            return null;
        } else {
            List<MessageEnum> objectEnums = Arrays.stream(MessageEnum.values())
                    .filter((item) -> item.getType() == type)
                    .distinct()
                    .collect(Collectors.toList());
            if (objectEnums.size() > 0) {
                return objectEnums.get(0);
            }
            return null;
        }
    }
	// setter、getter。。。。
}

到此Netty整个配置已经完成,但如果要跟随springboot一起启动,仍需要做一些配置。

  1. netty启动类配置
@Component
public class NettyServerBoot {
    private static final Logger log = LoggerFactory.getLogger(NettyServerBoot.class);
    @Resource
    NioEventLoopGroup boosGroup;
    @Resource
    NioEventLoopGroup workerGroup;
    final ServerBootstrap serverBootstrap;
    final NettyProperties nettyProperties;

    public NettyServerBoot(ServerBootstrap serverBootstrap, NettyProperties nettyProperties) {
        this.serverBootstrap = serverBootstrap;
        this.nettyProperties = nettyProperties;
    }


    /**
     * 启动netty
     *
     * @throws InterruptedException
     */
    @PostConstruct
    public void start() throws InterruptedException {
        // 绑定端口启动
        serverBootstrap.bind(nettyProperties.getPort()).sync();
        // 备用端口
        serverBootstrap.bind(nettyProperties.getPortSalve()).sync();
        log.info("启动Netty: {},{}", nettyProperties.getPort(), nettyProperties.getPortSalve());
    }

    /**
     * 关闭netty
     */
    @PreDestroy
    public void close() {
        log.info("关闭Netty");
        boosGroup.shutdownGracefully();
        workerGroup.shutdownGracefully();
    }
}

增加NettyServerBoot配置后,启动application时,netty服务端会跟随一起启动。
在这里插入图片描述
同时,在springboot关闭前,会先销毁netty服务。
在这里插入图片描述

完整源码

https://github.com/BerBai/JavaExample/tree/master/netty

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/30299.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

目标检测论文解读复现之十六:基于改进YOLOv5的小目标检测算法

前言 此前出了目标改进算法专栏&#xff0c;但是对于应用于什么场景&#xff0c;需要什么改进方法对应与自己的应用场景有效果&#xff0c;并且多少改进点能发什么水平的文章&#xff0c;为解决大家的困惑&#xff0c;此系列文章旨在给大家解读最新目标检测算法论文&#xff0c…

Java项目使用intellij-IDEA查看依赖包版本是否有冲突(方法及工具)

编译器及版本idea-ultimate依赖管理工具maven 第一个是idea本身的 Step1&#xff1a;点击右侧的maven Step2&#xff1a;右键依赖项&#xff0c;点击分析依赖关系 Step3&#xff1a;可以在模块名位置进行切换&#xff0c;左侧三角的标志则表示该包引入了多个版本&#xff…

【云原生】Docker网络原理及Cgroup硬件资源占用控制

内容预知 1.dockers的网络模式 获取容器的进程号 docker网络模式的特性 1.1 host主机模式 1.2 container模式 1.3 none模式 1.4 bridge 桥接模式 1.5 容器的自定义网络 &#xff08;1&#xff09;未创建自定义网络时&#xff0c;创建指定IP容器的测试 &#xff08;2&a…

双坐标轴柱状图

双坐标轴柱状图 setwd(“H:/分析评价 20220531/6-分析过程”) #设置工作路径 library(xlsx)#加载excel文件包 #---------------------------------------------------------------------------------------------------------- tiff(file“1-占比.tiff”,res600,width6000,hei…

负载均衡有哪些?

目录 【一】前言 【二】负载均衡分类 2.1 DNS 2.2 硬件负载均衡 2.3 软件负载均衡 2.4 组合负载均衡 【三】负载均衡算法 3.1 负载均衡算法分类 3.2 轮询 3.3 加权轮询 3.4 负载最低优先 3.5 性能最优类 3.6 Hash 【四】总结 【一】前言 在互联网尤其是移动互联…

【前沿技术RPA】 一文了解UiPath的项目活动设置

&#x1f40b;作者简介&#xff1a;博主是一位.Net开发者&#xff0c;同时也是RPA和低代码平台的践行者。 &#x1f42c;个人主页&#xff1a;会敲键盘的肘子 &#x1f430;系列专栏&#xff1a;UiPath &#x1f980;专栏简介&#xff1a;UiPath在传统的RPA&#xff08;Robotic…

【计算机组成原理Note】5.2 指令周期的数据流

5.2 指令周期的数据流 5.2.1 指令周期 指令周期&#xff1a;CPU从主存中每取出并执行一条指令所需的全部时间。 指令周期&#xff1a;常常用若干机器周期来表示&#xff0c;机器周期又叫CPU周期。 一个机器周期又包含若干时钟周期(也称为节拍、T周期或CPU时钟周期&#xff…

SpringCloud微服务(十一)——Sentinel服务熔断限流

SpringCloud Alibaba Sentinel服务熔断与限流 简介 github&#xff1a;[https://github.com/alibaba/Sentinel/wiki/%E4%B8%BB%E9%A1%B5](https://github.com/alibaba/Sentinel/wiki/如何使用) 官网&#xff1a;https://spring-cloud-alibaba-group.github.io/github-pages/…

为什么追踪员工的时间和出勤率很重要

监控员工的出勤时间和出勤率对于提高业务绩效和生产力至关重要。实施有效计时策略的组织可以帮助员工跟上项目目标和截止日期&#xff0c;提高客户满意度&#xff0c;并加强对员工产出的整体意识。所以每个企业组织都应该掌握员工出勤时间和出勤数据。 为什么要掌握员工出勤时…

阿里架构师耗时 1 年,把 P8 所需要的整个 Java 体系,都整理到了一起

有人调侃我们说&#xff1a; 程序员不如送外卖。送外卖是搬运食物&#xff0c;自己是搬运代码&#xff0c;都不产出新的东西…… 透支体力&#xff0c;又消耗健康&#xff0c;可替代性极强&#xff0c;30 岁之后就要面临被优化的危险…… 想跳槽&#xff0c;但是更高的平台难…

微信小程序怎么弄?【小程序制作】

微信小程序怎么弄&#xff1f;很多人都会想弄一个微信小程序&#xff0c;因为微信小程序这个轻应用现在的使用频率已经赶上微信了&#xff0c;有如此大的用户群体&#xff0c;企业和商家当然都想在这个庞大流量池里分一杯羹。那么微信小程序怎么弄呢&#xff1f;下面一起来看看…

群签名、环签名、盲签名

文章目录群签名定义安全性构造环签名定义安全性构造盲签名定义安全性构造群签名 定义 群签名方案是算法组 ΠGS(Gen,Sign,Ver,Open)\Pi_{GS}(Gen, Sign, Ver, Open)ΠGS​(Gen,Sign,Ver,Open)&#xff0c; Gen(1λ,n)Gen(1^\lambda,n)Gen(1λ,n)&#xff1a;密钥生成算法&…

百度Q3财报显AI技术厚度,“慢生意”稳步驶入“快车道”

一周前&#xff0c;笔者参加了一场百度主办的关于AIGC话题的沙龙&#xff0c;因为话题无比火爆&#xff0c;活动延迟到了一点钟才结束&#xff0c;以至于让约定的好友饭局也一等再等。 倒没有丝毫抱怨的意思&#xff0c;正是这个烧脑的活动&#xff0c;让我感受并体验到了当下最…

GEE开发之Modis_LAI数据分析和获取

GEE开发之Modis_LAI数据分析和获取1.遥感卫星数据叶面积指数LAI2.MOD15A2H(500m/8天)2.1 MOD15A2H下的指数2.2 LAI遥感影像查看获取3.LAI日数据下载4.LAI月数据下载5.LAI年数据下载前言&#xff1a;主要介绍LAI的概念&#xff0c;以及GEE下如何获取查看Modis下的LAI指数&#x…

Talk | 清华大学陈晓宇苏黎世联邦理工黄嘉伟 :基于实际应用的强化学习

本期为TechBeat人工智能社区第455期线上Talk&#xff01; 北京时间11月17日(周四)20:00&#xff0c;清华大学交叉信息研究院在读博士生——陈晓宇与苏黎世联邦理工大学计算机科学在读博士生——黄嘉伟的Talk将准时在TechBeat人工智能社区开播&#xff01; 他们与大家分享的主题…

NC发布猕猴大脑皮层多组学细胞图谱,助力神经系统疾病研究 | 时空专辑数据库

近日&#xff0c;杭州华大生命科学研究院&#xff08;以下简称杭州华大&#xff09;联合昆明理工大学灵长类转化医学研究院、美国艾伦脑科学研究所等国内外多家单位在国际学术期刊《自然通讯》&#xff08;Nature Communications&#xff09;在线发表题为《成年猕猴大脑皮层空间…

PyTorch深度学习实践——线性模型、梯度下降算法、反向传播

1、线性回归 参考资料1&#xff1a;https://blog.csdn.net/bit452/article/details/109627469 参考资料2&#xff1a;http://biranda.top/Pytorch%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0003%E2%80%94%E2%80%94%E7%BA%BF%E6%80%A7%E6%A8%A1%E5%9E%8B/#%E7%BA%BF%E6%80%A7%E6%A8%…

PC_多处理器

文章目录多处理器单指令单数据流SISD结构单指令流多数据流SIMD结构向量处理器多指令流单数据流MISD结构多指令多数据流MIMD结构小结硬件多线程细粒度多线程粗粒度多线程同时多线程多核处理器共享内存多处理器多处理器 常规的单处理器属于SISD常规多处理器属于MIMD 单指令单数…

腾格尔十月天传媒联手《巴林塔娜》,2255万粉丝多少买票支持

曾几何时&#xff0c;木桶原理非常流行&#xff0c;意思就是一个木桶能够盛多少水&#xff0c;取决于最短一块板的长度。可是随着社会的发展&#xff0c;木桶原理已经被淘汰&#xff0c;只要你拥有了团队合作&#xff0c;就可以统协作取长补短。 就拿有着“草原歌神”之称的腾格…

你的知识库能提高工作效率的7个原因

知识就是力量。但到目前为止&#xff0c;光有知识是不够的——你使用这些信息的方式让你领先于竞争对手。如果使用正确&#xff0c;知识库软件可以帮助您提供更好的服务&#xff0c;培训您的员工&#xff0c;并成为您的行业权威。拥有一个有效的知识库不仅会影响你在内部开展业…