"장고 컨슈머"의 두 판 사이의 차이
(→개요) |
|||
(같은 사용자의 중간 판 하나는 보이지 않습니다) | |||
4번째 줄: | 4번째 줄: | ||
=== 유의 === | === 유의 === | ||
− | * 동기 코드와 비동기 코드는 구현시간이 다름. 장고의 내용을 반영하면 곧장 새로고침되어 브라우저에 반영되지만, 컨슈머의 반영은 실시간 쓰레드로 이루어지기 때문에 동기 코드와 반영시간이 다른데, 더 늦기도 하다.(서버가 켜져서 동기페이지는 잘 나오는데, 비동기는 반영이 안되어 에러가 난 것으로 오해하게 되는 경우가 잦다.) | + | * 동기 코드와 비동기 코드는 구현시간이 다름. |
+ | ** 장고의 내용을 반영하면 곧장 새로고침되어 브라우저에 반영되지만, 컨슈머의 반영은 실시간 쓰레드로 이루어지기 때문에 동기 코드와 반영시간이 다른데, 더 늦기도 하다.(서버가 켜져서 동기페이지는 잘 나오는데, 비동기는 반영이 안되어 에러가 난 것으로 오해하게 되는 경우가 잦다.) | ||
+ | * 컨슈머는 한 번 실행 후 지속적으로 유지되는듯. | ||
+ | ** 모델 실행이 오래걸리는 챗봇 구현에서 한 번 컨슈머가 실행되고 나면 이후 응답은 굉장히 빠르다. | ||
== 형태 == | == 형태 == |
2022년 11월 12일 (토) 08:43 기준 최신판
1 개요[편집 | 원본 편집]
django concumer. 장고 채널에서 사용하는 컨슈머에 대한 설명을 위한 문서.
1.1 유의[편집 | 원본 편집]
- 동기 코드와 비동기 코드는 구현시간이 다름.
- 장고의 내용을 반영하면 곧장 새로고침되어 브라우저에 반영되지만, 컨슈머의 반영은 실시간 쓰레드로 이루어지기 때문에 동기 코드와 반영시간이 다른데, 더 늦기도 하다.(서버가 켜져서 동기페이지는 잘 나오는데, 비동기는 반영이 안되어 에러가 난 것으로 오해하게 되는 경우가 잦다.)
- 컨슈머는 한 번 실행 후 지속적으로 유지되는듯.
- 모델 실행이 오래걸리는 챗봇 구현에서 한 번 컨슈머가 실행되고 나면 이후 응답은 굉장히 빠르다.
2 형태[편집 | 원본 편집]
항목 | SyncConsumer | AsyncConsumer |
---|---|---|
목적 | 장고 ORM을 사용하는 경우.(동기식으로 쓰레드 안에서 실행)
기본적으로 이걸 사용하길 권장한다. |
비동기 코드를 수행하기 위해.(성능적인 면에서 우위)
비동기 라이브러리들을 사용하거나 성능을 향상시켜줄 수 있을 때 사용하길 권장한다. 데이터베이스 사용도 가능하다.(database_sync_to_async를 참고하자.) |
비동기에서 동기코드를 쓰면 다른 이벤트를 못받는건가?????
아마 쓰레드를 쓰지 않고 순차적으로 작동하는 듯한데, 이게 왜 문제가 되는지 모르겠다;;; | ||
DB를 쓰기 위한 방법이 없진 않다.
굳이 비동기방식에서 DB를 사용하고자 한다면 database_sync_to_async를 사용하면 된다. |
2.1 Generic consumer[편집 | 원본 편집]
뷰에서 함수형 뷰와 제너릭 뷰가 있었듯, 컨슈머도 여러 작업을 알아서 처리해주는 제네릭 컨슈머가 있다. 일반 컨슈머와의 차이는 알아서 접속종료 처리 등을 해준다는 것. 딱히 일반 컨슈머를 쓰는 이유는 찾지 못했다.
3 컨슈머 각 항목에 대하여[편집 | 원본 편집]
3.1 Consumer.py[편집 | 원본 편집]
컨슈머는 클래스로 구현되어 connect, disconnect 등으로 구성된다.
항목 | 설명 | 예시 |
---|---|---|
컨슈머 정의 | 클래스로 구현한다. | from channels.generic.websocket import WebsocketConsumer
from asgiref.sync import async_to_sync # 필요에 따라.
class 컨슈머이름(WebsocketConsumer):
from channels.generic.websocket import AsyncWebsocketConsumer
class 컨슈머이름(AsyncWebsocketConsumer):
|
3.1.1 connect[편집 | 원본 편집]
항목 | 설명 | 예시 | ||||||||
---|---|---|---|---|---|---|---|---|---|---|
사전 설정 | 각종 변수들의 조작을 먼저 한다.
스코프로 받아서 변수들을 조작하는데, 그 구조는 다음과 같다.
|
self.group_name = self.scope["url_route"]["kwargs"]["room_name"] | ||||||||
그룹 참여 | 그룹을 지정하여 참여시킨다. | async_to_sync(self.channel_layer.group_add)(
self.group_name, # 위에서 설정하여 넣어준다.
self.channel_name # 자동으로 고유하게 지정된다.
)
await self.channel_layer.group_add(self.group_name, self.channel_name)
| ||||||||
연결 | 위 설정을 토대로 연결한다.
연결 이후 필요한 명령을 넣기도 한다. 연결과 동시에 다음과 같이 신호를 보내기도 한다.notifications = get_notification()
if notifications:
async_to_sync(self.channel_layer.group_send)(
"center_name", {
"type": "notify",
"data": notifications
}
)
|
self.accept()
await self.accept()
| ||||||||
서브프로토콜 | 서브프로토콜을 지정해 연결할 수도 있다. | self.accept('subprotocol")
# self.scope['subprotocols'] 을 통해 관련 정보를 알 수 있다. |
3.1.2 disconnect[편집 | 원본 편집]
항목 | 설명 | 예시 |
---|---|---|
정의 | 연결을 끊기만 할 테니, 기본 설정은 간단하다.
적당히 끊어질 때의 명령을 넣기도 한다. |
def disconnect(self, close_code):
async_to_sync(self.channel_layer.group_discard)(
self.group_name,
self.channel_name
)
async def disconnect(self, close_code):
await self.channel_layer.group_discard(self.group_name, self.channel_name)
|
종료에러코드 | receive에서 특정 과정을 거쳐 self.close(code=1234) 등으로 커스텀에러를 낼 수도 있다. | disconnect에서 코드를 담아 상황에 맞게 작동, 반환한다. |
3.1.3 receive[편집 | 원본 편집]
타인으로부터 메시지를 받는 게 아니라, 자바스크립트로부터 받는 메시지와 관련된 함수이다.(웹소켓에서 메시지를 받을 때.)
항목 | 설명 | 예시 | ||||||
---|---|---|---|---|---|---|---|---|
정의 | 본인의 자바스크립트에서 웹소켓으로 메시지를 보낸 경우.
자바스크립트에서 데이터가 넘어올 땐 json으로 변환되어 오기 때문에 우측과 같이 구성해야 한다.
type의 이름이 chat.message 라면 chat_message로 변경되어 처리된다.
|
def receive(self, text_data):
text_data_json = json.loads(text_data)
message = text_data_json['message']
# 나 뿐 아니라 다른 사람들도 보게끔.
self.send(text_data=json.dumps({
'message': message
}))
async def receive(self, text_data):
text_data_json = json.loads(text_data)
message = text_data_json["message"]
# 나 뿐만 아니라 다른사람들도 보도록.
await self.channel_layer.group_send(
self.room_group_name, {"type": "chat_message",
"message": message}
)
|
3.1.4 이벤트[편집 | 원본 편집]
다른 사용자들로부터 메시지를 받는 등의 이벤트가 나타났을 때.
항목 | 설명 | 예시 |
---|---|---|
정의 | 함수의 이름은 메시지를 보낼 때의 type와 동일하게 짓는다.
type에 따라 다른 함수가 실행된다. (event가 들어오면 자동으로 실행되는 듯하다.) 웹소켓(자바스크립트)으로 보낼 땐 json으로 변환하여 보내주어야 한다. |
def chat_message(self, event): # 변수명으로 event 대신 text를 사용하기도 한다.
message = event['message']
# Send message to WebSocket
self.send(text_data=json.dumps({
'message': message
}))
async def chat_message(self, event):
message = event["message"]
# Send message to WebSocket
await self.send(text_data=json.dumps({"message": message}))
|
보내는 함수의 차이. |
3.1.5 자바스크립트에서[편집 | 원본 편집]
항목 | 설명 | 예시 |
---|---|---|
웹소켓 생성 | 스크립트의 초반에 우측과 같이 주소를 지정하여 소켓을 생성한다.
roomname과 같은 변수는 위에서 받아 넘겨주어야 한다.
이벤트의 활용은 아래 예시를 참고하자. |
const chatSocket = new WebSocket(
'ws://' + window.location.host
+ '/ws/chat/'
+ roomName
+ '/'
);
|
message | 웹소켓이 데이터를 수신할 때 발생 | chatSocket.onmessage = function(e) {
const data = JSON.parse(e.data);
document.querySelector('#chat-log').value += (data.message + '\n');
};
|
close | chatSocket.onclose = function(e) {
console.error('Chat socket closed unexpectedly');
};
| |
메시지 보내기 | 만든 소켓으로 메시지 보낼 때.
JavaScript 값이나 객체를 JSON 문자열로 변환해야만 한다. |
chatSocket.send(JSON.stringify({
'message': message
}));
|
4 Scope[편집 | 원본 편집]
뷰에 request가 있다면 컨슈머엔 scope가 있다.
request와 동일하게 사용자가 입력한 값, 기타정보들을 사전형태로 담고 있다.(담은 키도 request와 거의 동일하다고 보면 된다.)
4.1 속성[편집 | 원본 편집]
속성 | 설명 | 활용 |
---|---|---|
method | GET인가 POST인가의 정보. | if self.scope['method'] == POST: |
user | 요청자의 user계정이 담긴다. | self.scope['user'] |