切换语言为:繁体

MyBatisPlus与Netty整合全过程

  • 爱糖宝
  • 2024-09-04
  • 2050
  • 0
  • 0

前言

由于工作需要需要解析设备传输的协议,然后存入数据库,但是Netty整合mybatisplus遇到了不少问题,网上的博客都或多或少有点问题,于是记录下来这次整合

Netty和spring的关系

Netty 是一个独立的网络编程框架,它不依赖于 Spring 框架,因此 Netty 的组件不自动注册到 Spring 的 IOC 容器中。

Netty 的 ChannelHandler 实例是由 Netty 的 pipeline 创建和管理的

由于ioc无法管理,导致我前期踩坑不少

整合

maven

<dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

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

        <!--fastjson-->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.17</version>
        </dependency>

        <!--kafka依赖-->
        <dependency>
            <groupId>org.springframework.kafka</groupId>
            <artifactId>spring-kafka</artifactId>
        </dependency>

        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-all</artifactId>
            <version>5.7.6</version>
        </dependency>

        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.4.3</version>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>


    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration>
                    <excludes>
                        <exclude>
                            <groupId>org.projectlombok</groupId>
                            <artifactId>lombok</artifactId>
                        </exclude>
                    </excludes>
                </configuration>
            </plugin>
        </plugins>
    </build>

netty启动类修改

我采用的是springboot  配置bean的方式启动netty

netty启动方式

1. 实现ApplicationRunner

@Slf4j
@Component
@Order(1)
public class EPServer implements ApplicationRunner {

    /**
     * Netty服务端监听的端口号
     */
    public static final int PORT = 9999;
    // 创建两个EventLoopGroup,boss:处理连接事件,worker处理I/O事件

    /**
     * 分发线程组  处理连接事件
     */
    private static final EventLoopGroup bossGroup = new NioEventLoopGroup(2);

    /**
     * 工作线程组 处理I/O事件
     */
    private static final EventLoopGroup workerGroup = new NioEventLoopGroup(4);
    static MessageCode MESSAGE_CODEC = new MessageCode();



    /**
     * 启动服务
     */
    public static void runEPServer(){
        log.info("===== EP Netty Server start =====");
        try{
            // 创建一个ServerBootstrap服务端(同之前的ServerSocket类似)
            ServerBootstrap b = new ServerBootstrap();
            //创建事件循环组  将前面创建的两个EventLoopGroup绑定在server上
            b.group(bossGroup, workerGroup);
            // 指定服务端的通道为Nio类型
            b.channel(NioServerSocketChannel.class);
            // 为到来的客户端Socket添加处理器
            b.childHandler(new ChannelInitializer<SocketChannel>() {
                @Override
                public void initChannel(SocketChannel ch) {
                    ChannelPipeline pipeline = ch.pipeline();

                    // netty提供了空闲状态监测处理器 0表示禁用事件
                    pipeline.addLast(new MessageCode());
                    pipeline.addLast(new EPServerHandler());
                }
            });
            log.info("环保 Netty Server PORT = " + PORT);
            b.bind(PORT).sync();

        }catch (Exception e){
            e.printStackTrace();
            shutdown();
        }
    }

    /**
     * 关闭服务
     */
    public static void shutdown(){
        // 优雅关闭
        workerGroup.shutdownGracefully();
        bossGroup.shutdownGracefully();
    }

    @Override
    public void run(ApplicationArguments args) {
        // 启动环保监测Netty服务端
        runEPServer();
    }

}

2. springboot  配置bean的方式启动netty

在启动方法上添加@PostConstruct注解

@Slf4j
@Component
public class EPServerNew {

    /**
     * Netty服务端监听的端口号
     */
    public static final int PORT = 9999;
    // 创建两个EventLoopGroup,boss:处理连接事件,worker处理I/O事件

    /**
     * 分发线程组  处理连接事件
     */
    private static final EventLoopGroup bossGroup = new NioEventLoopGroup(2);

    /**
     * 工作线程组 处理I/O事件
     */
    private static final EventLoopGroup workerGroup = new NioEventLoopGroup(4);
    static MessageCode MESSAGE_CODEC = new MessageCode();


    @Autowired
    private EPServerHandler epServerHandler;


    /**
     * 启动服务
     */

    @PostConstruct
    public void start() throws InterruptedException {
        ServerBootstrap b=new ServerBootstrap();
        b.group(bossGroup, workerGroup)
                .channel(NioServerSocketChannel.class)
                .option(ChannelOption.SO_BACKLOG,128)
                .childHandler(new ChannelInitializer<SocketChannel>() {
                    @Override
                    protected void initChannel(SocketChannel socketChannel) throws Exception {
                        socketChannel.pipeline().addLast(new MessageCode());
                        socketChannel.pipeline().addLast(epServerHandler);
                    }
                });
        ChannelFuture future = b.bind(PORT).sync();
        if (future.isSuccess()) {
            System.out.println("启动 Netty 成功");
        }
    }



