🍏 프로토콜의 위치
✅ Kernel vs Application
- 과거
L1 = HW / L2,3,4 = TCP, IP = OS의 내부 kernel에 위치
- 현대 : Application layer에 존재
- OS kernel 안에 있는 경우
장점 : 성능이 좋다
단점 : 수정할때 난이도가 높고, OS 회사의 승인이 필요하다 / 새로운 통신 프로토콜을 넣는 것이 어렵다
- Application Layer에 있는 경우
장점 : 많은 사람들이 함께 개발할 수 있고, 오픈소스화할 수 있다
단점 : 성능 저하
-> Application이 추세. ZeroMQ 포함
✅ 전통적 인터넷 프로토콜 (TCP/UDP/IP)
- OS안에 존재
- 성능은 좋지만 허용된 일부의 개발자들만 수정 가능
- Socket API = 내가 만든 응용프로그램이 kernel안에있는 TCP, IP와 통신하도록 할 때 사용
- Socket : Application과 Kernel을 이어주는 구멍
- Unix 계열은 하드웨어 소프트웨어 자원이 file이다. file에 읽고 쓰는 것과 동일하게 동작하기 때문에 운영체제의 file 함수들을 비슷하게 불러서 사용
✅ ZMQ 프로토콜의 위치
in Application Layer
- ZeroMQ는 운영체제 내부가 아닌, application 레이어 위에 있다.
- 일부는 TCP를 쓰기도 한다.
🍏 ZeroMQ
= ØMQ, 0MQ, or zmq
- open-source messaging library
- 다양한 형태, 서비스에 대해 message를 주고받을 수 있는 라이브러리
- OS kernel이 아닌 application layer에 있다.
- 앞선 기능들을 더욱 쉽게 구현하는 것
- Universal
지원하는 language, OS가 다양하다.
- Smart
pub-sub, push-pull, client server
- High-Speed
비동기식으로 미리 만들어져있다 / full-duplex 가능
- Multi-Transport
사람들이 사용하는 Transport 계층 위에서 다 돌아갈 수 있다 (TCP/UDP/...)
- Community
- The Guide
✅ ZMQ 특징
- Zero
broker(반드시 사용해야하는 서버)가 없다.
zero latency(빠르다), zero cost( free ), zero administration
- 기본적으로 비동기
- Berkeley socket API를 일부 가져와서 사용
- 다양한 messaging pattern 제공
✅ ZMQ Patterns
- Request-reply
전형적인 client-server 구조
1:1, N:M, N:N 모두 가능
- Pub-sub
data를 한놈이 만들어서 뿌리는 것 / 공지하는 방식
- Pipeline
일을 처리할 떄 여러 일꾼들이 순차적으로 작업을 받고 전달. 결국 fan-out : 뿌려지는 것 / fan-int : 다시 모이는 것 -> PULL, PUSH 사용
✅ ZMQ Sockets
- Conventional sockets
직접 다 짜주기
- ZeroMQ sockets
ZMQ가 하나의 layer처럼 동작. 기존 처럼 message queue를 관리하며 many-to-many까지 가능하다
✅ Asynchronouse ZeroMQ sockets
- ZMQ는 기본이 비동기식
- connection이 있는지와 상관없이 application을 짤 수 있다.
- Socket의 경우 일정 단위로 해야할 일에 순서가 있었는데, ZMQ는 L1,L2가 끊어지거나 연결이 되지 않아도 데이터를 송수신하게 한다.
ex) 이동 통신에서 이동해서 연결이 끊기면 Socket programming -> 끊김 / ZeroMQ -> 다시 붙음
✅ ZMQ socket lifetime
- 소켓에도 옵션을 줄 수 있다.
- server가 data를 만들어서 보낼 때 다 받을지 일부만 받을 지 filter를 걸 수 있다.
- 소켓에서의 옵션은 TCP/UDP인지 통신에 대해 주고받는 것에 대한 것에 옵션을 걸었다면 ZMQ는 통신 서비스에 대한 것까지 갈 수 있다.
✅ ZMQ socket bind & connect
일반적으로
- bind()는 서버에서 호출
- connect()는 client에서 호출
TCP/UDP에서 원형 버퍼가 있었다. Layer간에도 sending queue/receiving queue가 있다.
- ZMQ도 마찬가지로 connection 별로 queue를 가진다
ex) sever가 3개의 clinet와 통신하면 3개
- ZMQ는 분산처리를 하며 반드시 server가 존재할 필요는 없다. 모든 sw들이 동등하고 하나의 sw가 다른 컴퓨터에게 server로 비춰질 수 있으면서 본인은 client 역할을 할 수 있음.
-> 따라서 꼭 서버가 bind()를하는 것은 아님.
본인이 짜는게 client-server가 아니라면 누가 bind/connect 해야하지..?
- bind( ) : 처음부터 끝까지 유지되는 것 / most stable, 시간적으로 오래 있는 애들
- connect( ) : 임시로 나타나고 시간이 지나면 없어지고 / 동적으로 되어있는 애들, device들
✅ RabbitMQ
- 중앙 집중형 MQ
- 기능이 한정적이기 때문에 우리의 요구와는 맞지 X
ex) 재난 문자 / 스팸 메일
특정 서버를 이용해서 문자를 보내면 SK/KT의 서버 쪽으로 전달. 요청들을 차곡차곡 쌓아두고 하나씩 꺼내서 적절한 지역에 맞춰서 방송. 전달되는 모든 것들은 반드시 broker를 통과한다.
zmq는 기본적으로 소켓 프로그래밍이 1:1 통신밖에 지원 안한거에 비해 다양한것을 제공. 1:1 통신을 이용해 다양한 서비스들을 만듦. 재사용 가능한 일종의 서비스 개발 플랫폼을 만들었다. socket은 라이브러리로 치면 zmq는 좀더 상위개념.
🍏 ZMQ 패턴 및 샘플 코드 이해
✅ Request-Reply pattern
- client역할을 하는 sw / server 역할을 하는 sw
- client : request를 보내고
- server : reply를 보낸다.
socket과 비슷하다.
✅ REQ-REP : [Synchronous REQ-REP]
[server]
- import zmq
- socket을 바로 부르지 않고 zmq.Context()를 통해서 context 객체를 먼저 생성해야 한다.
- zmq.REP : REP 패턴을 사용하겠다~
- "tcp를 쓰겠다. 포트번호는 5555다"
- 무한루프 -> receive 함수를 호출하면 정보를 읽을 수 있어욤. 받으면 화면에 출력을 하고, 1초를 쉬고, 다시 돌려주는 방식
[client]
- zmq.REQ
- connect를 통해 bind하고 있는 server에 접속
- while문 -> 10번동안 hello 보내고 응답('world')받아서 화면에 출력하고~
✅ REQ-REP : [Autonomous 1:N REQ-REP]
- 수천수만개의 client가 만들어지더라도 아무것도 하지 않는다.
- 앞선 프로그램을 그냥 실행하면 된다.
- 서버를 먼저 실행시켜서 bind 상태로 만든 후 client를 하나하나 실행시킨다.
✅ PUB-SUB Pattern
= Publish-Subscribe pattern
-
이제 socket과 달라짐
-
정보를 가지고 있고 배포하는 누군가가(Publisher) 있고, 그 정보를 수신하는 자(Subscriber)가 있다.
-
ex) 알람, 재난문자, 주식정보, 날씨 정보 등등
-
상대방이 구독을 한다면 업데이트할 때마다 무차별적으로 배포를 하는
-
TCP건 UDP건 server는 먼저 떠있어야 한다고 했다. server : bind 하는 주체야! 라고 했다. 근데 지금 부터는 개념이 좀 달라짐. 기능적으로 봣을때 접근을 당하는 애들이 bind 하게 되고 기다리게 된다. server라고 bind 하는 것이 X. 지금의 경우 정보를 제공하는 자가 bind를 하는 것이 맞음. subscriber들은 publisher에 connect하는 형식으로 동작
-
publisher가 subscriber들로부터 connect를 받고 publisher가 정보를 push하면 n개의 subscriber들이 받는다.
example) weather braodcasting
- 날씨 정보를 가지고 있는 컴퓨터가 있고 접속하는 사용자들이 있다.
- 누가 connect 하던 동일하게 전송 예정
- client는 필요한 정보만 볼 수 있도록 세팅할 수 있다. 따라서 client는 setsockopt를 통해 socket을 열면서 option을 줄 수 있다. 서버의 수많은 정보 중에 필요한 정보만 가져와서 쓸 수 있다.
[server]
-
zmq.context()만들고 socket 열기
-
이번에는 zmq.PUB 패턴
-
publish하는 애를 먼저 띄워야 하니 bind() / 5556번 포트 이용
-
우편번호 / 온도 / 습도 들을 send_string을 통해 subscribers에게 문자열 send.
-
server 코드에 client의 동작에 대한 코드는 X 개꿀 zmq 안에 알아서 동작이 들어가있음.
[client]
- import sys : 우편번호를 직접 받기 위해서(입력 파라메터)
- zeq.SUB
- server인 5556에 connect
- 접속해서 모든 정보를 보는 게 아니라 우편번호에 - setsockopt_string을 통해서 zip_filter를 줬어요. zmq는 subscribe 패턴을 써요~를 다시 정의. filter에 대한 정보만 가져올 수 있게 된다.
- for문 20번 반복 - filter를 통화한 것만 recv_string 가능.
- split을 통해서 온도/습도 등 나눠서 저장~ 평균도 구해주넹. 이건 zmq랑은 관계 X. 그냥 이 프로그램의 기능
✅ PUB-SUB Socket Characteristics
- 기본적으로 비동기식
보내고 싶을때 받고싶을때 받음
- undirectional
일방적으로 받거나 일방적으로 보냄. 뒤쪽에서는 섞어서 쓰기도 함.
✅ Publish-Sebscribe with Pipeline pattern
Pipeline Pattern
= PUSH-PULL pattern
병렬적으로 일을 분산해서 처리한 후 결과를 하나로 모으자! = pipeline
- fan-out : 하나에서 여러개로 퍼지는것
- fan-in : 여러개가 하나로 모이는 것
여기서 push하는 정보들은 대부분 달라용. R1 / R4 / 등등.. PULL은 이 전부를 받아서 queue(buffer)에 촵촵 저장. 코드에 손을 대면 누구로부터 먼저 받을지도 조정이 가능.
PUSH/PULL example
-
Server는 publish도 하고 pull도 한다.
pull : 어디서 정보를 가져온다
publish : 내 정보를 n개에 보낸다
-> 서버는 pull이 될 수도 pub , req 등ㄷ..등 여러개의 pattern을 가질수 있다!
-
client들은 server가 publish한 정보들을 받을 수 있다(sub). push도 함(client의 상태정보. 어느 개인이 availiable한지 등에 대한 정보들을 push. server에게 ). 그럼 server는 다른 client들에게 정보를 뿌릴 수 있다.
[server]
- pub/pull 동시에 가능
- client들이 주기적으로 정보를 보냅니다. 본인이 필요할때 pull 하고, 모든 client에게 pub
[client]
- sub / push 가능
- 본인의 상태 정보를 server에 push
- 다른 client들의 상태는 server를 통해서 받음
Sample Code
[server]
내가 만든 프로그램이 interpreter에게 주어진 최초의 python 소스코드인가에 대한 검사 : if__name main -- 그부분
- context 열고 PUB / PULL 둘다 있음
- socket을 두개 열어고, 이름도 다르게 함 publisher / collector(client들의 상태 정보들을 모아서 관리하고있다~ 의미)
- 얘가 bind 해줌.
- 반복분 - collector가 client들이 push 한 정보를 pull 해서 다른 publisher들에게 보냄.
- collector가 receive하고 publisher를 통해서 보내버린다. 정보에 대한 관심 X 암것도 안하고 보내기만
[client]
- SUB / PUSH 를 위한 socket 열기 & 옵션걸고 & connect
- b '' : 뒤의 문자가 아스키 코드일 것이다.
- setsockopt : 옵션 걸기
- 내가 필요로 할 떄 수신 buffer를 보고 있으면 처리. 바쁘면 나중에 처리
- poll : 수신 buffer에 데이터가 있어?
- POLLIN : flag
- if문으로 수신 buffer에 데이터 있는지 확인하고 그에 따라 처리.
- 다듬은 버전
프로그램 이름은 같은데 v2한 거는 client의 아이디를 사용자로부터 받도록 했습니다. main함수를 실행할 때 받음. -> sys.argv에 들어감
-> client가 id를 가지고 send할 때 id를 줘서 누가 보내는지 알 수 있도록 다듬음.
✅ Dealer-Router pattern
- 주로 req/rep를 고도화된 기능으로 확대할 때 사용
- req/rep의 비동기식 버전
- 1:n과 n:1이 섞여있을 때 사용
- Server : 라우터같은 기능
client의 요청을 적절한 worker에게 전달
client들이 본인이 원하는 기능을 server에게 요청. 그럼 server는 다시 이 일을 여러 명에게 뿌린다. pipeline이랑 헷갈릴 수 있긴 한데, 이 경우에는 클라이언트가 서버에게 요청하면 서버가 워커에게 일을 주고, 워커가 일을 마치면 서버가 받고, 다시 서버가 일을 요청했던 client에게 돌려준다.
- client는 보내고싶을 때 보내고 받을 때 받는다. 상대는 server 하나에요. server 입장에서는 상대가 여럿이니까 라우터 패턴을 사용.
✅ Asynchronous version of REQ-REP
- client는 server에게 연결을 하고 응답을 받는다.
- server는 client의 요청에 대해 응답들을 보낼 수 있다.
- client는 비동기식이기 때문에 보내고싶을 때 보내고, 기본적으로 여러개의 요청에 앞쪽 응답이 없더라도 보낼 수 있다.
- server도 여러개의 요청을 받으면 그에 대한 응답을 수시로 보낼 수 있다.
Code - [Server]
- proxy(router,dealer)
router - dealer 사이 정보를 그냥 forward한다.
받아서 다시 socket으로 내보내고, 반복문 만들고, rec, send 호출하고 난리쳐야되는데, zmq에서는 아무것도 안할 때는 proxy를 통해 그냥 둘을 연결해주자!
Code - [Client]
- 입력 파라메터로 받은 개수 만큼의 worker 생성하고 tcp/udp 방식이 아닌 내부통신 방식으로 연결
[전체 흐름]
- 클라이언트가 요청한 정보를 server의 router가 받아 proxy로 1:1 연결한 딜러로 전달. 딜러는 누구든 상관없으니 적정한 애에게 전달하고, 에코친걸 받은 dealer는 router 기능을 통해 client에게 전달
- fair queueing
아무 것도 하지 않으면 client의 요청을 받은 server는 workder에게 전달할 때 fair하게 전달함. w1 - w2 - w2.
✅ Dealer-Router pattern with Multi-thread Client
- client도 비동기식 방식으로
- 수신하는 것 분리
✅ Dirty P2P example
- P2P를 위해서는 통신을 하고자 하는 컴퓨터는 같은 네트워크에 있어서 network add는 동일하고 host add만 달라야 한다.
- too heavy
🍏 새로운 Web 기술의 도입
- 대부분 웹기술은 mozila foundation을 통해 표준화를 하기 때문에 fire fox에 가장 먼저 탑재됨.
- Java의 장점 : 어느 컴퓨터든 동작 가능 / virtual machine에서 돌아가는 프로그램을 만들기 때문.
Google이 크롬 브라우저를 만들어야할 이유
- 새로운 기술을 개발하기 원활하게 하도록
- Web 기술 성능 개선
웹브라우저는 OS에서 돌아가는 대부분의 어플리케이션을 올릴 수 있다
-> 그래서 요즘은 OS 안의 TCP IP를 쓰기보다는 본인이 만든다. application layer에서 구현하고 오픈소스화 해서 관심있는 사람들끼리 같이 만들기. 그러면 OS로부터 honorship을 가져올 수 있다.