切換語言為:簡體

MyBatisPlus與Netty整合全過程

  • 爱糖宝
  • 2024-09-04
  • 2051
  • 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.