결론부터 말하면 NonBlocking의 원리, Network 프로그래밍일 경우 OS에서 동작하는 Socket의 동작 방식을 모르기 때문이다.

Java의 Standard IO의 경우 사실 단순하다.
하나의 Connection에 대해서 하나의 Thread가 1:1로 대응되어,
개발시 하나의 Connection에 대한 처리 로직이 명확하고, 다른 Connection에 대해 독립적이기 때문이다.

쉽게 말해 개발자가 개발하기 쉬운 구조다.

하지만 NIO의 경우 다수의 Client Connection을 극단적인 경우 하나의 Thread 만으로도 처리할 수 있는 구조이다.

NonBlocking에 대한 심도 깊은 이해가 필요하다.
사실 NonBlocking은 의미적으로는 간단하다.
NonBlocking Method를 호출할때 일반 IO와 달리 Blocking 되지 않고 즉시 Return됨을 의미한다.

하지만 Blocking IO만 접해왔던 개발자들에게 즉시 Return이라는 것은 쉽게 이해할 수 없는 부분이다.

왜냐하면 read(buffer) 라는 method를 call해서 int형으로 넘겨받은 값이, 실질적으로 client에 전송된 byte의 수를 의미하는 것인데, 이것은 달리 해석하면, data 전송이후 실질적으로 client가 해당 data를 consume한 것이 확인 되었을때 알 수 있는 값이기 때문이다.

쉽게 말해서 blocking method 호출시 최소한 작업 완료에 대한 응답값이 필요하다.
서버가 특정 data를 전송했을때, client로 부터 data가 consume되었다는 프로토콜 구조적인 확인이 필요하고, 이때 client의 처리가 지연될 경우 method 호출로 인해 blocking 되는 시간은 길어진다고 볼 수 있다.

다시 본론으로 돌아와서,
그렇다면 NIO 의 write(buffer) 를 호출했을때 어떠한 원리로 blocking 없이 즉시 return 값을 받을수 있을까?

이것을 이해하기 위해서는 OS의 Socket에 대한 이해가 필요하다.
더 자세히 말해 Socket Buffer에 대한 이해가 절대적으로 필요하다. 

결론부터 얘기하면,
Kernel Level의 모든 Socket Object는 자체적인 Read/Write Buffer를 가진다.
Socket Buffer로 넘겨진 data는 실질적으로 OS의 Kernel로 그 제어권이 넘어간 것이라 할 수 있다.
Application Level에서 Socket Buffer의 Data가 넘어가는 순간 실질적인 Data 전송의 스케쥴링은 Socket을 컨트롤 하는 OS에 달렸다고 볼수 있다. (물론 Application Level에서 Socket Buffer의 크기를 조절하여, 간접적인 Data 전송 스케쥴도 가능하긴 하다)
NIO의 핵심 메카니즘은 Socket Buffer에 단순히 Data를 Write하고 Read하는 역할 밖에는 하지 않는다.

단순히 메모리상의 특정 영역(Java Memory Area)에서 OS의 영역(Socket Buffer in Kernel)으로 데이터를 Write/Read하는 작업만을 하기때문에, 실질적으로 Blocking이 없다고 볼 수 있다.

예측할 수 없는 Data가 불규칙적으로 오고가는, Socket Buffer의 Data를 효과적으로 감시하기 위해서는, Polling이 아닌 Event기반의 감시자(Monitor Object)가 필요한데, 그 역할을 하는 것이 NIO의 Selector Object이다.

Selector에 대한 자세한 설명은 다음 강좌에 연재하겠다.

어찌되었건, NIO의 핵심은 구현 관점에서 NonBlocking Call과 Event기반의 처리라 볼 수 있다.
특징은 기존 IO에 비해 구현은 훨씬 복잡해지면서 Thread의 수를 최소화 할 수 있어서 대량접속에 대한 처리에 대해 쓰레드의 Context Switching 비용의 부담에서 벗어 날 수 있다.

물론 구현이 복잡해 지는 만큼, Design(설계)시 고려사항이 많아지며, 필수적이고 잘 알려진, Network, Thread,  I/IO 관련 Design Pattern에 대한 이해가 필수적이다.

-글 : 김영곤(gonni21c@gmail.com)
다음편에 계속..
SUBTYPES MUST BE SUBSTITUTABLE FOR THEIR BASE TYPES.

하위타입의 객체가 상위 객체를 대체할 수 있어야 한다.

Have you ever seen code that has lots of instanceof expressions in the clauses of if
statements. Though there are some legitimate uses for expressions like this, they are few
and far between. Usually they are a result of violating the LSP, and are themselves a violation
of the OCP.