    /**
     * 关闭服务
     */
    @PreDestroy
    public static void shutdown(){
        // 优雅关闭
        workerGroup.shutdownGracefully();
        bossGroup.shutdownGracefully();
    }

}

两种启动方式对比

使用 CommandLineRunner 接口:

  • 优点:

    • 易于实现:只需创建一个实现 CommandLineRunner 的类,并override run 方法以启动 Netty 服务。

    • 灵活性:您可以在 run 方法中执行任何必要的设置或清理任务。

  • 缺点:

    • 控制有限:CommandLineRunner 接口旨在运行命令行应用程序,因此您对 Netty 服务的生命周期控制有限。

    • 无法管理 bean:Netty 服务不是作为 Spring bean 管理的,因此您无法 inject 依赖项或使用 Spring 的生命周期管理功能。

配置 bean 来启动 Netty 服务:

  • 优点:

    • 更好的控制:通过配置 bean 来启动 Netty 服务,您对服务的生命周期有更多的控制权,并可以使用 Spring 的生命周期管理功能。

    • 依赖注入:您可以将依赖项 inject 到 Netty 服务 bean 中,使得测试和维护变得更加容易。

  • 缺点:

    • 更复杂:您需要创建一个配置类,并定义一个 bean 来启动 Netty 服务,这可能比实现 CommandLineRunner 更复杂。

    • 紧耦合:Netty 服务 bean 紧耦合到 Spring 应用程序上下文中,这可能使得测试或在非 Spring Boot 应用程序中使用变得更加困难。


application  配置文件

spring:
  datasource:
    url: jdbc:mysql://127.0.0.1:33306/sums_hfcyjs?characterEncoding=utf-8&serverTimezone=UTC
    username: root
    password: root
    driver-class-name: com.mysql.cj.jdbc.Driver

#开启日志
mybatis-plus:
  configuration:
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl

启动类

@SpringBootApplication
@EnableScheduling
@MapperScan("com.dhj.hj212.mapper")

public class Hj212Application {

    public static void main(String[] args) {
        SpringApplication.run(Hj212Application.class, args);
    }

}

具体包名根据自己的实际情况修改

EPServerHandler类

这个类加了@Component@Sharable注解

在这里我们就可以注入mybatisplus的mapper,然后执行insert操作

@Sharable注解解释

在 Netty 中,ChannelHandler 是一个对象,它处理通道的入站和出站事件。默认情况下,每个 ChannelHandler 实例都是为每个通道单独创建的,这意味着每个通道都有其自己的处理器实例。

但是,在某些情况下,您可能想跨多个通道共享同一个 ChannelHandler 实例。这就是 @ChannelHandler.Sharable 注解的用途。

当您在 ChannelHandler 类上使用 @ChannelHandler.Sharable 注解时,它表明该处理器实例可以安全地跨多个通道共享。这意味着 Netty 将重用同一个处理器实例 для多个通道,而不是为每个通道创建一个新的实例。

使用 @ChannelHandler.Sharable 的一些含义是:

  1. 线程安全:由于处理器实例是共享的,因此它必须是线程安全的。这意味着处理器的状态必须被仔细地管理,以确保它可以被多个线程安全地访问和修改。

  2. 通道独立:由于处理器实例是共享的,因此它不能维护任何通道特定的状态。这意味着处理器不能存储任何通道特定的信息,例如通道的 ID 或地址。

  3. 性能:共享同一个处理器实例跨多个通道可以提高性能,因为它减少了对象创建和垃圾回收的数量。

@Slf4j
@Component
@ChannelHandler.Sharable
public class EPServerHandler extends ChannelInboundHandlerAdapter   {

    /**
     * 定义一个HashMap,用于保存所有的channel和设备ID的对应关系。
     */
    private static Map deviceInfo = new HashMap(64);

    @Autowired
    private SepticHandheldMapper septicHandheldMapper;


    /**
     * 消息读取
     */
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws UnsupportedEncodingException {
        log.info("=============" + (new java.text.SimpleDateFormat("yyyy-MM-dd HH:mm:ss")).format(new Date()) + "=============");
        if (msg instanceof ByteBuf) {
            ByteBuf byteBuf = (ByteBuf) msg;
            log.info("最后总收到数据: {}", ByteBufUtil.hexDump(byteBuf));
            log.info("开始解析数据");
           ........
              // 这里进行插入操作
               int insert = septicHandheldMapper.insert(septicHandheld);

mybatisplus

整合mybatisplus可以参考官方文档这个比较简单

总结

问题就是把netty交给spring去管理,然后我们就可以在里面注入mapper,实现数据插入的操作

0条评论

您的电子邮件等信息不会被公开,以下所有项均必填

OK! You can skip this field.