springboot整合netty(二)


前言

上一篇講了netty的一個入門的demo;項目上我也把數據處理做好了,就要開始存數據庫了;我用的mybatis框架,如果單獨使用還是覺得比較麻煩,所以就用了springboot+mybatis+netty;本篇主要講netty與springboot的整合,以及我在這個過程中遇到的問題,又是怎么去解決的;

正文

我在做springboot與netty整合的時候在谷歌,百度找了無數文章,都沒有一篇是自己想要的,也達不到自己所想的目的;

代碼

1. 新建一個springboot項目,在pom文件中添加netty依賴:

        <dependency>
			<groupId>io.netty</groupId>
			<artifactId>netty-all</artifactId>
			<version>5.0.0.Alpha1</version>
		</dependency>

2.新建netty服務

其實可以復制上一篇文章的netty的三個服務類,做一些稍微的修改就行了;這里為了方便演示,且修都是改好了的,就直接貼出來了;

  1. DiscardServer類:
@Component
public class DiscardServer {
    @Resource
    private ChildChannelHandler childChannelHandler;
    public void run(int port) throws Exception {
        EventLoopGroup bossGroup = new NioEventLoopGroup();
        EventLoopGroup workerGroup = new NioEventLoopGroup();
        System.out.println("准備運行端口:" + port);
        try {
            ServerBootstrap bootstrap = new ServerBootstrap();
            bootstrap.group(bossGroup, workerGroup)
                    .channel(NioServerSocketChannel.class)
                    .option(ChannelOption.SO_BACKLOG, 128)
                    .childHandler(childChannelHandler);
            //綁定端口,同步等待成功
            ChannelFuture f = bootstrap.bind(port).sync();
            //等待服務監聽端口關閉
            f.channel().closeFuture().sync();
        } finally {
            //退出,釋放線程資源
            workerGroup.shutdownGracefully();
            bossGroup.shutdownGracefully();
        }
    }
}

  1. ChildChannelHandler類
@Component
public class ChildChannelHandler extends ChannelInitializer<SocketChannel> {
    @Resource
    private DiscardServerHandler discardServerHandler;

    public void initChannel(SocketChannel socketChannel) throws Exception {
        socketChannel.pipeline().addLast(discardServerHandler);
    }
}

3.DiscardServerHandler類

特別注意DiscardServerHandler類上需要加@Sharable注解,如果不加的話會報錯;

@Component
@Sharable
public class DiscardServerHandler extends ChannelHandlerAdapter {
    @Resource
    private BaseService baseService;
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) {

        try {
            ByteBuf in = (ByteBuf) msg;
            System.out.println("傳輸內容是");
            System.out.println(in.toString(CharsetUtil.UTF_8));
            //這里調用service服務
            baseService.test();
        }  finally {
            ReferenceCountUtil.release(msg);
        }
    }
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        // 出現異常就關閉
        cause.printStackTrace();
        ctx.close();
    }
}

3.netty調用所需的服務類

1.BaseService接口

public interface BaseService {
    /**
     * 測試接口
     */
    void test();
}

2.接口實現類BaseServiceImpl:

@Service
public class BaseServiceImpl implements BaseService {
    @Override
    public void test() {
        System.out.println("調用service服務");
    }
}

4 springboot啟動類

  1. 由於main方法是靜態方法,netty服務啟動類不是靜態類,在main方法里面需要用new的方式啟動;
  2. 也可以將netty服務啟動類改為靜態類,然后調用其他非靜態的類時就得用new方法來構造其他類了;

我也百度到了幾篇文章說實現CommandLineRunner接口,所以我用了springboot啟動類實現CommandLineRunner接口的run方法,然后在run方法里啟動netty服務

@SpringBootApplication
public class DemoApplication implements CommandLineRunner {
	@Resource
	private DiscardServer discardServer;

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

	@Override
	public void run(String... args) throws Exception {
		discardServer.run(8080);
	}
}

5.測試

寫一個能發送數據的socket就可以了;

發送的數據為:

public static void main(String[] args){
        try {
            Socket socket=new Socket("localhost",8080);
            OutputStream outputStream = socket.getOutputStream();
            PrintWriter printWriter=new PrintWriter(outputStream);
            printWriter.write("$tmb00035ET3318/08/22 11:5804029.94,027.25,20.00,20.00$");
            printWriter.flush();
            socket.shutdownOutput();
            socket.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

我的測試結果:

傳輸內容是
$tmb00035ET3318/08/22 11:5804029.94,027.25,20.00,20.00$
aaaaa

到這里,netty與springboot的整合就完成了;

我在整合過程中遇到的問題

我使用springboot結合netty的流程

springboot啟動類中啟動netty啟動類(DiscardServer),netty啟動類(DiscardServer)再調用初始化channel類(ChildChannelHandler),然后初始化channel類再調用(DiscardServerHandler)類;然后DiscardServerHandler類再調用service服務;如下示例圖:

avatar

問題

  1. springboot啟動類我並沒有實現CommandLineRunner接口,直接在main方法通過new的方式啟動netty服務
  2. 我實現了CommandLineRunner接口,但是我在run方法中用的new的方式啟動的netty服務或者我在run方法使用注入的方式啟動netty,但是在其他某個地方調用另一個類使用了new的方式;
  3. DiscardServerHandler類上為標記@Sharable類,會報錯誤;

以上總結起來的問題就是我在springboot整合netty的過程中有其中一處的調用其他類時使用的方式是new構造的,這樣雖然springboot能啟動,netty也能啟動,但是netty服務中使用new構造的那個類中無法依賴注入,會報空指針異常;

舉個栗子:在圖中的過程中,我在ChildChannelHandler類中通過new的方式調用DiscardServerHandler類,其他的過程都是使用注入的方式調用,就會出現上邊的問題;

在遇到空指針的時候,我把spring托管的bean打印了出來,所有的類都在spring的托管中,但是就是無法注入,我也一直沒有明白怎么回事,最后用了一個極端的方法,就是在調用服務時,獲取spring的上下文,然后再根據名字來獲取bean,你谷歌或百度:非托管類調用spring托管類,就能找到很多文章了;雖然用這個方式能解決上述的問題,但總是不好的;

最后的解決辦法:所以類之間的調用都使用spring的依賴注入,別用new的方式來調用或者靜態方法的方式調用

總結

既然項目中用到了spring,那么類與類之間的調用就用依賴注入,不然會報空指針的問題(就是非托管對象調用spring托管對象);這也算是一個常識性的問題了,只是自己現在才遇到這樣的問題,還是要踩坑才能遇漲記性啊;這些問題困擾了我兩三天,還是要有經驗的人帶,如果有經驗的人帶的話,說不幾分鍾就搞定了;

netty的文章到這里就告一段落了,接下來就是趕項目了;哈哈;

GitHub項目地址:


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM