[CS] [Blocking I/O, Non-blocking I/O] X [Sync, Async]
웹서비스에서는 API 서비스를 개발 및 요청하다 보면 Sync/Async 개념을 접했을 것이다. 업무나 기능에 따라 무의식적으로 Blocking, Non-Blocking 를 처리했을 수도 있다.
금융권에서도 내부시스템간/대외계 간에 EAI나 MCI를 인터페이스를 통해서 요청하기 때문에 아래 4가지 구분으로 설정값으로 업무에 따라 구현되고 있다.
그 외 여러 기능에서 멀티프로세스, 멀티쓰레드 등 연관성이 있다.
여기서 I/O란? 데이터의 입력(Input)과 출력(Output)을 말한다. I/O는 파일 입출력뿐만 아니라 이기종간 네트워크를 통해서 데이터를 주고 받는 스트림들도 I/O라 칭한다.
1. Blocking I/O 와 Non-blocking I/O 설명
1-1. Blocking I/O
- Blocking I/O 는 기본적으로 Synchronous 이다.
- 어플리케이션에서 커널에게 read I/O를 요청하면
커널에서 제어권
을 가져감- 커널에서 read 작업이 끝나고 응답을 줄때까지
어플리케이션은 Block(기다림)
- 그 동안 어플리케이션은 다른 작업을 못함
- 작업 완료
1-2. Non-blocking I/O
- 어플리케이션에서 커널에게 read I/O를 요청하지만
어플리케이션에서 계속 제어권
을 가지고 있음어플리케이션은 기다리지 않고(Non-Block)
커널괴 각자 자신의 작업을 처리함- 어플리케이션은 다른 작업을 하면서 시스템 콜을 보내서 커널에 요청한 I/O가 완료되었는지 물어봄
- 작업 완료
2. [Blocking I/O, Non-blocking I/O] X [Sync, Async]
Blocking I/O 와 Non-blocking I/O 는 Async/Sync 따라 아래와 같이 4가지 케이스로 구분되어 있다.
Blocking Non-Blocking Sync Blocking/Sync Non-Blocking/Sync Async Blocking/Async AIO
(Non-Blocking/Async)Bloking/Non-Blocking
구분 : 어플리케이션에서다른 주체(커널 또는 타시스템)
에 I/O를 요청하면, 요청된다른 주체가 제어권을 가지는 여부
이다.Blocking I/O
: 자신의 프로세스가 작업을 진행하다가 다른 주체에 I/O를 요청하면요청받은 I/O가 제어권을 가져가고
, I/O요청이 끝날때까지 기다렸다가 자신의 작업을 처리Non-Blocking I/O
: 자신의 프로세스가 다른 주체에 I/O를 요청해도,자신의 프로세스가 제어권을 가지고
다른 주체의 작업과 상관없이 자신의 작업을 진행 하는것
Synchronous(동기)
: 작업을 요청하고 기다리다가 결과를 리턴해주면 바로 결과를 처리
한다. Asynchronous(비동기)
: 작업을 요청하고 다른 작업을 하다가 결과를 리턴(Notification)
해주면 콜백함수(를 통해서) 결과를 처리한다.(바로 처리하는 개념은 아님)
2-1. Sync Blocking I/O
Blocking
: I/O 호출이 발생하면 커널의 I/O 작업이 완료될때까지 제어권을 커널이 소유, 자신의 프로세스는 I/O요청이 끝날 떄까지 다른 작업을 할 수 없다.Sync
: 커널 I/O 작업이 완료되면 해당 결과를 어플리케이션에서 리턴 받아서 직접 바로 처리한다.
SyncBlocking.java (Sync + Blocking)
public static void main(String[] args) throws Exception {
System.out.println("## Application 클라이언트 요청 시작 ##");
/* Sync(동기) 처리 역할: 콜백을 이용해서 타시스템 API 또는 프로세트 개념으로 생각 */
//아래 콜백(커널(또는 B시스템))이 제어권을 가지기 때문에 Block
Callee callee = new Callee();
callee.setCallback(new Callee.CallBack() {
@Override
public void getApiBOK(Callee callee) {
if (callee.getReturnYn() == false) {
System.out.println(" 커널(또는 B시스템)이 제어권 소유 - 요청/작업 등 대기 중");
}
}
});
callee.syncStart();
System.out.println("커널(또는 B시스템)에서 작업요청 받은 결과 직접 바로 작업 처리");
System.out.println("결과 값 (계좌번호): " + callee.getAccountNumber());
System.out.println("---------------------------------------------");
System.out.println("## Application 작업 완료 후에 클라이언트 요청 종료 ##");
System.out.println("---------------------------------------------");
}
결과
## Application 클라이언트 요청 시작 ##
커널(또는 B시스템)이 제어권 소유 - 요청/작업 등 대기 중
커널(또는 B시스템)이 제어권 소유 - 요청/작업 등 대기 중
커널(또는 B시스템)이 제어권 소유 - 요청/작업 등 대기 중
커널(또는 B시스템)이 제어권 소유 - 요청/작업 등 대기 중
커널(또는 B시스템)이 제어권 소유 - 요청/작업 등 대기 중
커널(또는 B시스템)이 제어권 소유 - 요청/작업 등 대기 중
커널(또는 B시스템)이 제어권 소유 - 요청/작업 등 대기 중
커널(또는 B시스템)이 제어권 소유 - 요청/작업 등 대기 중
커널(또는 B시스템)이 제어권 소유 - 요청/작업 등 대기 중
커널(또는 B시스템)이 제어권 소유 - 요청/작업 등 대기 중
커널(또는 B시스템)이 제어권 소유 - 요청/작업 등 대기 중
커널(또는 B시스템)이 제어권 소유 - 요청/작업 등 대기 중
커널(또는 B시스템)이 제어권 소유 - 요청/작업 등 대기 중
커널(또는 B시스템)에서 작업요청 받은 결과 직접 바로 작업 처리
결과 값 (계좌번호): 1002-45-123145
---------------------------------------------
## Application 작업 완료 후에 클라이언트 요청 종료 ##
---------------------------------------------
2-2. Async Blocking I/O
Blocking
: I/O 호출이 발생하면 커널의 I/O 작업이 완료될때까지 제어권을 커널이 소유, 자신의 프로세스는 I/O요청이 끝날 떄까지 다른 작업을 할 수 없다.Async
: 커널 I/O 작업이 완료되면 결과를리턴(Noti)
해주면 해당 결과를 콜백함수를 호출을 통해서 처리한다.(바로 처리하는 개념은 아님)
AsyncBlocking.java (Async Blocking I/O)
public static void main(String[] args) throws Exception {
System.out.println("## Application 클라이언트 요청 시작 ##");
/* Sync(동기) 처리 역할: 콜백을 이용해서 타시스템 API 또는 프로세스 개념으로 생각 */
//아래 콜백(커널(또는 B시스템))이 제어권을 가지기 때문에 Block
Callee callee = new Callee();
callee.setCallback(new Callee.CallBack() {
@Override
public void getApiBOK(Callee callee) {
if (callee.getReturnYn() == false) {
System.out.println(" 커널(또는 B시스템)이 제어권 소유 - 요청/작업 등 대기 중");
} else {
System.out.println(" 커널(또는 B시스템)에서 작업요청 결과 콜백함수 호출로 처리(바로 처리는 안함)");
System.out.println(" 결과 값 (계좌번호): " + callee.getAccountNumber());
}
}
});
callee.syncStart();
System.out.println("---------------------------------------------");
System.out.println("## Application 작업 완료 후에 클라이언트 요청 종료 ##");
System.out.println("---------------------------------------------");
}
결과
## Application 클라이언트 요청 시작 ##
커널(또는 B시스템)이 제어권 소유 - 요청/작업 등 대기 중
커널(또는 B시스템)이 제어권 소유 - 요청/작업 등 대기 중
커널(또는 B시스템)이 제어권 소유 - 요청/작업 등 대기 중
커널(또는 B시스템)이 제어권 소유 - 요청/작업 등 대기 중
커널(또는 B시스템)이 제어권 소유 - 요청/작업 등 대기 중
커널(또는 B시스템)이 제어권 소유 - 요청/작업 등 대기 중
커널(또는 B시스템)이 제어권 소유 - 요청/작업 등 대기 중
커널(또는 B시스템)이 제어권 소유 - 요청/작업 등 대기 중
커널(또는 B시스템)이 제어권 소유 - 요청/작업 등 대기 중
커널(또는 B시스템)이 제어권 소유 - 요청/작업 등 대기 중
커널(또는 B시스템)이 제어권 소유 - 요청/작업 등 대기 중
커널(또는 B시스템)이 제어권 소유 - 요청/작업 등 대기 중
커널(또는 B시스템)이 제어권 소유 - 요청/작업 등 대기 중
커널(또는 B시스템)에서 작업요청 결과 콜백함수 호출로 처리(바로 처리는 안함)
결과 값 (계좌번호): 1002-45-123145
---------------------------------------------
## Application 작업 완료 후에 클라이언트 요청 종료 ##
---------------------------------------------
2-3. Sync Non-Blocking I/O
Non-Blocking
: 커널의 I/O 작업을 요청해도 어플리케이션이 제어권을 소유하고, 어플리케이션과 커널은 각각 다른 작업을 할 수 있다.Sync
: 어플리케이션에서 커널 I/O 작업의 완료여부를시스템 콜
을 통해서 커널에게 묻는다. 해당 결과를 어플리케이션에서 리턴 받아서 직접 바로 처리하기 때문에 지속적으로 커널에게 물어본다.
SyncNonBlocking.java (Sync Non-Blocking I/O)
public static void main(String[] args) throws Exception {
System.out.println("## Application 클라이언트 요청 시작 ##");
/* 쓰레드와 콜백을 이용해서 타시스템 API 역할로 생각 */
Callee callee = new Callee();
callee.setCallback(new Callee.CallBack() {
@Override
public void getApiBOK(Callee callee) {
//Nothing...
}
});
callee.start();
while (! callee.systemCall()) {//커널(또는 B시스템) 시스템 콜
Thread.sleep(2000);
if (callee.getReturnYn()) {
System.out.println("커널(또는 B시스템)에서 작업요청 받은 결과 직접 바로 작업 처리");
System.out.println("결과 값 (계좌번호): " + callee.getAccountNumber());
} else {
System.out.println("Application 제어권 소유 - 작업 진행 또는 커널(또는 B시스템) 결과 대기 중...");
}
}
System.out.println("---------------------------------------------");
System.out.println("## Application 작업 완료 후에 클라이언트 요청 종료 ##");
System.out.println("---------------------------------------------");
}
결과
## Application 클라이언트 요청 시작 ##
>> 커널(또는 시스템) 시스템 콜 (작업진행상태) [ 0 %]
Application 제어권 소유 - 작업 진행 또는 커널(또는 B시스템) 결과 대기 중...
>> 커널(또는 시스템) 시스템 콜 (작업진행상태) [ 15 %]
Application 제어권 소유 - 작업 진행 또는 커널(또는 B시스템) 결과 대기 중...
>> 커널(또는 시스템) 시스템 콜 (작업진행상태) [ 30 %]
Application 제어권 소유 - 작업 진행 또는 커널(또는 B시스템) 결과 대기 중...
>> 커널(또는 시스템) 시스템 콜 (작업진행상태) [ 46 %]
Application 제어권 소유 - 작업 진행 또는 커널(또는 B시스템) 결과 대기 중...
>> 커널(또는 시스템) 시스템 콜 (작업진행상태) [ 61 %]
Application 제어권 소유 - 작업 진행 또는 커널(또는 B시스템) 결과 대기 중...
>> 커널(또는 시스템) 시스템 콜 (작업진행상태) [ 69 %]
Application 제어권 소유 - 작업 진행 또는 커널(또는 B시스템) 결과 대기 중...
>> 커널(또는 시스템) 시스템 콜 (작업진행상태) [ 84 %]
커널(또는 B시스템)에서 작업요청 받은 결과 직접 바로 작업 처리
결과 값 (계좌번호): 1002-45-123145
>> 커널(또는 시스템) 시스템 콜 (작업진행상태) [ 100 %]
---------------------------------------------
## Application 작업 완료 후에 클라이언트 요청 종료 ##
---------------------------------------------
2-4. Aync Non-Blocking I/O
Non-Blocking
: 커널의 I/O 작업을 요청해도 어플리케이션이 제어권을 소유하고, 어플리케이션과 커널은 각각 다른 작업을 할 수 있다.Async
: 커널 I/O 작업이 완료되면 결과를리턴(Noti)
해주면 해당 결과를 콜백함수를 호출을 통해서 처리한다.(바로 처리하는 개념은 아님)
AsyncNonBlocking.java (Aync Non-Blocking I/O)
public static void main(String[] args) throws Exception {
System.out.println("## Application 클라이언트 요청 시작 ##");
/* 쓰레드와 콜백을 이용해서 타시스템 API 역할로 생각 */
Callee callee = new Callee();
callee.setCallback(new Callee.CallBack() {
@Override
public void getApiBOK(Callee callee) {
if (callee.getReturnYn() == false) {
System.out.println(" 커널(또는 B시스템) 요청/작업 등 대기 중");
} else {
System.out.println(" 커널(또는 B시스템)에서 작업요청 결과 콜백으로 처리(바로 처리는 안함)");
System.out.println(" 결과 값 (계좌번호): " + callee.getAccountNumber());
}
}
});
callee.start();
for (int i=10; i>0; i--) {
System.out.println("Application 제어권 소유 - 작업 진행");
try {
Thread.sleep(1000);
} catch (Exception e) {}
}
System.out.println("---------------------------------------------");
System.out.println("## Application 작업 완료 후에 클라이언트 요청 종료 ##");
System.out.println("---------------------------------------------");
}
결과
## Application 클라이언트 요청 시작 ##
Application 제어권 소유 - 작업 진행
커널(또는 B시스템) 요청/작업 등 대기 중
Application 제어권 소유 - 작업 진행
커널(또는 B시스템) 요청/작업 등 대기 중
Application 제어권 소유 - 작업 진행
커널(또는 B시스템) 요청/작업 등 대기 중
Application 제어권 소유 - 작업 진행
커널(또는 B시스템) 요청/작업 등 대기 중
커널(또는 B시스템) 요청/작업 등 대기 중
Application 제어권 소유 - 작업 진행
Application 제어권 소유 - 작업 진행
커널(또는 B시스템) 요청/작업 등 대기 중
커널(또는 B시스템) 요청/작업 등 대기 중
Application 제어권 소유 - 작업 진행
Application 제어권 소유 - 작업 진행
커널(또는 B시스템) 요청/작업 등 대기 중
커널(또는 B시스템) 요청/작업 등 대기 중
Application 제어권 소유 - 작업 진행
Application 제어권 소유 - 작업 진행
커널(또는 B시스템) 요청/작업 등 대기 중
커널(또는 B시스템) 요청/작업 등 대기 중
---------------------------------------------
## Application 작업 완료 후에 클라이언트 요청 종료 ##
---------------------------------------------
커널(또는 B시스템) 요청/작업 등 대기 중
커널(또는 B시스템) 요청/작업 등 대기 중
커널(또는 B시스템)에서 작업요청 결과 콜백으로 처리(바로 처리는 안함)
결과 값 (계좌번호): 1002-45-123145
정리
Bloking/Non-Blocking
Bloking/Non-Blocking
는 자신의 시스템(프로세스)이 다른 시스템(프로세스)에게 I/O를 요청할 때 누가제어권을 가지는 여부
이다.Blocking I/O
:I/O요청받은 시스템(프로세스)이 제어권을 가져가고
, 요청이 끝날때까지 기다렸다가 자신의 시스템(프로세스)를 처리Non-Blocking I/O
: 자신의 시스템(프로세스)가 다른 시스템(프로세스)에 요청해도,자신의 시스템(프로세스)가 제어권을 가지고
다른 시스템(프로세스)과 각자 작업을 처리
Sync/Async
Sync/Async
는 호출되는 시스템(프로세스)의 작업 완료 여부를 누가 신경쓰냐가 관심사동기(sync)
: 호출되는 함수의 작업 완료 여부를호출 하는
시스템(프로세스)가 신경씀비동기(async)
: 호출되는 함수의 작업 완료 여부를호출 되는
` 시스템(프로세스)가 신경씀
[참고]
- https://etloveguitar.tistory.com/140
- https://limdongjin.github.io/concepts/blocking-non-blocking-io.html#useful-references
[참고]
쓰레드와 콜백을 이용해서 편의상 타시스템 API(프로세스) 역할을 임의로 만든 것.
public class Callee extends Thread {
CallBack callBack;
Callee () {}
private String accountNumber;
private String msg;
private int progressRate = 0;
private Boolean returnYn = false;
public String getAccountNumber() {
return accountNumber;
}
public void setAccountNumber(String accountNumber) {
this.accountNumber = accountNumber;
}
public String getMsg() {
return msg;
}
public void setMsg(String msg) {
this.msg = msg;
}
public int getProgressRate() {
return progressRate;
}
public void setProgressRate(int progressRate) {
this.progressRate = progressRate;
}
public Boolean getReturnYn() {
return returnYn;
}
public void setReturnYn(Boolean returnYn) {
this.returnYn = returnYn;
}
@FunctionalInterface
public interface CallBack{
public void getApiBOK(Callee callee);
}
public void setCallback(CallBack callBack) {
this.callBack = callBack;
}
public boolean systemCall() {
System.out.println(" >> 커널(또는 시스템) 시스템 콜 (작업진행상태) [ "+ this.getProgressRate() +" %]");
return this.getReturnYn();
}
public Callee(CallBack callback) {
this.callBack = callback;
}
/**
* 비동기 API 역할 (하나의 쓰레드를 타시스템 API 또는 타프로세스 개념)
*/
@Override
public void run() {
/* 커널(또는 B시스템) 작업 */
for(int i=0;i<13;i++) {
this.setReturnYn(false);
this.setProgressRate( i * 100 / 13 );
this.callBack.getApiBOK(this);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
this.setReturnYn(true);
this.setProgressRate(100);
this.setAccountNumber("1002-45-123145");
this.setMsg("조회 성공");
this.callBack.getApiBOK(this);
}
/**
* 비동기 API 역할
*/
public void syncStart() {
/* 커널(또는 B시스템) 작업 */
for(int i=0;i<13;i++) {
this.setReturnYn(false);
this.setProgressRate( i * 100 / 13 );
this.callBack.getApiBOK(this);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
this.setReturnYn(true);
this.setProgressRate(100);
this.setAccountNumber("1002-45-123145");
this.setMsg("조회 성공");
this.callBack.getApiBOK(this);
}
}