방법 : stream generate
stream generate는 요청을 받을 때 마다 무한히 만들어 줌. generate에 있는 함수는 호출할 때 마다 값을 준다. 밑의 코드의 stream을 실행하면 1부터 20까지 랜덤값을 발행해줌 무한히.
또 stream에 여러개의 메소드 channing을 해도 실행하기 전까지 연산 지연함. collect()를 하면 연산함. 이게 stream의 귀찬음.
asSequence를 쓸때는 sequence의 일이 얼마나 무겁냐에 따라 효율이 달라진다. loop이 100번 인데 chainning 함수가 2000개 라면 무조건 sequence로 다루는게 효율적이다. map에서 하는일이 코루틴을 통해 네트워크에서 받아온다면 ? 이러면 맵다음에 또 filter를 돌면… 어마어마한..
Stream으로 바꾸지 않고 sequence로 하면 코틀린의 콜렉션 메소드와 굉장히 일치함. 대신 sequence는 코루틴을 사용해서 분해하지 않는이상 멀티스레드를 지원하지 않는다. sequence를 코루틴으로 분해해서 사용하면 원소 하나 처리할 때마다 코루틴이 발동된다. 그럼 dispatcher가 어떤 스레드에 넣을지는 알아서 한다. 그래서 멀티스레드 디스패처를 만들어 코루틴으로 sequence를 해소하게 되면 얘는 멀티스레드에서 각 원소가 해소하는 처리를 하게 되고 자바의 stream도 multithread 처리를 할 수 있지만 stream에 대한 기본전략을 잘 잡아줘야함 .
컬렉션의 멀티스레드는 어렵다. 컬렉션의 원소가 다 atomic 해야만 멀티스레드를 구현할 수 있는데 잘 안된다. 실무에선 generator와 asSequence를 굉장히 많이 쓴다.
fun main() {
val stream = Stream.generate {
(1..20).random()
}
val resultantList = stream
.limit(10)
.collect(Collectors.toList())
}
kotlin은 buildSequence를 이용할 수 있는데 그 함수를 이용하면 sequence를 generator 문법으로 만들 수 있다. kotlin에서는 yield가 외부에 출력은 가능하지만 입력은 받지 못한다. while문 안에 yield가 들어갈 때 마다 멈출 수 있기 때문에 걔는 yield를 기준으로 continuation이 만들어 진다. take(10)을 하면 제어문인데 while문을 멈춰버리고 거기서 빠져나올 수가 있는 것이다. 이러면 알아서 continuation을 만들어서 사용함. 하지만 streamGenerate는 실제로는 함수를 작성하는것도 못한다. 제어문 형태로 못만듬.
예를들어 전국민 전화번호 만든다 생각해보자. 근데 전화번호부를 반복하면서 뭘 처리하고 싶은데, 모든 전화번호부를 메모리에 할당하면 엄청난 메모리가 필요한데, sequence를 사용하면 내가 필요할때마다 외부 API에 계속 요청하면 됨. 통신을 통해서 각 전화번호를 받으면, 내 컴퓨터의 메모리에는 없는데 외부에서 받아올 수 있다. 그러면 무한의 메모리를 당겨 쓰는 것과 같다. 그래서 시퀀스라는것은 단지 계산을 지연할 수 있는 기능만이 아니라 메모리를 외부로 이전하는 효과가 일어난다. 그렇다는 얘기는 시퀀스의 출처를 시퀀스도 만들 수 있다. 시퀀스가 시퀀스를 출력할 수 있다는말. 어떤 서버가 시퀀스 서버라고 생각하면 어떤 서버를 시퀀스로 가져오는 시퀀스를 만들어서 걔를 . 또한 나한태 시퀀스 형태로 데이터를 주고 있는 그 시퀀스 서버도 자기 데이터의 일부를 다른 시퀀스 서버로부터 가져올 수 있다. 그러면 어떤 요청을 일으키면 자기 메모리에서 일어나는게 아니라 그 요청이 다른 시퀀스 서버의 다음 시퀀스를 요청하게 되고 그 서버는 또 다른 시퀀스 서버의 다음 번쨰 시퀀스를 요청하게 됨. 그중에 어떤 시퀀스는 내가 처리하지만 다른 처리는 다른 서버로 부터 받아오는데 나의 하나의 요청이 서버로 전파하고 나면서 쭉 필요한 만큼만 시퀀스로 댕기고 나머지들은 요청할것이다. 내가 시퀀스 서버로부터 홀수를 공급받을 건데, 홀수일땐 나한태 주고 3의 배수라면 다른 서버로 부터 받아오는것임.그러면 내가 홀수를 댕길때 홀수만 처리하는 서버가 처리하는 양과 3의 배수만 처리하는 서버의 처리양이 다르겠지만, 다들 내가 원하는 만큼만 일하고 더 이상 일하지 않음. 예를들어 15까지 요청했다면 3의 배수만을 처리하는 시퀀스 서버는 딱 5번만 움직이고 움직이지 않는다. 홀수 서버는 그것을 제외한 4번만 움직인다. 그렇다면 7개의 홀수를 얻게 된다. 그렇다면 모든 서버가 다 시퀀스로 되어져 있다면 내가 필요한 정보를 서버별로 분산하는게 자동으로 이루어지고 딱 필요한 만큼만 이루어짐. 이런 시퀀스 chain을 내가 pull로 당겨서 일으키고 있는 것이다. 그래서 sequence는 pull 방식의 분산을 유도할 수 있다. Rx는 push 방식의 분산을 유도한다. push 방식은 당기는게 아니라 밀어주는 것임. 내가 받겠다고 해서 저쪽에서 밀어주는거지만 내가 당기는것은 아님. 이게 언제 유리하냐면 push와 pull의 부하를 분산하고 싶을때 유리하다. 별도의 ec2인스턴스를 만들어서 얘는 thread sleep으로 while 돌면서 감시만 하는 인스턴스가 있음. 그럼 이벤트 리스너에 함수를 붙혔다면 함수는 실행안됨. 이게 부하 분산이 된것임. while은 빡빡하게 돌지만, 리스너에 붙은 함수는 실행이 안되고 있는것이다. 그래서 결국 가 pull인데 pull 과 pull&push로 나누는 이유는 pull 쪽의 부하를 가져가게 분산시킬 수 있다. 같은 머신내에서도 일어나고 다른 머신 내에서도 일어난다. 아마존 simple queue는 pull push 둘다는 제공하지 않는다. 큐에 쓰고 읽는건 되지만 이벤트 알람해주는 기능은 없다. 직접 구현해야된다. 실제로 queue에는 데이터가 많이 들어 가지 않는다. 그래서 bucket에 큰 데이터를 넣고, 큐에는 작은 데이터를 넣는다. pull push 개념을 잘 이해 해야하는데 우리가 사용하는 spring의 컨트롤러도 push 방식이다. 요청을 하면 이벤트 리스너가 발생한것 처럼 실행 되는데, 그 안에선 사실 pull을 겁나게 돌고 있는애가 있다. 왜 이렇게 까지 할까 ? end 포인트 개발이 쉬워지고 부하 분산이 쉽다. pull 루프는 c가 돌고 마지막에 다이얼로그형으로 짜여져 있는 리스너는 느린 자바스크립트로 짤 수 있게 되는것이다.(부하분산) 또한 rx처럼 프레임워크 수준에서 push로 바꿔주는 시스템을 쓰면 어떤 장점이 있냐면 rx에 온 수많은 요청을 종합적으로 내부 스케줄링을 통해 더 효율적으로 만들 수 있는 기회가 생긴다. 코루틴도 비슷한데 디스패처를 공유하게되면 더 많은 suspend함수를 스케줄러 안에 넣고 분산시켜서 처리할 수 있는 여력이 생기게된다. 그래서 중앙 집중화 되어 있는 push 시스템은 무의미하지 않다.
둘중 무엇을 쓸 것이냐는 data의 주도권이 누구한태 있냐에 달려있다.
pull 방식을 쓰는 경우와 장점
push 방식을 쓰는 경우
장점
우리가 두 서버간의 직접적인 통신을 막아서 부하분산을 처리하기 위해선 중간에 suspend를 주면 된다. 어떻게 주냐면 Queue에 주면 된다. 서버가 필요한 일이 있으면 queue에 쓰면 된다. 큐에 새로운게 들어왔다는 알람을 다른 서버가 받아서 처리를 다 하고 처리가 다 끝났으면 다른 큐에 보고해 주면된다. 이것이 더블큐 방식이다. 더블큐 방식에서는 요청한 쪽도 큐에 쓰고 완료한 쪽도 큐에 쓴다. 완료했다는 큐를 통보받은 애가 집어가면 된다. 재일 쉬운 방법은 요청을 큐에 쌓을때 이 요청건수에 대한 고유한 uuid를 만들어서 큐에 쌓고 그걸 인수한 애는 그 작업을 처리한 후에 그 uuid로 보고해주면 서로 uuid 키교환이 되기 때문에 내가 원했던 uuid가 들어왔네 하고 캐치할 수 있다. 이방식에서 내가 원하는건 큐에 넣을땐 내 마음대로 넣고 입력 큐에 들어온걸 감시하는건 푸시알람으로 받았으면 좋겠잔아 근데 실제적으로는 while sleep을 돌면서 계속해서 감시하는 것이다. 그런 서버가 중간에 있다면 이 서버는 계속 pull 감시하고 있었지만 나한태 등록된 리스너 한태는 push로 알려줄 수가 있다. push는 pull을 push로 전환을 한번 더 해주는 것임. 마법같은 push는 없다. event listener란 누군가가 루프돌면서 감시해서 그걸 event로 바꿔서 listener에게 준것이다.
jvm 은 순수 자바로 제어하지만 순수 자바로 시스템 스레드를 건드릴 순 없다. java 에서 윈도우 스레드를 어떻게 사용할까 ? 당연히 win 32/64프로그램을 만든 프로그램을 경유해서 사용하게 될것이다. C모듈로 되어있는 네이티브 모듈을 자바 인터페이스로 바꿔주는걸 JNI(Java Native Interface) 레이어라고 부른다. JNI를 자바 인터페이스에 맞는 헤더를 가져와 네이티브로 컴파일 하고 나면 애는 자바 인터페이로 생산됬기 때문에 자바 내부에서 부를 수 있다. 결국엔 jvm 가상 머신 재일 밑에는 JNI 규격을 따르는 수 많은 네이티브 함수들이 널려있다. 네트워크 그래픽 메모리 프로세스 관리. 그렇기 때문에 jvm 수준에서 jni 인터페이스를 쓰는지 않쓰는지 알수가 있음. 어떤 jni를 쓰는지 알 수 있음. 안드로이드는 main 스레드에서 JNI에 제약이 걸려있다. 그래서 어떤 라이브러리를 써도 결국 http 커넥션을 쓸건데 jni 소켓을 사용할 것임. 그러면 안드로이드는 죽일것임.
observable은 스케줄러가 그다음번 푸쉬할 값을 가져오는 일을 한다. observable의 스케줄러는 한명이라도 구독자가 생겼을때부터 발동한다. 구독자가 없어지면 스케줄러는 멈춘다. 구독자가 생기면 푸쉬 작업을 한다.그러면 이 스케줄러가 전체 스레드풀에서 노(오)는 애들이 그다음번 처리를 해주게끔 계속 작업하게 시킬 수 있다. 그럼 observable을 30개 만들었는데 스레드풀은 스레드에 5개 있다면 5개가 돌아가면서 각각 애들의 푸시작업들을 위임받아서 한다. 정확하겐 suspend 함수가 쌓여있는 코루틴과 똑같이 작동한다. 그래서 그냥 그 자체로서의 stream은 코루틴 처럼 하기 힘든데 까다롭다고 해봐야 sequence로 바꿔서 코루틴 dispatcher를 걸어주면 똑같이 작동된다. 우리가 제너레이터를 만들었다고 해도 suspend 블록들이 만들어졌을 뿐 걔를 아직 소비하고 있지는 않음. 그냥 소비할때 take로 소비했는데 take가 dispatcher로 소비하는게 아니라 현재 스레드를 이용해 소비한다. 그래서 시퀀스를 코루틴이 소비하게 해야지 위에서말한 스케줄러가 멀티스레드를 쓰는것처럼 서스펜드블록을 코루틴에 있는 dispather가 소비하게 된다. yield(내부를 보면 suspend function을 부르고 있음.)를 통해 시퀀스를 만들어주는건 sequence 라는 빌더밖에 없다. 코루틴 시스템이나 코틀린이 제공하는 시퀀스 불리는 서스펜드 함수를 호출할 때만 cps를 나눠준다. 나눠진건 suspend 함수의 컬렉션일 뿐이다. 정확하게 컬렉션이 아니라 컨티뉴에이션과 연결된 형태이다. 지연 실행이 가능한 구조로서만 준비되어져 있는것이다. 그것을 소비할 것임. 소비할건데 시퀀스 입장에선 시퀀스 메소드를 사용해서 소비하는 것이다.take(10)이 sequence linkedlist를 하나씩 전진시켜서 실행하게 하는것이다. 그럼 우리가 시퀀스 블록도 take을 사용해서 실행할 수 있다. 근데 우리는 코루틴 만들때 시퀀스로 만들지 않고 코루틴 빌더로 만들었다. launch같은. 이것은 외부에 job을 리턴한다. 이 스레드가 언제 끝나는지 알려주기 위해 job을 리턴하는데 그에 비해 sequence는 suspend 블록을 다 처리하고 난 다음 뭘 이것을 감싸는 자료구조체로써의 인터페이스를 리턴한다. 컬렉션처럼. 그게 시퀀스 빌더와 코루틴 빌더와의 차이다. 안에 만들어지는건 똑같이 서스펜드에 링크드 리스트인데 얘는 목적이 dispatch로 분산하는 멀티스레드와 스레드 타이밍을 조절하기 위한 job(우린 join 때문에 왜냐면 언제 다른 job이 끝날지 모르니까) 목적이 아니라 컬렉션 처럼 다루는 자료구조를 만드는것이 목적이니까 내부적으로 서스펜드 함수에 링크드리스트가 만들어 지지만 얘는 바깥쪽에 노출하는게 dispatcher를 넣거나 context를 넣는게 아니라 collection 처럼 노출한다. 그럼 얘는 노출된 애를 다시 코루틴 빌더로 집어넣어서 소비하게 되게 하면 되긴한다. sequence를 launch안에 넣고 await으로 take(1) 을 여러번 해주면 된다. 그럼 이 안에서 suspend가 일어날 떄 마다 디스패처가 작동한다. 아까 말했듯이 시퀀스도 서스펜드 블록이고, take(1)도 suspend 함수를 꺼내는 행위라면 코루틴 안에서는 이 행위를 dispatcher가 해주는 것이다. cps 전진시키는것을 dispatcher가 해준다. 그래서 sequence를 만들고 launch를 비롯한 코루틴 스코프에 넣어주면 그떄 그안에서 하는 행위가 dispatcher가 관여한다. 안하면 buildSequence는 코루틴 컨텍스트에 dispatcher를 main으로 지정해 주는거나 마찬가지이다.
함수형을 도입하는 이유는 지연 연산이 굉장히 크다. 대규모 DB 시스템을 프로그램으로 구축할때 메모리가 부족하면 어쩔 건가 ? stream왜 생겼을까 stream을 가장 많이 사용되는 곳은 http connection을 쓰면 inputStream과 outputStream을 사용한다. inputstream으로 받는 이유는 뭘까? 소켓 통신이 지속적으로 데이터를 주기 때문이다. 내장되어있는 null 문자열이 오면 socket을 끊는다. 그러면 inputstream만 조사하면 헤더도 본문도 stream으로 날라오면 끊을 수 없고 매번 끝을 확인하지 않고 buffer가 찰 때까지 stream으로 돌린다. 그럼 우리는 fileBuffer를 stream 동기적으로 돌리는데 그럼 안되는거 아닌가 ? 스트림이 다들어올때 까지 블록킹 하는데? 만약에 통신이 좋지 않다면 계속 블로킹 될탠데 ? inputStream을 while문으로 buffer에 계속 집어넣어주고 싶다면 스레드 루프를 돌거나 중간에 스레드 슬립을 걸어줘야한다. 그냥 while문으로 buffer에 넣어주면 안된다. 따로 스레드로 빼서 해줘야함. 근데 우리가 짠 http 커넥션을 보면 이 전체를 backgroun에 넣을지언정 stream을 처리하는 부분은 따로 스레드로 빼지 않는다. 근데 자바 inputStream과 outputStream은 블로킹 아닌가요(질문)? 모라는지 모르겠음.. NIO 안써도됨 NIO는 오히려 버퍼 전체를 감시하려고 쓰는거다. 근데 왜 stream으로 할까 ? 데이터가 corecursion이기 때문이다. 조금씩 무한히 얼마나 들어올지 몰라서 stream이다. 예전 소켓프로그래밍 하면 앞의 자리수를 지정해서 잘랐는데 http 프로토콜은 리스폰스할때 청크로 들어오면 전체 크기를 모른다. 마지막에 터미널 문자열이 들어와야 끈을 수 있다. 또 stream을 쓰는 이유는 메모리 보호하고 연산을 사용하기 위해서이다. 만약 html 본문이 1기가 들어온다고 생각해보자. stream을 다 기다렸다가 DOMParsor가 돌수 없다. 죽는다. 그러면 stream이 들어올떄 꺽쇠가 나올떄 마다 부분 랜더 파싱을 한다. 이게 바로 삭스 파서이다.
연산과 메모리는 교환된다. Math.sin을 매번 계산하는 방법이 있다면 라디안 소수 세번째까지의 모든 sin의 계산값을 배열에 넣어놔도 된다. 그럼 라디안 소수 세번째 까지는 배열에서 찾으면 된다. 이게 바로 메모리를 연산 대신 사용하고 있는 것이다. 반대로 내가 메모리를 하나도 안쓴다면 사인에 라디안값 들어올 떄 마다 수식으로 계산해서 리턴하면 된다. 그럼 메모리를 하나도 안쓰는 것이다. 그래서 컴퓨터는 어떤 경우에나 메모리와 연산을 교환할 수 있다. 그럼 stream은 극단적으로 메모리를 한번에 들어오는 stream 양으로 보호하고 연산을 사용하기 위해서이다. 컬렉션은 그에비해 반대로 한다. 메모리를 써서 연산을 줋인다. 메모리와 연산의 교환법칙에서 우리가 메모리를 보호하려면 stream을 쓰고 이러다보니 서버에서 많이 쓴다. 사용자가 많이 달라붙으니까 . 서버가 너무 느려 사람이 짜증내면 stream을 보다 메모리를 쓰는 방법으로 바꾸고 서버를 스캐일아웃하는 방식으로 간다. stream은 또한가지 방법중에 하나가 외부 stream에 의존하는 건데 그렇기 때문에 나한태 없는 데이터를 외부에서 받는 방식으로 외부한태 메모리나 연산을 다 전가시킬 수 있다. 예를들어 내가 stream의 결과로 image processing의결과를 받으려고 할때, 그럼 내가 stream에 원본 이미지를 여러개의 무거운 메소드(라운드, 플립 등 이미지 처리하는 무거운 연산들) 체이닝을 했을때 원본 이미지는 나한태 시작하지만 다음번 stream에 대한 연산은 다른 서버에 갖다가 둔다. 그럼 내가 처음에 이미지 배열을 갖고 있으면 첫번째 이미지는 다른 서버로 가서 요청해서 받아온다. 그러면 이 컴퓨터는 메모리도 아끼고 연산도 아낄 수 있다. 인코딩 갖고 호들갑을 떨 필요가 있나 싶지만 유튜브에 영상 인코딩은 어떻게 할건데 비디오 업로드 리소스를 가지고 mp4에 자기들의 포맷으로 인코딩 한후에 스케일 레벨로 동영상을 3초단위로 잘라서 용량별로 다 바꾼다. 이걸 서버 분산을 스트림으로 짤 수 있음. rx를 짤 수도 있고. 하지만 Rx는 푸쉬할 수 있는 원본 소스가 서버에 있다. 그래서 rx 원본을 만들고 observable로 subscribe를 다른 서버들이 하는것이다. 걔내한태 쏴주는 것이다. 나를 구독한 애들한태 다 쏴주면 구독하는 애들중 자르는애들은 자르고, 인코딩하고 , 인코딩을 구독하는 애는 저장소로 보낼 거고 저장이 되면 저장소를 구독하는애들은 화면 업데이트를 할태고 그럼 클라이언트는 화면 업데이트 하는애를 구독하면서 할것임. 이렇게 하면 push 로 구현할 것이고 반대로 구현하면 pull 로 구현하는것이다. 프론트엔드가 polling 으로 데이터가 업데이트 됬는지 물어보고 데이터 업데이트 서버가 인코딩 저장완료됬는지 그 담당 서버에게 물어보고 인코딩 서버에 또는 서버에 물어보고 하면 pull 방식이 된다. 하지만 위에선 observable subscribe 를 여러개 구성했으니 얘내들 마다 다 스케줄러가 폴링을 하고 있을 것이다. 서버들 마다 polling을 돌고 있는거다. 푸쉬란 마법은 없다. 이거 없이 서버 분산하면 서버 사이마다 다 큐를 둬야한다. 그러다 보니 rx 채인을 하려는 거고 이게 spring flux인 것이다. flux 서버끼리 stream 전환이 되는 이유가 바로 이것이다. 그래서 spring flux 끼리 채이닝을 구성하면 중간에 mq가 필요가 없다. 한개 할땐 mq 껴서 하는게 편할 수 있지만, 여러 채이닝 하면 이방법이 낫다. 클라이언트를 간략하게 하고 싶으니까 push 형으로 데이터 교환하고 싶고 지연시키고 싶고 내가 풀을 구현해서 앞뒤처리하기 싫으니까 rx로 쓰는것임 지가 알아서 polling을 하니까.
특징
인증 절차
특징
인증 절차
특징
인증 절차
특징
인증 절차
http는 HTML로 작성된 문서를 전송하기 위한 프로토콜 HTTP 이다.
Ajax(Asynchronous JavaScript+XML)
Comet
SPDY
기능
기능
핸드쉐이크/리퀘스트
정적인 HTML 내용을 클라이언트 사이드 스크립트를 사용해서 동적으로 변경하는 기술을 말한다. HTML등으로 만들어진 웹 페이지를 JS등의 클라이언트 사이드 스크립트로 조작하여 동적으로 변화시킨다.
DOM(Document Object Model) :HTML 문서와 XML 문서를 위한 API이다. Dom을 사용하면 HTML 내의 요소를 오브젝트로 다룰 수 있기 때문에 요소 내의 문자열을 추출하거나 CSS를 프로퍼티로서 변경해 디자인알 수 있다.
웹 어플리케이션은 웹 기능을 사용해서 제공되는 프로그램을 지칭한다. 어플리케이션이 발달하면서 프로그램에 의해 생성된 컨텐를 만들어 보낸다. 이것을 동적 컨텐츠라 하고, 사전에 준비된 컨텐츠를 동적 컨텐츠라고 한다.
CGI(Common Gateway Interace) : 는 웹 서버가 클라이언트에서 받은 리퀘스트를 프로그램에 전달하기 위한 구조이다.
특징
java Servlet
XML(eXtensible Markup Language) : 목적에 맞게 확장 가능한 범용적으로 사용할 수 있는 마크업 언어이다. HTML과 같은 문서 기술 언어이다. 하지만 HTML에 비해 데이터를 기술하는 것에 특화되어 있다.
특징
RSS/ATOM
경량 데이터 기술 언어로서 JavaScript에 있어서 오브젝트 표기법을 바탕으로 하고 있다. 데이터 형은 boolean null 오브젝트 수치 문자열 등 7가지 이다.
WebSocket은 웹 브라우저와 웹 서버를 위한 양방향 통신 규격이다. 한번 접속을 확립하면 그 뒤의 통신은 모두 전용 프로토콜로 하는 방식으로 JSON이나 XML, HTML 이나 이미지 등 임의 형식의 데이터를 보낸다.