Springboot整合Netty,自定义协议实现

news2024/11/25 13:12:15

Springboot整合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服务。

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

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

相关文章

Unity物理系统-物理材质-Collider碰撞体的弹力不够大如何处理

物理材质Physic Material&#xff1b; 当碰撞器发生碰撞的时候&#xff0c;具有该材质的游戏物体就会具有该材质的属性&#xff1b; Step1&#xff1a;在Project窗口下新建Physic Material&#xff08;命名为mat&#xff09;&#xff0c;并更改其弹力参数为1 Step2&#xff1a…

408 考研《操作系统》第二章第七节:死锁和死锁的三种处理策略(预防死锁、避免死锁、检测和解除)

文章目录1.死锁1.1 什么是死锁&#xff1f;1.2 死锁、饥饿、死循环的区别1.3 死锁产生的必要条件1.4 什么时候会发生死锁&#xff1f;1.6 总结2. 死锁的处理策略——预防死锁2.1 破坏互斥条件2.2 破坏不剥夺条件2.3 破坏请求和保持条件2.4 破坏循环等待条件2.5 总结3. 死锁的处…

chatgpt+mirai实现QQ机器人

chatGPTbot 配合mirai机器使用 安装 Java &#xff08;版本必须 > 11&#xff09; 下载Mirai 控制台 https://github.com/iTXTech/mirai-console-loader/releases 手动安装插件mirai-api-http 1.双击mcl.cmd 或./mcl 运行 Mirai Console 生成 plugins 文件夹 2.从 ht…

Android基于开源项目搭建自己的技术堆栈

一、app的整体架构 从较高的层次讲&#xff0c;一个APP的整体架构可以分为两层&#xff0c;即应用层和基础框架层。 1、应用层专注于行业领域的实现&#xff0c;如金融、支付、地图、社交等&#xff0c;它直接面向用户&#xff0c;是用户对产品的第一层感知。 2、基础框架层…

从安装过程品国产Linux操作系统的技术来源与异同之处

作者&#xff1a;IT圈黎俊杰 使用Linux操作系统的第一步无疑是安装&#xff0c;本文通过作者亲手对多个国外主流开源操作系统、多个主流国产操作系统的安装过程进行记录与对比&#xff0c;让大家可以借助安装过程的界面风格、页面布局、功能等&#xff0c;细细的品一下国产Linu…

Unity3D教程:简单的碰撞检测

需求&#xff1a;当立方体Cube碰到地面Plane的时候&#xff0c;输出碰撞物体的名称&#xff0c;则表述检测到立方体碰撞了地面。 1.搭建一个简单的场景。 在新的工程中选择File->new Scene创建新的场景。然后在该场景中添加地板&#xff1a;GameObject->Create Other-&…

Mapstruct类型转换Person ->PersonDTO以及po、vo、dto、request概念

文章目录po、vo、dto、requestPerson ->PersonDTO安装插件引入依赖Maven项目结构图Person 与 PersonDTOConverterpo、vo、dto、request Mapstruct 完成类型转换&#xff01; po&#xff1a;数据持久层对象&#xff0c;用于映射数据库中的表 dto&#xff1a;数据传输对象&a…

各类软件研发行业源代码防泄密需求分析

各类软件研发行业&#xff0c;都有自己的核心数据以及核心文档&#xff0c;用户数据等敏感信息&#xff0c;这些信息数据有以下共性&#xff1a; 属于核心机密资料&#xff0c;万一泄密会给造成恶劣影响 核心数据类型多&#xff0c;有源代码数据&#xff0c;员工计算机水平高…

[附源码]Nodejs计算机毕业设计基于web的图书借阅管理系统Express(程序+LW)

该项目含有源码、文档、程序、数据库、配套开发软件、软件安装教程。欢迎交流 项目运行 环境配置&#xff1a; Node.js Vscode Mysql5.7 HBuilderXNavicat11VueExpress。 项目技术&#xff1a; Express框架 Node.js Vue 等等组成&#xff0c;B/S模式 Vscode管理前后端分…

2-3查找树-树-数据结构和算法(Java)

文章目录1 概述2 查找3 插入3.1 向2-结点中插入新键3.2 向一个只含有一个3-结点的树中插入新键3.3 向一个父结点为2-结点的3-结点中插入新键3.4 向一个父结点为3-结点的3-结点中插入新键4 分析4.1 局部变换4.2 全局性质5 后记1 概述 我们前面学习过关于二叉树的算法已经能够很…

