리액터 패턴

1 분 소요

리액터 패턴

이벤트 처리(event handling) 디자인 패턴이다. JAVA TCP 통신에서 클라이언 수 만큼 Thread를 생성한다.
생성된 Thread에서 동시에 여러개의 요청이 들어올 경우 context switching이 빈번히 발생하게 되는데 리액터 패턴은
event handler에게 역할을 위임하여 이러한 문제를 해결한다.

에코 채팅 서버

public class Main {
    public static final int PORT = 5000;
    
    public void main(String[] args) {
        Reactor reactor = new Reactor(PORT);
        
        reactor.registerHandler(new ReactorEventHandler());
        reactor.startServer();
    }
}
public class Reactor {
    private ServerSocket serverSocket;
    private HandleMap handleMap;
    
    public Reactor(int port) {
        handleMap = new HandleMap();
        try {
            serverSocket = new ServerSocket(port);
        } catch (IOException e){
          e.printStackTrace();
        }
    }
    
    public void startServer() {
        ThreadPoolDispatcher dispatcher = new ThreadPoolDispatcher();
        dispatcher.dispatch(serverSocket, handleMap);
    }
    
    public void registerHandler(EventHandler handler) {
        handleMap.put(handler.getHandler(), handler);
    }
    
    public void removeHandler(EventHandler handler) {
        handleMap.remove(handler.getHandler());
    }
}
public interface Dispatcher {
    public void dispatch(ServerSocket serverSocket, HandleMap handleMap);
}
public class HandleMap extends HashMap<String, EventHandler> {
    private static final long serialVersionUID = 1L;
}
public class Demultiplexer implements Runnable {
	private final int HEADER_SIZE = 6;

	private Socket socket;
	private HandleMap handleMap;
	
	public Demultiplexer(Socket socket, HandleMap handleMap) {
		this.socket = socket;
		this.handleMap = handleMap;
	}

	@Override
	public void run() {
		InputStream inputStream = null;
		try {
			byte[] headerBuffer = new byte[HEADER_SIZE]; 
			inputStream = socket.getInputStream();
			
			inputStream.read(headerBuffer);
			String header = new String(headerBuffer);

			handleMap.get(header).handleEvent(inputStream);
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			if (inputStream != null) {
				try {
					inputStream.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
			
			if (socket != null) {
				try {
					socket.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}
	}
}
public class ThreadPoolDispatcher implements Dispatcher {

	static final String NUM_THREADS = "8";
	static final String THREAD_PROP = "Threads";

	private int numThreads;

	public ThreadPoolDispatcher() {
		numThreads = Integer.parseInt(System.getProperty(THREAD_PROP, NUM_THREADS));
	}

	@Override
	public void dispatch(final ServerSocket serverSocket, final HandleMap handleMap) {
		for (int idx_thread = 0; idx_thread < (numThreads - 1); idx_thread++) {
			Thread thread = new Thread() {
				public void run() {
					dispatchLoop(serverSocket, handleMap);
				}
			};
			thread.start();
		}		
		dispatchLoop(serverSocket, handleMap);
	}

	private void dispatchLoop(ServerSocket serverSocket, HandleMap handleMap) {
		while (true) {
			try {
				Socket socket = serverSocket.accept();

				Demultiplexer demultiplexer = new Demultiplexer(socket, handleMap);
				Thread thread = new Thread(demultiplexer);

				thread.start();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	}
}