The LSP says that the users of base classes should not have to do anything special in
order to use derivatives. Specifically, they should not have to use instanceof, or downcasts.
Indeed, they should know nothing about the derivatives at all. Not even that they
exist.

Consider the payroll application shown in Figure 6-8. The Employee class is abstract
and has an abstract method named calcPay. It’s pretty clear that SalariedEmployee
will implement this to return the employee’s salary. It’s also pretty clear that Hourly-
Employee will implement it to return the hourly rate times the sum of the hours on this
week’s time cards.
SOFTWARE ENTITIES (CLASSES, MODULES, FUNCTIONS, ETC.)
SHOULD BE OPEN FOR EXTENSION, BUT CLOSED FOR MODIFICATION.

S/W의 요소(클래스, 모듈, function)은 확장에는 열려있고, 
수정에는 닫혀있어야 한다.

This principle has a high-falutin’ definition, but a simple meaning: You should be able to
change the environment surrounding a module without changing the module itself.

이 원리의 핵심은 어떤 특정 기능을 시스템 자체의 모듈을 변화시키지 않고도, 
기능상의 변화를 줄 수 있다는 것이다.

Consider, for example, Figure 6-4. It shows a simple application that deals with
Employee objects through a database facade named EmployeeDB. The facade deals
directly with API of the database. This violates the OCP because a change to the implementation
of the EmployeeDB class can force a rebuild of the Employee class.
The Employee is transitively bound to the database API. Any system that contains the
Employee class must also contain TheDatabase API.

EmployeeDB는 DAO 역할을 하는 class이며, DB의 API를 직접적으로 Access하는데,
이것은 OCP 위반에 해당된다. 왜냐하면 EmployeeDB의 변경이 client, 즉 Employee의 
변화까지 동반하기 때문이다.

결국 coupling으로 인해 기능상 확장성이 떨어지는 구조라 하겠다.


Unit tests are often the places where we want to make controlled changes to the environment.
Consider, for example, how we would test Employee. Employee objects make
changes to the database. In a test environment we don’t want the real database to change.
We also don’t want to create dummy databases just for the purposes of unit testing.
Instead, we’d like to change the environment so that the test catches all the calls that
Employee makes to the database, and verifies that those calls are made correctly.

Employee를 Unit 테스트할 경우 Real DB에 영향을 주는 것은 문제가 될 수 있으며,
Test를 위한 Dummy Object를 생성하는 것도 코드의 수정을 동반하기 때문에 좋지 못하다.
결국 Employee의 호출을 검증할 수 있는 테스트 환경의 제공이 최적의 조건이라 할 수 있다.


We can do this by converting EmployeeDB to an interface as in Figure 6-5. Then we
can create derivatives that either invoke the true database API, or that support our tests.
The interface separates Employee from the database API, and allows us the change the
database environment that surrounds Employee without affecting Employee at all.

이러한 문제는 DAO 자체를 Interface로 변경해서 해결할 수 있다.
테스트용 DB 환경과 Real DB 환경을 유연하게 사용할 수 있다.
Empoyee에 어떠한 영향을 주지 않고 자유롭게 DB 환경을 제어할 수 있다는것 이점.




A CLASS SHOULD HAVE ONLY ONE REASON TO CHANGE.
하나의 클래스는 변화에 오직 하나의 이유만 있어야 한다.

You’ve probably read the nonsense about objects needing to know how to draw themselves
in a GUI, or save themselves to disk, or convert themselves to XML, right? Beginning
OO texts like to say things like that. Ridiculous! Classes should know about only one
thing. They should have a single responsibility. More to the point, there should only be
one reason for a class to change.

 Consider Figure 6-1. This class knows way too much. It knows how to calculate pay
and taxes, how to read and write itself on disk, how to convert itself to XML and back, and
how to print itself on various reports. Can you smell the Fragility? Change from SAX to
JDOM and you have to change Employee. Change from Access to Oracle, and you have t
change Employee. Change the format of the tax report and you have to change
Employee. This design is badly coupled.


하나의 클래스가 너무나 많은 역할(responsibility)를 가지고 있어 coupling이 극도로 증가한 상태


In reality we want to separate all these concepts into their own classes so that each
class has one, and only one, reason to change. We’d like the Employee class to deal with
pay and taxes, an XML related class to deal with converting Employee instances to and
from XML, an EmployeeDatabase class to deal with reading and writing Employee
instances to and from the database, and individual classes for each of the various reports.
In short, we want a separation of concerns. A potential structure is shown in Figure 6-2

위의 복잡한 것을 각자의 역할을 가지는, 변화에 대해 단 한가지 이유를 가지는 개별의 class로 나누어야 한다.