CDN工作原理以及使用CDN的好处

所有的大厂以及其他很多互联网公司都使用了CDN&#xff0c;那CDN到底是什么呢&#xff1f;为什么要使用&#xff1f;一起来看看吧&#xff01; 文章目录1. 什么是CDN&#xff1f;2. CDN 是怎么工作的&#xff1f;3. 使用 CDN 有什么好处&#xff1f;3.1 缩短网站加载时间3.2 减…

Zookeeper[2]- Zookeeper集群环境搭建

前边步骤可参考: Zookeeper[1]-Zookeeper介绍与安装以及集群环境准备_豆虫儿的博客-CSDN博客Zookeeper的介绍和安装Zookeeper客户端使用ZookeeperJavaAPI使用我们为了学习Dubbo&#xff0c;而在dubbo中需要一个注册中心&#xff0c;而Zookeeper是我们在使用Dubbo是官方推荐的…

作业-12.13【使用QT制作一个简单的登录界面】

#include "widget.h" #include "ui_widget.h" Widget::Widget(QWidget *parent) : QWidget(parent) , ui(new Ui::Widget) { ui->setupUi(this); //设置窗口属性 this->setWindowTitle("Sumeru Dance Training"); this->setFixedSize(8…

[激光原理与应用-52]:《激光焊接质量实时监测系统研究》-3-传感器选型和电路设计

目录 3.1 传感器的选取 3.1.1 光电探测基本概念 3.1.2 可见光传感器 3.1.4 声音传感器 3.2 信号调理电路 3.2.0 可见光电信号前置放大电路 3.2.1 红外光电信号前置放大电路 3.2.2 程控自适应增益的光信号放大电路 3.2.3 光信号滤波电路 3.2.4 声信号调理电路 3.3 数据…

【图像处理】边缘检测算子有哪些以及它们之间的对比

这个问题应该是做过传统图像处理的人都接触过的吧。粗略总结下&#xff0c;应该也不会问太细&#xff0c;面试官大概就考察下大家的知识面吧。 综述 边缘检测是图像处理和计算机视觉中&#xff0c;尤其是特征提取中的一个研究领域。图像边缘检测大幅度地减少了数据量&#xf…

第五章. 可视化数据分析图表—常用图表的绘制3—散点图,面积图,热力图

第五章. 可视化数据分析图 5.3 常用图表的绘制3—散点图&#xff0c;面积图&#xff0c;热力图 本节主要介绍常用图表的绘制&#xff0c;主要包括散点图&#xff0c;面积图&#xff0c;热力图。 1.散点图&#xff08;matplotlib.pyplot.scatter&#xff09; 散点图主要用来查…

Redux Hooks actions 调用方式优化(一)

hooks 可以说是现在react编程的的主流&#xff0c;redux 迎合这个主流也推出 toolkit 工具集来。但是在用toolkit 搭建前端框架后&#xff0c;给人的感觉就是比原先的connect 那一套好些&#xff0c;但用起来还是挺繁琐的。 一 toolkit 搭建的正常使用版本 1.1 创建store im…

三、pcm音频转wav

前言 ffmpeg录制下来的音频为pcm格式&#xff08;内部存储着十六进制数据&#xff09;&#xff0c;但pcm格式的音频无法直接播放 本文先将pcm转换成wav格式&#xff08;提要提前了解音频知识&#xff09; 首先分析wav文件格式&#xff08;wav的本质是在pcm数据前加上文件头&a…

[附源码]Node.js计算机毕业设计电子工厂进销存管理系统Express

项目运行 环境配置&#xff1a; Node.js最新版 Vscode Mysql5.7 HBuilderXNavicat11Vue。 项目技术&#xff1a; Express框架 Node.js Vue 等等组成&#xff0c;B/S模式 Vscode管理前后端分离等等。 环境需要 1.运行环境&#xff1a;最好是Nodejs最新版&#xff0c;我…

《Linux运维总结:Centos7.6使用yum安装Mysql8.0.31》

一、使用yum安装mysql服务 官方&#xff1a;下载对应的yum源 1、yum源下载 [rootlocalhost ~]# wget https://dev.mysql.com/get/mysql80-community-release-el7-7.noarch.rpm [rootlocalhost ~]# rpm -ivh mysql80-community-release-el7-7.noarch.rpm如下图所示&#xff1a;…