반응형

웹서버와 자바 어플리케이션 간의 소켓 연결을 위해 Netty를 사용하고 있는데 특성상 연결이 끊길경우 반드시 자동 재접속을 시도를 해야 한다. 이것을 구현해보려고 많은 예시를 봐왔지만 딱히 해결 될 기미가 보이지 않던 찰나에 좋은 코드를 찾았고 이를 적용하니 정상적으로 대응이 되었다.

 

public final class UptimeClient {
  
      static final String HOST = System.getProperty("host", "127.0.0.1");
      static final int PORT = Integer.parseInt(System.getProperty("port", "8080"));
      // Sleep 5 seconds before a reconnection attempt.
      static final int RECONNECT_DELAY = Integer.parseInt(System.getProperty("reconnectDelay", "5"));
      // Reconnect when the server sends nothing for 10 seconds.
      private static final int READ_TIMEOUT = Integer.parseInt(System.getProperty("readTimeout", "10"));
  
      private static final UptimeClientHandler handler = new UptimeClientHandler();
      private static final Bootstrap bs = new Bootstrap();
  
      public static void main(String[] args) throws Exception {
          EventLoopGroup group = new NioEventLoopGroup();
          bs.group(group)
                  .channel(NioSocketChannel.class)
                  .remoteAddress(HOST, PORT)
                  .handler(new ChannelInitializer<SocketChannel>() {
                      @Override
                      protected void initChannel(SocketChannel ch) throws Exception {
                          ch.pipeline().addLast(new IdleStateHandler(READ_TIMEOUT, 0, 0), handler);
                      }
                  });
          bs.connect();
      }
  
      static void connect() {
          bs.connect().addListener(new ChannelFutureListener() {
              @Override
              public void operationComplete(ChannelFuture future) throws Exception {
                  if (future.cause() != null) {
                      handler.startTime = -1;
                      handler.println("Failed to connect: " + future.cause());
                  }
              }
          });
      }
  }

 

위의 예제의 경우 Netty Client 소스 코드를 Main에서 실행하도록 했지만 사실 Main 문을 그냥 하나의 메서드로 바꿔버린다음 다른 곳에서 호출하는 형식으로 써도 된다. 실제로 그렇게 쓰고 있고 적용이 잘 된다.

 

예를 들어 public static void main 을 public void NettyClient로 바꾼다음 다른 곳에서 NettyClient()로 바꾼다던지 말이다.

 

NettyClient의 실질적인 기능을 부여할 Handler의 경우 addLast를 통해 추가한다

 


  package io.netty.example.uptime;
  
  import io.netty.channel.ChannelHandler.Sharable;
  import io.netty.channel.ChannelHandlerContext;
  import io.netty.channel.SimpleChannelInboundHandler;
  import io.netty.handler.timeout.IdleState;
  import io.netty.handler.timeout.IdleStateEvent;
  
  import java.util.concurrent.TimeUnit;
  
 /**
   * Keep reconnecting to the server while printing out the current uptime and
   * connection attempt getStatus.
   */
  @Sharable
  public class UptimeClientHandler extends SimpleChannelInboundHandler<Object> {
  
      long startTime = -1;
  
      @Override
      public void channelActive(ChannelHandlerContext ctx) {
          if (startTime < 0) {
              startTime = System.currentTimeMillis();
          }
          println("Connected to: " + ctx.channel().remoteAddress());
      }
  
      @Override
      public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
          // Discard received data
      }
  
      @Override
      public void userEventTriggered(ChannelHandlerContext ctx, Object evt) {
          if (!(evt instanceof IdleStateEvent)) {
              return;
          }
  
          IdleStateEvent e = (IdleStateEvent) evt;
          if (e.state() == IdleState.READER_IDLE) {
              // The connection was OK but there was no traffic for last period.
              println("Disconnecting due to no inbound traffic");
              ctx.close();
          }
      }
  
      @Override
      public void channelInactive(final ChannelHandlerContext ctx) {
          println("Disconnected from: " + ctx.channel().remoteAddress());
      }
  
      @Override
      public void channelUnregistered(final ChannelHandlerContext ctx) throws Exception {
          println("Sleeping for: " + UptimeClient.RECONNECT_DELAY + 's');
  
          ctx.channel().eventLoop().schedule(new Runnable() {
              @Override
             public void run() {
                  println("Reconnecting to: " + UptimeClient.HOST + ':' + UptimeClient.PORT);
                  UptimeClient.connect();
              }
          }, UptimeClient.RECONNECT_DELAY, TimeUnit.SECONDS);
      }
  
      @Override
      public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
          cause.printStackTrace();
          ctx.close();
      }
  
      void println(String msg) {
          if (startTime < 0) {
              System.err.format("[SERVER IS DOWN] %s%n", msg);
          } else {
              System.err.format("[UPTIME: %5ds] %s%n", (System.currentTimeMillis() - startTime) / 1000, msg);
          }
      }
  }

Netty에서 많은 기능을 제공하지만 재접속 부분만 보자면 channelUnregistered를 보면 된다.

 

channelUnregistered에서 EventLoop를 통해 꾸준히 Connection을 시도하는 것을 확인할 수 있다.

 

이렇게 적용했을때 단순 연결 끊김 외에도 유령 세션에도 대응할 수 있었다. 

반응형

!!!! 주의 : 윈도우 10에서만 작동이 보증되며 타 운영체제(특히 윈도우11)에서는 작동을 보증할 수 없습니다

 

요즘 자바를 한창 하고 있는데 git으로 파일을 관리 중이다.

 

SourceTree라는 아주 좋은 관리 툴이 있어서 이걸 애용하고 있는데 집에서 이걸 설치하니 자꾸 CredentialHelperSelector가 뜬다. 

 

 

여기서 다른건 왠지 건드리면 안될것 같아서 no helper 선택하고 선택을 해도 자꾸 뜨고 Always use this from now on 체크 해도 자꾸 뜬다. 근데 이게 항상 위로 떠버려서 작업 하다가 방해가 이만저만이 아니다.

 

해결 방법 알아보던 중 좋은 팁이 있어서 공유를 해본다. 별도의 응용프로그램을 설치 하지 않고 간단히 설정만으로 해결 할 수 있다.

 

1. Windows10 검색창에서 powershell을 관리자 모드로 실행한다

 

 

 

2. powershell에서 아래의 명령어를 입력한뒤 엔터를 친다

 

cd $env:LOCALAPPDATA\Atlassian\SourceTree\git_local\mingw32\bin\

 

3. 아래의 문구를 입력한 뒤 엔터를 친다

 

./git.exe config --edit --system

 

4. 아래의 화면에서 'helper = manager'로 변경한다

 

아마 가장 아랫줄에 있는 helper의 값이 다른 값으로 되어있을것인데 이걸 manager로 바꾼다.

 

편집을 위해서는 키보드 i 키를 누르면 --INSERT-- 라는 문구가 뜰텐데 이게 편집 가능한 상태이다.

 

이 상태에서 수정하면 된다. 

 

그러고 난뒤 Esc 키를 한번 누르고 :wq를 입력한뒤 엔터를 친다. 이렇게 하면 저장을 한다.

 

 

 


위의 과정을 따라하면 기존 CredentialHelperSelector가 더이상 뜨질 않을 것이다.

 

나 같은 경우는 더이상 저것이 뜨지 않는다. 

'Programming Languages > Java' 카테고리의 다른 글

Netty Client Reconnect(재접속) 로직 예시  (1) 2022.04.28

+ Recent posts