Violation of this principle is pretty easy to spot in a UML diagram. Look for classes
that have dependencies on more than one topic area. A dead give-away is a class that
implements one or more interfaces that endow it with certain properties. Careless use of an
interface that endows an object with the ability to be stored on disk, for example, can lead
to classes that couple business rules with issues of persistence.

잘못된 interface의 상속구조의 사용한 향후 coupling의 문제를 야기시킨다.


Consider the two diagrams in Figure 6-3. The one on the left couples Persistable
tighly into Employee. All users of Employee will transitively depend upon
Persistable. This dependence may not be great, but it will be there. Changes to the
Persistable interface will have the potential of affecting all users of Employee.

좌측의 구조는 Employee와 Persistable interface가 강하게 coupling된 상태로,
Persistable의 변화가 모든 Employee 클래스에 영향을 줄 수 있다.


The diagram on the right side of Figure 6-3 leaves Employee independen of Persistable,
and yet allows for persistence just the same. Instances of PersistableEmployee
can be passed around the system as Employees, without the rest of the
system knowing about the coupling. The coupling exists, but is hidden to most of the system.

우측의 구조는 Employee가 Persistable에 독립된 구조로,
시스템 상에서 coupling이 없는 상태로 Employee의 형태로만 사용될 수 있다.
coupling은 존재하지만, hidden으로서의 이점이 있다.


Many of these smells are a result of mismanaged dependencies. Mismanaged dependencies
conjure the view of code that is a tangled mass of couplings. Indeed, it is this view of
entanglement that was the origin of the term “spaghetti code”.

Object oriented languages provide tools that aid in managing dependencies. Interfaces
can be created that break or invert the direction of certain dependencies. Polymorphism
allows modules to invoke functions without depending upon the modules that contain
them. Indeed, an OOPL gives us lots of power to shape the dependencies the way we
want.


잘못된 코드는 객체의 의존관계 관리를 잘못한 결과물이라 할 수 있다. 
잘못된 의존관계는 복잡하게 꼬여서 coupling을 가지는 코드의 관점을 만들어 내고,
실제로 이것은 '스파게티 코드'라는 말의 어원이 되었다.

객체지향언어는 관계 관리에 도움을 주는 툴을 제공해준다.
인터페이스는 어떤 직접적인 관계를 방지해주도록 사용될 수 있다.
다형성(Polymorphism)은 모듈이 어떤 모듈에 대해 의존성 없이 기능을 사용할 수 있게 해준다
객체지향 어언는 우리가 원하는 형태의 의존 구조를 만드는데 도움을 준다.


Design Quality

What does it mean to be well designed? A system that is well designed is easy to understand,
easy to change, and easy to reuse. It presents no particular development difficulties,
is simple, terse, and economical.

잘 디자인(설계)된 프로그램은 위와 같은 특징들이 있음


Design Smells.

You know when a programmer is working with a poor design by the state of his eyes
and nose while he’s looking at the code. If his or her facial expression reminds you of the
detectives who have just opened a body bag containing a 12 day old corpse, then the
design is probably pretty ripe. The smells of a poor design have many different components.

1. Rigidity: The system is hard to change because every time you change one thing,
you have to change something else in a never ending succession of changes.

- 강직, 경직됨 : 시스템을 바꾸기 어려운 경우, 매번 바꾸었기 때문(스파게티 코드), 지속적인 변경이 필연적인 상황

2. Fragility: A change to one part of the system causes it to break in many other,
completely unrelated, parts.

- 단편, 깨지기 쉬움 : 하나의 변화 전체 시스템에 치명적 영향을 줌, Strongly Coupled~

3. Immobility: It is hard to disentangle the system into components that can be
reused in other systems.

- 부동성 : 시스템을 콤포넌트(다른 시스템에서 사용될 수 있는) 조합으로 분리해 내기 힘든 상황

4. Viscosity: The development environment is held together with scotch tape and
toothpaste. It takes forever to go around the edit, compile, test loop.

- 점착성 : 개발 환경에 지나친 의존성을 가지는 상황

5. Needless Complexity: There are lots of very clever code structures that aren’t
acutally necessary right now, but could be very useful one day.

- 불필요한 복잡성 : 당장 필요하지도 코드 구조가 존재하는 상황, 그러나 언젠가는 유용할 지도 모르는 코드임

6. Needless Repetition: The code looks like it was written by two programmers
named Cut and Paste.

- 불필요한 반복 : Ctrl C, V로 생산된 코드가 혼재하는 상황

7. Opacity: Elucidation of the originator’s intent presents certain difficulties
related to convolution of expression.

- 투명성 : 작성자의 의도에 대한 설명이 난해한 표현으로 어려움을 내포하고 있음

+ Recent posts