반응형

myhappyman.tistory.com/172

 

Spring - 스프링 프로젝트에서 netty사용하기

기존에 운영중이던 프로젝트를 고도화 시키면서 특정 서비스단에서 전달된 값을 DB에 저장하고 저장된 정보를 바로 특정 소켓 서버로 전달을 해줘야 하는 기능을 작성해야 했습니다. 이미 tomcat

myhappyman.tistory.com

 

이전에 Controller부분에서 @PostConstruct 어노테이션을 활용하여 Thread를 생성하여 돌리고 서버를 따로 구동하는 형태로 작성하였는데, Bean등록을 하거나 @Component 어노테이션을 활용해서 등록하고 사용해보는 예제를 확인해보겠습니다.

Bean등록을 통한 설정

servlet-context.xml

<!-- Netty Server 등록 -->
<beans:bean id="nettyServer" class="package명.NettyServer" name="nettyServer"/>

정의한 NettyServer class파일을 등록하여 사용하면 됩니다.

 

@Component 어노테이션을 통한 설정

@Component
public class NettyServer {
    private static final Logger logger = Logger.getLogger(NettyServer.class);

    private final int SERVER_PORT = 15500;
    private final TestService testService;    

    @Autowired
    public NettyServer(TestService testService) {
        this.testService = testService;
    }

    private int SERVER_PORT;
    private ServerBootstrap sbs = new ServerBootstrap();
    private EventLoopGroup bossGroup;
    private EventLoopGroup workerGroup;

    @PostConstruct
    public void run() {
        logger.info(" ============================= Netty Server Start ============================= ");
        bossGroup = new NioEventLoopGroup(20);
        workerGroup = new NioEventLoopGroup(20);

        sbs.group(bossGroup, workerGroup)
        .channel(NioServerSocketChannel.class)
        .option(ChannelOption.SO_TIMEOUT, 5 * 1000)
        .childOption(ChannelOption.SO_TIMEOUT, 5 * 1000)
        .childHandler(new ChannelInitializer<SocketChannel>() {

            @Override
            protected void initChannel(SocketChannel ch) throws Exception {
                // 응답없는 read상태 확인하는 Handler
                ch.pipeline().addLast("idleStateHandler", new IdleStateHandler(5, 0, 0)); //아이들 상태

                // 메시지 송신, 수신, 응답없는 이벤트등을 처리할 Handler
                ch.pipeline().addLast("socketServerHandler", new NettyServerHandler(testService) ); //직접 동작 할 핸들러
            }
        });

        doConnect();
    }

    private void doConnect() {
        //서버는 Listen상태로 기다려야하는데, 톰캣이 Timeout이 발생함
        //이를 방지하기 위해 Thread로 처리한다.
        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    ChannelFuture future = sbs.bind(SERVER_PORT).sync();
                    future.channel().closeFuture().sync();
                } catch (InterruptedException e) {
                    logger.error("InterruptedException", e);
                }
            }
        }).start();
    }

    @PreDestroy
    public void serverDestroy() throws InterruptedException {
        logger.info("================ Netty BootStrapServer Destroy() ================");
        bossGroup.shutdownGracefully();
        workerGroup.shutdownGracefully();
        sbs.group().shutdownGracefully();
    }
}

xml설정이 어렵다면 작성한 classBean으로 등록해주는 @Component 어노테이션을 활용하면 보다 쉽게 설정이 가능합니다.

 

추가해주시고 service, dao등 설정이 필요한 부분은 생성자를 통해 주입해줍니다.

 

이후 @PostConstruct 어노테이션을 활용하여 ServerBootStrap 옵션을 설정하고 구동을 해줍니다.

doConnect()함수에서 쓰레드를 생성하고 특정 포트로 바인딩하도록 설정하였는데, @PostConstruct는 해당 함수가 끝날때까지 대기하도록 설계되어 있는데, 해당부분에서 Netty 서버는 Listen상태로 계속 기다리고 있게됩니다.

톰캣입장에서는 구동 완료되는 시간이 Timeout설정이 되어 있는데 Timeout시간이 될때까지 설정이 끝나질 않으니 Timeout 에러가 발생하게 됩니다. 이부분을 막기위해 Thread를 통해 별도로 구성을 했습니다.

 

마무리

아직 스프링부트 프로젝트를 제대로 해볼 일이 없어서 아직도 Legacy만 쓰고 있는데, xml의 설정이 늘 어렵고 복잡하며 헷갈린것 같습니다. 이번 글에서 주의할 점은 xml에서 Bean등록을 했다면 Component 어노테이션은 지워야하고, Component 어노테이션으로 등록을 했다면 xml에서 Bean등록을 삭제해주시면 됩니다. 둘 다 설정하여도 동작은 하겠지만 2중으로 등록되면서 오류가 발생할 수 있습니다.

반응형