Websocket phát trực tuyến video python

Hôm nay, chúng tôi rất vui mừng được đón tiếp Jacob Kaplan-Moss. Jacob là cựu Herokai và là người đóng góp cốt lõi trong thời gian dài cho Django, và anh ấy ở đây để chia sẻ cái nhìn sâu sắc về điều gì đó mà anh ấy tin rằng sẽ xác định tương lai của khuôn khổ

Khi Django được tạo ra, hơn mười năm trước, web là một nơi ít phức tạp hơn. Phần lớn các trang web là tĩnh. Các ứng dụng web kiểu Mô hình/Chế độ xem/Trình điều khiển được hỗ trợ bởi cơ sở dữ liệu là thứ hấp dẫn mới. Ajax hầu như không bắt đầu được sử dụng và chỉ trong những ngữ cảnh hẹp

Web vào khoảng năm 2016 mạnh hơn đáng kể. Vài năm gần đây đã chứng kiến ​​sự nổi lên của cái gọi là web “thời gian thực”. các ứng dụng có sự tương tác cao hơn nhiều giữa máy khách và máy chủ và mạng ngang hàng. Các ứng dụng bao gồm nhiều dịch vụ [một. k. a. microservice] là tiêu chuẩn. Và, các công nghệ web mới cho phép các ứng dụng web đi theo hướng mà chúng ta chỉ có thể mơ tới cách đây một thập kỷ. Một trong những công nghệ cốt lõi này là WebSockets. một giao thức mới cung cấp giao tiếp song công hoàn toàn — một kết nối mở, liên tục giữa máy khách và máy chủ, có thể gửi dữ liệu bất cứ lúc nào

Trong thế giới mới này, Django cho thấy tuổi của nó. Về cốt lõi, Django được xây dựng dựa trên khái niệm đơn giản về yêu cầu và phản hồi. trình duyệt đưa ra yêu cầu, Django gọi một chế độ xem, trả về phản hồi được gửi lại cho trình duyệt

Điều này không hoạt động với WebSockets. Chế độ xem chỉ tồn tại trong vòng đời của một yêu cầu và không có cơ chế nào để giữ mở kết nối hoặc gửi dữ liệu xuống máy khách mà không có yêu cầu liên quan

Như vậy. Kênh Django. Tóm lại, các kênh thay thế “phần ruột” của Django - chu kỳ yêu cầu/phản hồi - bằng các thông báo được gửi qua các kênh. Kênh cho phép Django hỗ trợ WebSockets theo cách rất giống với chế độ xem HTTP truyền thống. Các kênh cũng cho phép các tác vụ nền chạy trên cùng các máy chủ như phần còn lại của Django. Các yêu cầu HTTP tiếp tục hoạt động giống như trước đây nhưng cũng được định tuyến qua các kênh. Do đó, bên dưới Kênh Django bây giờ trông như thế này

Như bạn có thể thấy, Kênh giới thiệu một số khái niệm mới cho Django

Kênh về cơ bản là hàng đợi nhiệm vụ. thông điệp được nhà sản xuất đẩy lên kênh và sau đó được đưa đến một trong những người tiêu dùng đang nghe trên kênh đó. Nếu bạn đã sử dụng các kênh trong Go, khái niệm này sẽ khá quen thuộc. Sự khác biệt chính là các kênh Django hoạt động trên một mạng và cho phép các nhà sản xuất và người tiêu dùng chạy một cách minh bạch trên nhiều dyno và/hoặc máy móc. Lớp mạng này được gọi là lớp kênh. Các kênh được thiết kế để sử dụng Redis làm lớp kênh ưa thích của nó, mặc dù có hỗ trợ cho các loại khác [và API hạng nhất để tạo các lớp kênh tùy chỉnh]. Có các chi tiết kỹ thuật gọn gàng và tinh tế;

Hiện tại, Kênh có sẵn dưới dạng ứng dụng độc lập hoạt động với Django 1. 9. Kế hoạch là đưa các Kênh vào Django trong 1. 10, dự kiến ​​ra mắt vào mùa hè này

Tôi tin rằng Kênh sẽ là một bổ sung quan trọng đáng kinh ngạc cho Django. họ cho phép Django chuyển sang thời đại phát triển web mới này một cách suôn sẻ. Mặc dù các API này chưa phải là một phần của Django nhưng chúng sẽ sớm có. Vì vậy, bây giờ là thời điểm hoàn hảo để bắt đầu tìm hiểu về Kênh. bạn có thể tìm hiểu về tương lai của Django trước khi nó hạ cánh

Ví dụ, tôi đã xây dựng một ứng dụng trò chuyện thời gian thực đơn giản — giống như một ứng dụng Slack rất, rất nhẹ. Có rất nhiều phòng và mọi người trong cùng một phòng có thể trò chuyện theo thời gian thực với nhau [sử dụng WebSockets]

Bạn có thể truy cập trực tuyến triển khai ví dụ của tôi, kiểm tra mã trên GitHub hoặc triển khai bản sao của riêng bạn bằng nút này [yêu cầu tài khoản Heroku miễn phí, vì vậy hãy đăng ký tài khoản đó trước]

Ghi chú. bạn sẽ cần mở rộng loại quy trình

def chat_room[request, label]:
    # If the room with the given label doesn't exist, automatically create it
    # upon first visit [a la etherpad].
    room, created = Room.objects.get_or_create[label=label]

    # We want to show the last 50 messages, ordered most-recent-last
    messages = reversed[room.messages.order_by['-timestamp'][:50]]

    return render[request, "chat/room.html", {
        'room': room,
        'messages': messages,
    }]
7 sau khi sử dụng nút. Sử dụng Bảng điều khiển hoặc chạy
def chat_room[request, label]:
    # If the room with the given label doesn't exist, automatically create it
    # upon first visit [a la etherpad].
    room, created = Room.objects.get_or_create[label=label]

    # We want to show the last 50 messages, ordered most-recent-last
    messages = reversed[room.messages.order_by['-timestamp'][:50]]

    return render[request, "chat/room.html", {
        'room': room,
        'messages': messages,
    }]
8

Để biết chi tiết chuyên sâu về cách thức hoạt động của ứng dụng — bao gồm cả lý do tại sao bạn cần

def chat_room[request, label]:
    # If the room with the given label doesn't exist, automatically create it
    # upon first visit [a la etherpad].
    room, created = Room.objects.get_or_create[label=label]

    # We want to show the last 50 messages, ordered most-recent-last
    messages = reversed[room.messages.order_by['-timestamp'][:50]]

    return render[request, "chat/room.html", {
        'room': room,
        'messages': messages,
    }]
7 dynos. - đọc tiếp. Tôi sẽ hướng dẫn các bước cần thực hiện để xây dựng ứng dụng này, đồng thời làm nổi bật các khái niệm và thông tin nhập trong quá trình thực hiện

Mặc dù có những khác biệt bên dưới, nhưng đây vẫn là Django mà chúng tôi đã sử dụng trong mười năm qua. Vì vậy, các bước ban đầu giống như bất kỳ ứng dụng Django nào. [Nếu bạn là người mới sử dụng Django, bạn có thể muốn xem và/hoặc Hướng dẫn dành cho các cô gái Django. ] Sau khi tạo một dự án, chúng ta có thể xác định mô hình để đại diện cho một phòng trò chuyện và các tin nhắn trong đó [chat/models. py]

class Room[models.Model]:
    name = models.TextField[]
    label = models.SlugField[unique=True]

class Message[models.Model]:
    room = models.ForeignKey[Room, related_name='messages']
    handle = models.TextField[]
    message = models.TextField[]
    timestamp = models.DateTimeField[default=timezone.now, db_index=True]

[Trong ví dụ này và tất cả các ví dụ tiếp theo, tôi đã cắt mã xuống mức tối thiểu để chúng ta có thể tập trung vào các phần quan trọng. Để biết toàn bộ mã chi tiết lộn xộn, hãy xem dự án trên Github. ]

Và sau đó là một chế độ xem [trò chuyện/lượt xem. py và các url liên quan. py và trò chuyện/phòng. mẫu html]

def chat_room[request, label]:
    # If the room with the given label doesn't exist, automatically create it
    # upon first visit [a la etherpad].
    room, created = Room.objects.get_or_create[label=label]

    # We want to show the last 50 messages, ordered most-recent-last
    messages = reversed[room.messages.order_by['-timestamp'][:50]]

    return render[request, "chat/room.html", {
        'room': room,
        'messages': messages,
    }]

Tại thời điểm này, chúng tôi có một chức năng - nếu không thú vị - ứng dụng Django. Nếu bạn chạy ứng dụng này với bản cài đặt Django tiêu chuẩn, bạn sẽ có thể xem các phòng trò chuyện và bản ghi hiện có, nhưng không thể tương tác với chúng theo bất kỳ cách nào. Trò chuyện thời gian thực không hoạt động — vì điều đó, chúng tôi cần thứ gì đó có thể xử lý WebSockets

Để nắm được những gì cần thực hiện ở phần phụ trợ, hãy xem mã máy khách. Bạn có thể tìm thấy nó trong trò chuyện. js — và không có nhiều. Đầu tiên, chúng tôi tạo một websocket

var ws_scheme = window.location.protocol == "" ? "wss" : "ws";
var chat_socket = new ReconnectingWebSocket[ws_scheme + '://' + window.location.host + "/chat" + window.location.pathname];

Lưu ý rằng

  • Giống như HTTP và HTTPS, giao thức WebSocket có các loại bảo mật [WSS] và không bảo mật [WS]. Chúng ta cần đảm bảo sử dụng hương vị “đúng”

  • Bởi vì, tôi sử dụng một miếng đệm nhỏ xung quanh WebSocket của trình duyệt để tự động kết nối lại nếu ổ cắm bị ngắt kết nối. [Cảm ơn Kenneth Reitz, người đã chỉ ra điều này trong ví dụ Flask WebSocket của anh ấy. ]

Tiếp theo, chúng tôi sẽ kết nối một cuộc gọi lại để khi biểu mẫu được gửi, chúng tôi sẽ gửi dữ liệu qua WebSocket [thay vì POST nó]

$['#chatform'].on['submit', function[event] {
    var message = {
        handle: $['#handle'].val[],
        message: $['#message'].val[],
    }
    chat_socket.send[JSON.stringify[message]];
    return false;
}];

Chúng tôi có thể gửi bất kỳ dữ liệu văn bản nào chúng tôi muốn qua WebSocket. Giống như hầu hết các API, JSON là dễ nhất, vì vậy chúng tôi sẽ gộp dữ liệu dưới dạng JSON và gửi theo cách đó

Cuối cùng, chúng ta cần kết nối một cuộc gọi lại để kích hoạt khi nhận được dữ liệu mới trên WebSocket

chatsock.onmessage = function[message] {
    var data = JSON.parse[message.data];
    $['#chat'].append['' 
        + '' + data.timestamp + '' 
        + '' + data.handle + ''
        + '' + data.message + ' '
    + ''];
};

thứ đơn giản. chỉ cần thêm một hàng vào bảng phiên âm của chúng tôi, lấy dữ liệu ra khỏi tin nhắn đã nhận. Nếu tôi chạy mã này bây giờ, nó sẽ không hoạt động — không có gì đang nghe các kết nối WebSocket, chỉ có HTTP. Vì vậy, bây giờ, hãy chuyển sang kết nối WebSockets

Để “kênh hóa” ứng dụng này, chúng ta cần thực hiện ba việc. cài đặt Kênh, thiết lập lớp kênh, xác định định tuyến kênh và sửa đổi dự án của chúng tôi để chạy trong Kênh [chứ không phải WSGI]

Để cài đặt Kênh, chỉ cần

var ws_scheme = window.location.protocol == "" ? "wss" : "ws";
var chat_socket = new ReconnectingWebSocket[ws_scheme + '://' + window.location.host + "/chat" + window.location.pathname];
0, sau đó thêm
var ws_scheme = window.location.protocol == "" ? "wss" : "ws";
var chat_socket = new ReconnectingWebSocket[ws_scheme + '://' + window.location.host + "/chat" + window.location.pathname];
1 vào cài đặt
var ws_scheme = window.location.protocol == "" ? "wss" : "ws";
var chat_socket = new ReconnectingWebSocket[ws_scheme + '://' + window.location.host + "/chat" + window.location.pathname];
2 của bạn. Việc cài đặt Kênh cho phép Django chạy ở “chế độ kênh”, hoán đổi chu kỳ yêu cầu/phản hồi với kiến ​​trúc dựa trên kênh được mô tả ở trên. [Để tương thích ngược, bạn vẫn có thể chạy Django ở chế độ WSGI, nhưng WebSockets và tất cả các tính năng Kênh khác sẽ không hoạt động ở chế độ tương thích ngược này. ]

Tiếp theo, chúng ta cần xác định một lớp kênh. Đây là cơ chế vận chuyển mà Kênh sử dụng để chuyển thông điệp từ nhà sản xuất [người gửi thông báo] đến người tiêu dùng. Đó là một loại hàng đợi tin nhắn với một số thuộc tính cụ thể []

Chúng tôi sẽ sử dụng Redis cho lớp kênh của chúng tôi. đó là lớp kênh chất lượng sản xuất được ưu tiên và là lựa chọn hiển nhiên khi triển khai lên Heroku. Ngoài ra còn có các lớp kênh trong bộ nhớ và cơ sở dữ liệu được hỗ trợ, nhưng những lớp này phù hợp hơn với sự phát triển cục bộ hoặc sử dụng lưu lượng truy cập thấp. [Để biết thêm chi tiết, một lần nữa tham khảo tài liệu. ]

Nhưng trước tiên. bởi vì lớp kênh Redis được triển khai trong một gói khác, chúng tôi sẽ cần

var ws_scheme = window.location.protocol == "" ? "wss" : "ws";
var chat_socket = new ReconnectingWebSocket[ws_scheme + '://' + window.location.host + "/chat" + window.location.pathname];
3. [Tôi sẽ nói thêm một chút về “ASGI” là gì bên dưới. ] Sau đó, chúng tôi xác định lớp kênh của mình trong cài đặt
var ws_scheme = window.location.protocol == "" ? "wss" : "ws";
var chat_socket = new ReconnectingWebSocket[ws_scheme + '://' + window.location.host + "/chat" + window.location.pathname];
4

def chat_room[request, label]:
    # If the room with the given label doesn't exist, automatically create it
    # upon first visit [a la etherpad].
    room, created = Room.objects.get_or_create[label=label]

    # We want to show the last 50 messages, ordered most-recent-last
    messages = reversed[room.messages.order_by['-timestamp'][:50]]

    return render[request, "chat/room.html", {
        'room': room,
        'messages': messages,
    }]
3

Lưu ý rằng chúng tôi đang rút URL kết nối Redis ra khỏi môi trường, để chứng minh trong tương lai khi chúng tôi triển khai lên Heroku

Trong

var ws_scheme = window.location.protocol == "" ? "wss" : "ws";
var chat_socket = new ReconnectingWebSocket[ws_scheme + '://' + window.location.host + "/chat" + window.location.pathname];
4, chúng tôi đã cho Kênh biết nơi tìm định tuyến kênh của chúng tôi —
var ws_scheme = window.location.protocol == "" ? "wss" : "ws";
var chat_socket = new ReconnectingWebSocket[ws_scheme + '://' + window.location.host + "/chat" + window.location.pathname];
6. Định tuyến kênh là một khái niệm rất giống với định tuyến URL. Định tuyến URL ánh xạ các URL để xem các chức năng; . Cũng tương tự như
var ws_scheme = window.location.protocol == "" ? "wss" : "ws";
var chat_socket = new ReconnectingWebSocket[ws_scheme + '://' + window.location.host + "/chat" + window.location.pathname];
7, theo quy ước, các tuyến kênh sống trong một
var ws_scheme = window.location.protocol == "" ? "wss" : "ws";
var chat_socket = new ReconnectingWebSocket[ws_scheme + '://' + window.location.host + "/chat" + window.location.pathname];
8. Hiện tại, chúng ta sẽ chỉ tạo một cái trống

def chat_room[request, label]:
    # If the room with the given label doesn't exist, automatically create it
    # upon first visit [a la etherpad].
    room, created = Room.objects.get_or_create[label=label]

    # We want to show the last 50 messages, ordered most-recent-last
    messages = reversed[room.messages.order_by['-timestamp'][:50]]

    return render[request, "chat/room.html", {
        'room': room,
        'messages': messages,
    }]
8

[Chúng ta sẽ thấy một vài tuyến kênh bên dưới, khi chúng ta kết nối WebSockets. ]

Bạn sẽ nhận thấy rằng ứng dụng ví dụ của chúng tôi có cả

var ws_scheme = window.location.protocol == "" ? "wss" : "ws";
var chat_socket = new ReconnectingWebSocket[ws_scheme + '://' + window.location.host + "/chat" + window.location.pathname];
7 và
var ws_scheme = window.location.protocol == "" ? "wss" : "ws";
var chat_socket = new ReconnectingWebSocket[ws_scheme + '://' + window.location.host + "/chat" + window.location.pathname];
8. chúng tôi đang sử dụng cùng một ứng dụng để xử lý các yêu cầu HTTP và WebSockets. Điều này được mong đợi, và điển hình. Ứng dụng kênh vẫn là ứng dụng Django, vì vậy tất cả các tính năng mà bạn ngoại trừ từ Django — chế độ xem, biểu mẫu, mô hình, v.v. — tiếp tục hoạt động như trước khi có Kênh

Cuối cùng, chúng ta cần trao đổi trình xử lý yêu cầu dựa trên HTTP/WSGI của Django, để lấy một trình xử lý được tích hợp trong các kênh. Điều này dựa trên một tiêu chuẩn mới được gọi là ASGI [Giao diện cổng máy chủ không đồng bộ], vì vậy chúng tôi sẽ xác định trình xử lý đó trong tệp

$['#chatform'].on['submit', function[event] {
    var message = {
        handle: $['#handle'].val[],
        message: $['#message'].val[],
    }
    chat_socket.send[JSON.stringify[message]];
    return false;
}];
1

def chat_room[request, label]:
    # If the room with the given label doesn't exist, automatically create it
    # upon first visit [a la etherpad].
    room, created = Room.objects.get_or_create[label=label]

    # We want to show the last 50 messages, ordered most-recent-last
    messages = reversed[room.messages.order_by['-timestamp'][:50]]

    return render[request, "chat/room.html", {
        'room': room,
        'messages': messages,
    }]
2

[Trong tương lai, Django có thể sẽ tự động tạo tệp này, giống như hiện tại cho

$['#chatform'].on['submit', function[event] {
    var message = {
        handle: $['#handle'].val[],
        message: $['#message'].val[],
    }
    chat_socket.send[JSON.stringify[message]];
    return false;
}];
2. ]

Tại thời điểm này, nếu chúng tôi làm đúng mọi thứ, chúng tôi sẽ có thể chạy ứng dụng theo các kênh. Máy chủ giao diện Kênh được gọi là Daphne và chúng tôi có thể chạy ứng dụng của mình với nó như vậy

def chat_room[request, label]:
    # If the room with the given label doesn't exist, automatically create it
    # upon first visit [a la etherpad].
    room, created = Room.objects.get_or_create[label=label]

    # We want to show the last 50 messages, ordered most-recent-last
    messages = reversed[room.messages.order_by['-timestamp'][:50]]

    return render[request, "chat/room.html", {
        'room': room,
        'messages': messages,
    }]
4

** Nếu bây giờ chúng ta truy cập

$['#chatform'].on['submit', function[event] {
    var message = {
        handle: $['#handle'].val[],
        message: $['#message'].val[],
    }
    chat_socket.send[JSON.stringify[message]];
    return false;
}];
3, chúng ta sẽ thấy…. không có gì xảy ra. Điều này có thể gây nhầm lẫn, cho đến khi bạn nhớ rằng Kênh chia Django thành hai phần. máy chủ giao diện ngoại vi, Daphne và người tiêu dùng thông báo phụ trợ. Vì vậy, để thực sự xử lý các yêu cầu HTTP, chúng ta cần chạy một worker

def chat_room[request, label]:
    # If the room with the given label doesn't exist, automatically create it
    # upon first visit [a la etherpad].
    room, created = Room.objects.get_or_create[label=label]

    # We want to show the last 50 messages, ordered most-recent-last
    messages = reversed[room.messages.order_by['-timestamp'][:50]]

    return render[request, "chat/room.html", {
        'room': room,
        'messages': messages,
    }]
6

Bây giờ, yêu cầu nên đi qua. Điều này minh họa một cái gì đó khá gọn gàng. Các kênh tiếp tục xử lý tốt các yêu cầu HTTP[S] nhưng thực hiện theo một cách hoàn toàn khác. điều này không quá khác biệt so với việc chạy Celery với Django, nơi bạn sẽ chạy một nhân viên Celery cùng với máy chủ WSGI. Tuy nhiên, bây giờ, tất cả các tác vụ — yêu cầu HTTP, WebSockets, tác vụ nền — được chạy trong worker

[Nhân tiện, chúng tôi vẫn có thể chạy

$['#chatform'].on['submit', function[event] {
    var message = {
        handle: $['#handle'].val[],
        message: $['#message'].val[],
    }
    chat_socket.send[JSON.stringify[message]];
    return false;
}];
4 để kiểm tra cục bộ dễ dàng. Khi chúng tôi làm như vậy, các Kênh chỉ cần chạy Daphne và một nhân viên trong cùng một quy trình. ]

Được rồi, thiết lập đủ rồi;

Các kênh ánh xạ các kết nối WebSocket tới ba kênh

  • Một tin nhắn được gửi đến các kênh

    $['#chatform'].on['submit', function[event] {
        var message = {
            handle: $['#handle'].val[],
            message: $['#message'].val[],
        }
        chat_socket.send[JSON.stringify[message]];
        return false;
    }];
    
    5 khi một khách hàng mới lần đầu tiên [tôi. e. trình duyệt] kết nối qua WebSocket. Khi điều này xảy ra, chúng tôi sẽ ghi lại rằng khách hàng hiện đang “ở trong” một phòng trò chuyện nhất định

  • Mỗi tin nhắn mà máy khách gửi trên ổ cắm sẽ được gửi qua kênh

    $['#chatform'].on['submit', function[event] {
        var message = {
            handle: $['#handle'].val[],
            message: $['#message'].val[],
        }
        chat_socket.send[JSON.stringify[message]];
        return false;
    }];
    
    6. [Đây chỉ là những tin nhắn nhận được từ trình duyệt; hãy nhớ rằng các kênh là một chiều. Chúng ta sẽ xem cách gửi lại tin nhắn cho khách hàng sau. ] Khi nhận được tin nhắn, chúng tôi sẽ phát tin nhắn đó cho tất cả các khách hàng khác trong "phòng"

  • Cuối cùng, khi máy khách ngắt kết nối, một tin nhắn sẽ được gửi tới

    $['#chatform'].on['submit', function[event] {
        var message = {
            handle: $['#handle'].val[],
            message: $['#message'].val[],
        }
        chat_socket.send[JSON.stringify[message]];
        return false;
    }];
    
    7. Khi điều đó xảy ra, chúng tôi sẽ xóa khách hàng khỏi “phòng”

Đầu tiên, chúng ta cần kết nối từng kênh này trong

var ws_scheme = window.location.protocol == "" ? "wss" : "ws";
var chat_socket = new ReconnectingWebSocket[ws_scheme + '://' + window.location.host + "/chat" + window.location.pathname];
8

def chat_room[request, label]:
    # If the room with the given label doesn't exist, automatically create it
    # upon first visit [a la etherpad].
    room, created = Room.objects.get_or_create[label=label]

    # We want to show the last 50 messages, ordered most-recent-last
    messages = reversed[room.messages.order_by['-timestamp'][:50]]

    return render[request, "chat/room.html", {
        'room': room,
        'messages': messages,
    }]
0

Những thứ khá đơn giản. chỉ cần kết nối mỗi kênh với một chức năng thích hợp. Bây giờ, hãy xem xét các chức năng đó. Theo quy ước, chúng tôi sẽ đặt các chức năng này trong một

$['#chatform'].on['submit', function[event] {
    var message = {
        handle: $['#handle'].val[],
        message: $['#message'].val[],
    }
    chat_socket.send[JSON.stringify[message]];
    return false;
}];
9 [nhưng, giống như các chế độ xem, chúng có thể ở bất kỳ đâu. ]

Hãy nhìn vào

chatsock.onmessage = function[message] {
    var data = JSON.parse[message.data];
    $['#chat'].append['' 
        + '' + data.timestamp + '' 
        + '' + data.handle + ''
        + '' + data.message + ' '
    + ''];
};
0 trước

def chat_room[request, label]:
    # If the room with the given label doesn't exist, automatically create it
    # upon first visit [a la etherpad].
    room, created = Room.objects.get_or_create[label=label]

    # We want to show the last 50 messages, ordered most-recent-last
    messages = reversed[room.messages.order_by['-timestamp'][:50]]

    return render[request, "chat/room.html", {
        'room': room,
        'messages': messages,
    }]
1

[Để rõ ràng, tôi đã loại bỏ tất cả các xử lý lỗi và ghi nhật ký khỏi mã này. Để xem phiên bản đầy đủ, hãy xem người tiêu dùng. py trên GitHub]

Đây là dày đặc;

7. Máy khách sẽ kết nối với WebSocket bằng URL có dạng

chatsock.onmessage = function[message] {
    var data = JSON.parse[message.data];
    $['#chat'].append['' 
        + '' + data.timestamp + '' 
        + '' + data.handle + ''
        + '' + data.message + ' '
    + ''];
};
1, trong đó
chatsock.onmessage = function[message] {
    var data = JSON.parse[message.data];
    $['#chat'].append['' 
        + '' + data.timestamp + '' 
        + '' + data.handle + ''
        + '' + data.message + ' '
    + ''];
};
2 ánh xạ tới thuộc tính
chatsock.onmessage = function[message] {
    var data = JSON.parse[message.data];
    $['#chat'].append['' 
        + '' + data.timestamp + '' 
        + '' + data.handle + ''
        + '' + data.message + ' '
    + ''];
};
2 của Phòng. Vì tất cả các tin nhắn WebSocket [bất kể URL] được gửi đến cùng một nhóm người tiêu dùng kênh, chúng tôi sẽ cần tìm ra Phòng mà chúng tôi đang nói đến bằng cách phân tích cú pháp đường dẫn tin nhắn

Lưu ý rằng người tiêu dùng chịu trách nhiệm phân tích đường dẫn WebSocket bằng cách đọc

chatsock.onmessage = function[message] {
    var data = JSON.parse[message.data];
    $['#chat'].append['' 
        + '' + data.timestamp + '' 
        + '' + data.handle + ''
        + '' + data.message + ' '
    + ''];
};
4. Điều này khác với định tuyến URL truyền thống, trong đó các định tuyến
var ws_scheme = window.location.protocol == "" ? "wss" : "ws";
var chat_socket = new ReconnectingWebSocket[ws_scheme + '://' + window.location.host + "/chat" + window.location.pathname];
7 của Django dựa trên đường dẫn; . [Đây là nơi thể hiện khía cạnh “những ngày đầu” của Kênh; rất có thể các phiên bản Kênh trong tương lai sẽ bao gồm định tuyến URL WebSocket. ]

8. Bây giờ, chúng ta có thể tra cứu đối tượng

chatsock.onmessage = function[message] {
    var data = JSON.parse[message.data];
    $['#chat'].append['' 
        + '' + data.timestamp + '' 
        + '' + data.handle + ''
        + '' + data.message + ' '
    + ''];
};
6 từ cơ sở dữ liệu

9. Dòng này là chìa khóa để trò chuyện hoạt động. Chúng tôi cần biết cách gửi tin nhắn lại cho khách hàng này. Đối với điều đó, chúng tôi sẽ sử dụng thông báo của

chatsock.onmessage = function[message] {
    var data = JSON.parse[message.data];
    $['#chat'].append['' 
        + '' + data.timestamp + '' 
        + '' + data.handle + ''
        + '' + data.message + ' '
    + ''];
};
7 - mọi thông báo sẽ có thuộc tính
chatsock.onmessage = function[message] {
    var data = JSON.parse[message.data];
    $['#chat'].append['' 
        + '' + data.timestamp + '' 
        + '' + data.handle + ''
        + '' + data.message + ' '
    + ''];
};
7, chúng tôi có thể sử dụng thuộc tính này để gửi lại thông báo cho khách hàng. [Chúng ta không cần tự tạo kênh đó; Các kênh tạo kênh đó cho chúng ta. ]

Tuy nhiên, sẽ không đủ tốt nếu chỉ gửi tin nhắn đến một kênh đó; . Đối với điều đó, chúng tôi sẽ sử dụng một nhóm kênh. Nhóm chỉ đơn giản là sự kết nối của các kênh mà bạn có thể phát tin nhắn tới. Vì vậy, chúng tôi sẽ thêm

chatsock.onmessage = function[message] {
    var data = JSON.parse[message.data];
    $['#chat'].append['' 
        + '' + data.timestamp + '' 
        + '' + data.handle + ''
        + '' + data.message + ' '
    + ''];
};
7 của tin nhắn này vào một nhóm dành riêng cho phòng trò chuyện này

10. Cuối cùng, các tin nhắn tiếp theo [nhận/ngắt kết nối] sẽ không chứa URL nữa [vì kết nối đã hoạt động]. Vì vậy, chúng ta cần một cách để “ghi nhớ” phòng mà kết nối WebSocket ánh xạ tới. Đối với điều này, chúng ta có thể sử dụng một phiên kênh. Phiên kênh rất giống với khung phiên của Django. chúng lưu giữ dữ liệu giữa các thông báo kênh trên một thuộc tính có tên là

def chat_room[request, label]:
    # If the room with the given label doesn't exist, automatically create it
    # upon first visit [a la etherpad].
    room, created = Room.objects.get_or_create[label=label]

    # We want to show the last 50 messages, ordered most-recent-last
    messages = reversed[room.messages.order_by['-timestamp'][:50]]

    return render[request, "chat/room.html", {
        'room': room,
        'messages': messages,
    }]
30. Thêm trình trang trí
def chat_room[request, label]:
    # If the room with the given label doesn't exist, automatically create it
    # upon first visit [a la etherpad].
    room, created = Room.objects.get_or_create[label=label]

    # We want to show the last 50 messages, ordered most-recent-last
    messages = reversed[room.messages.order_by['-timestamp'][:50]]

    return render[request, "chat/room.html", {
        'room': room,
        'messages': messages,
    }]
31 cho người tiêu dùng là tất cả những gì chúng ta cần để phiên hoạt động. [Tài liệu có]

Bây giờ một khách hàng đã được kết nối, hãy xem

def chat_room[request, label]:
    # If the room with the given label doesn't exist, automatically create it
    # upon first visit [a la etherpad].
    room, created = Room.objects.get_or_create[label=label]

    # We want to show the last 50 messages, ordered most-recent-last
    messages = reversed[room.messages.order_by['-timestamp'][:50]]

    return render[request, "chat/room.html", {
        'room': room,
        'messages': messages,
    }]
32. Người tiêu dùng này sẽ được gọi mỗi khi nhận được tin nhắn trên WebSocket

def chat_room[request, label]:
    # If the room with the given label doesn't exist, automatically create it
    # upon first visit [a la etherpad].
    room, created = Room.objects.get_or_create[label=label]

    # We want to show the last 50 messages, ordered most-recent-last
    messages = reversed[room.messages.order_by['-timestamp'][:50]]

    return render[request, "chat/room.html", {
        'room': room,
        'messages': messages,
    }]
2

[Một lần nữa, tôi đã loại bỏ việc xử lý lỗi và ghi nhật ký cho rõ ràng. ]

Một vài dòng đầu tiên khá đơn giản. trích xuất căn phòng từ

def chat_room[request, label]:
    # If the room with the given label doesn't exist, automatically create it
    # upon first visit [a la etherpad].
    room, created = Room.objects.get_or_create[label=label]

    # We want to show the last 50 messages, ordered most-recent-last
    messages = reversed[room.messages.order_by['-timestamp'][:50]]

    return render[request, "chat/room.html", {
        'room': room,
        'messages': messages,
    }]
33, tra cứu nó trong cơ sở dữ liệu, phân tích cú pháp thông báo JSON và lưu trữ thông báo trong cơ sở dữ liệu dưới dạng đối tượng
def chat_room[request, label]:
    # If the room with the given label doesn't exist, automatically create it
    # upon first visit [a la etherpad].
    room, created = Room.objects.get_or_create[label=label]

    # We want to show the last 50 messages, ordered most-recent-last
    messages = reversed[room.messages.order_by['-timestamp'][:50]]

    return render[request, "chat/room.html", {
        'room': room,
        'messages': messages,
    }]
34. Sau đó, tất cả những gì chúng ta phải làm là phát tin nhắn mới này cho mọi người trong phòng trò chuyện và đối với điều này, chúng ta sẽ sử dụng cùng một nhóm kênh như trước đây.
def chat_room[request, label]:
    # If the room with the given label doesn't exist, automatically create it
    # upon first visit [a la etherpad].
    room, created = Room.objects.get_or_create[label=label]

    # We want to show the last 50 messages, ordered most-recent-last
    messages = reversed[room.messages.order_by['-timestamp'][:50]]

    return render[request, "chat/room.html", {
        'room': room,
        'messages': messages,
    }]
35 sẽ đảm nhận việc gửi tin nhắn này tới mọi
chatsock.onmessage = function[message] {
    var data = JSON.parse[message.data];
    $['#chat'].append['' 
        + '' + data.timestamp + '' 
        + '' + data.handle + ''
        + '' + data.message + ' '
    + ''];
};
7 được thêm vào nhóm

Sau đó,

def chat_room[request, label]:
    # If the room with the given label doesn't exist, automatically create it
    # upon first visit [a la etherpad].
    room, created = Room.objects.get_or_create[label=label]

    # We want to show the last 50 messages, ordered most-recent-last
    messages = reversed[room.messages.order_by['-timestamp'][:50]]

    return render[request, "chat/room.html", {
        'room': room,
        'messages': messages,
    }]
37 rất đơn giản

def chat_room[request, label]:
    # If the room with the given label doesn't exist, automatically create it
    # upon first visit [a la etherpad].
    room, created = Room.objects.get_or_create[label=label]

    # We want to show the last 50 messages, ordered most-recent-last
    messages = reversed[room.messages.order_by['-timestamp'][:50]]

    return render[request, "chat/room.html", {
        'room': room,
        'messages': messages,
    }]
3

Ở đây, sau khi tra cứu phòng trong phiên kênh, chúng tôi ngắt kết nối [

def chat_room[request, label]:
    # If the room with the given label doesn't exist, automatically create it
    # upon first visit [a la etherpad].
    room, created = Room.objects.get_or_create[label=label]

    # We want to show the last 50 messages, ordered most-recent-last
    messages = reversed[room.messages.order_by['-timestamp'][:50]]

    return render[request, "chat/room.html", {
        'room': room,
        'messages': messages,
    }]
38]
chatsock.onmessage = function[message] {
    var data = JSON.parse[message.data];
    $['#chat'].append['' 
        + '' + data.timestamp + '' 
        + '' + data.handle + ''
        + '' + data.message + ' '
    + ''];
};
7 khỏi nhóm trò chuyện. Đó là nó

Bây giờ chúng tôi đã kết nối WebSockets và đang hoạt động, chúng tôi có thể kiểm tra nó bằng cách chạy

def chat_room[request, label]:
    # If the room with the given label doesn't exist, automatically create it
    # upon first visit [a la etherpad].
    room, created = Room.objects.get_or_create[label=label]

    # We want to show the last 50 messages, ordered most-recent-last
    messages = reversed[room.messages.order_by['-timestamp'][:50]]

    return render[request, "chat/room.html", {
        'room': room,
        'messages': messages,
    }]
80 và
def chat_room[request, label]:
    # If the room with the given label doesn't exist, automatically create it
    # upon first visit [a la etherpad].
    room, created = Room.objects.get_or_create[label=label]

    # We want to show the last 50 messages, ordered most-recent-last
    messages = reversed[room.messages.order_by['-timestamp'][:50]]

    return render[request, "chat/room.html", {
        'room': room,
        'messages': messages,
    }]
81, như trên [hoặc bằng cách chạy
def chat_room[request, label]:
    # If the room with the given label doesn't exist, automatically create it
    # upon first visit [a la etherpad].
    room, created = Room.objects.get_or_create[label=label]

    # We want to show the last 50 messages, ordered most-recent-last
    messages = reversed[room.messages.order_by['-timestamp'][:50]]

    return render[request, "chat/room.html", {
        'room': room,
        'messages': messages,
    }]
82]. Nhưng thật cô đơn khi nói chuyện với chính mình, vì vậy hãy xem những gì cần thiết để chạy ứng dụng này trên Heroku

Đối với hầu hết các phần, ứng dụng Kênh hoạt động giống như ứng dụng Python như Heroku — chỉ định các yêu cầu trong

def chat_room[request, label]:
    # If the room with the given label doesn't exist, automatically create it
    # upon first visit [a la etherpad].
    room, created = Room.objects.get_or_create[label=label]

    # We want to show the last 50 messages, ordered most-recent-last
    messages = reversed[room.messages.order_by['-timestamp'][:50]]

    return render[request, "chat/room.html", {
        'room': room,
        'messages': messages,
    }]
83, xác định thời gian chạy Python trong
def chat_room[request, label]:
    # If the room with the given label doesn't exist, automatically create it
    # upon first visit [a la etherpad].
    room, created = Room.objects.get_or_create[label=label]

    # We want to show the last 50 messages, ordered most-recent-last
    messages = reversed[room.messages.order_by['-timestamp'][:50]]

    return render[request, "chat/room.html", {
        'room': room,
        'messages': messages,
    }]
84, triển khai với
def chat_room[request, label]:
    # If the room with the given label doesn't exist, automatically create it
    # upon first visit [a la etherpad].
    room, created = Room.objects.get_or_create[label=label]

    # We want to show the last 50 messages, ordered most-recent-last
    messages = reversed[room.messages.order_by['-timestamp'][:50]]

    return render[request, "chat/room.html", {
        'room': room,
        'messages': messages,
    }]
85 tiêu chuẩn, v.v. [Để biết thêm thông tin, hãy xem hướng dẫn. ] Tôi sẽ chỉ nêu bật sự khác biệt giữa ứng dụng Kênh và ứng dụng Django tiêu chuẩn

Vì các ứng dụng Kênh cần cả máy chủ HTTP/WebSocket và người tiêu dùng kênh phụ trợ, nên

def chat_room[request, label]:
    # If the room with the given label doesn't exist, automatically create it
    # upon first visit [a la etherpad].
    room, created = Room.objects.get_or_create[label=label]

    # We want to show the last 50 messages, ordered most-recent-last
    messages = reversed[room.messages.order_by['-timestamp'][:50]]

    return render[request, "chat/room.html", {
        'room': room,
        'messages': messages,
    }]
86 cần xác định cả hai loại này. Đây là
def chat_room[request, label]:
    # If the room with the given label doesn't exist, automatically create it
    # upon first visit [a la etherpad].
    room, created = Room.objects.get_or_create[label=label]

    # We want to show the last 50 messages, ordered most-recent-last
    messages = reversed[room.messages.order_by['-timestamp'][:50]]

    return render[request, "chat/room.html", {
        'room': room,
        'messages': messages,
    }]
86 của chúng tôi

def chat_room[request, label]:
    # If the room with the given label doesn't exist, automatically create it
    # upon first visit [a la etherpad].
    room, created = Room.objects.get_or_create[label=label]

    # We want to show the last 50 messages, ordered most-recent-last
    messages = reversed[room.messages.order_by['-timestamp'][:50]]

    return render[request, "chat/room.html", {
        'room': room,
        'messages': messages,
    }]
4

Và, khi chúng tôi triển khai lần đầu, chúng tôi sẽ cần đảm bảo rằng cả hai loại quy trình đều đang chạy [Heroku sẽ chỉ khởi động dyno

def chat_room[request, label]:
    # If the room with the given label doesn't exist, automatically create it
    # upon first visit [a la etherpad].
    room, created = Room.objects.get_or_create[label=label]

    # We want to show the last 50 messages, ordered most-recent-last
    messages = reversed[room.messages.order_by['-timestamp'][:50]]

    return render[request, "chat/room.html", {
        'room': room,
        'messages': messages,
    }]
88 theo mặc định]

def chat_room[request, label]:
    # If the room with the given label doesn't exist, automatically create it
    # upon first visit [a la etherpad].
    room, created = Room.objects.get_or_create[label=label]

    # We want to show the last 50 messages, ordered most-recent-last
    messages = reversed[room.messages.order_by['-timestamp'][:50]]

    return render[request, "chat/room.html", {
        'room': room,
        'messages': messages,
    }]
5

[Một ứng dụng đơn giản sẽ chạy trong giới hạn của các bậc sở thích hoặc miễn phí của Heroku, mặc dù để sử dụng trong thế giới thực, bạn có thể muốn nâng cấp lên các dynos sản xuất để xử lý thông lượng tốt hơn. ]

Giống như hầu hết các ứng dụng Django, bạn sẽ muốn có một cơ sở dữ liệu và Heroku Postgres là lựa chọn hoàn hảo cho điều đó. Tuy nhiên, Kênh cũng yêu cầu phiên bản Redis hoạt động như lớp kênh. Vì vậy, chúng tôi sẽ muốn tạo cả phiên bản Heroku Postgres và Heroku Redis khi triển khai ứng dụng của chúng tôi lần đầu tiên

def chat_room[request, label]:
    # If the room with the given label doesn't exist, automatically create it
    # upon first visit [a la etherpad].
    room, created = Room.objects.get_or_create[label=label]

    # We want to show the last 50 messages, ordered most-recent-last
    messages = reversed[room.messages.order_by['-timestamp'][:50]]

    return render[request, "chat/room.html", {
        'room': room,
        'messages': messages,
    }]
6

Vì Kênh còn khá mới nên các vấn đề về khả năng mở rộng vẫn chưa được biết rõ. Tuy nhiên, tôi có thể đưa ra một vài dự đoán dựa trên kiến ​​trúc và một số bài kiểm tra hiệu suất ban đầu mà tôi đã thực hiện. Điều quan trọng là các Kênh phân chia các quy trình giữa những người chịu trách nhiệm xử lý các kết nối [

def chat_room[request, label]:
    # If the room with the given label doesn't exist, automatically create it
    # upon first visit [a la etherpad].
    room, created = Room.objects.get_or_create[label=label]

    # We want to show the last 50 messages, ordered most-recent-last
    messages = reversed[room.messages.order_by['-timestamp'][:50]]

    return render[request, "chat/room.html", {
        'room': room,
        'messages': messages,
    }]
80] và những người chịu trách nhiệm xử lý các thông báo kênh [_______181]. Điều này có nghĩa rằng

  • Thông lượng của các kênh — yêu cầu HTTP, thông báo WebSocket hoặc thông báo kênh tùy chỉnh — chủ yếu được xác định bởi số lượng worker dyno. Vì vậy, nếu bạn cần xử lý khối lượng yêu cầu lớn hơn, bạn sẽ làm điều đó bằng cách nhân rộng worker dynos [e. g.
    def chat_room[request, label]:
        # If the room with the given label doesn't exist, automatically create it
        # upon first visit [a la etherpad].
        room, created = Room.objects.get_or_create[label=label]
    
        # We want to show the last 50 messages, ordered most-recent-last
        messages = reversed[room.messages.order_by['-timestamp'][:50]]
    
        return render[request, "chat/room.html", {
            'room': room,
            'messages': messages,
        }]
    
    21]
  • Mức độ đồng thời - số lượng kết nối mở hiện tại - hầu hết sẽ bị giới hạn bởi quy mô của dyno web mặt trước. Vì vậy, nếu bạn cần xử lý nhiều kết nối WebSocket đồng thời hơn, bạn sẽ mở rộng quy mô web dyno [e. g.
    def chat_room[request, label]:
        # If the room with the given label doesn't exist, automatically create it
        # upon first visit [a la etherpad].
        room, created = Room.objects.get_or_create[label=label]
    
        # We want to show the last 50 messages, ordered most-recent-last
        messages = reversed[room.messages.order_by['-timestamp'][:50]]
    
        return render[request, "chat/room.html", {
            'room': room,
            'messages': messages,
        }]
    
    22]

Dựa trên các bài kiểm tra hiệu suất ban đầu của tôi, Daphne hoàn toàn có khả năng xử lý hàng trăm kết nối đồng thời trên một máy phát điện Standard-1X, vì vậy tôi cho rằng sẽ hiếm khi phải mở rộng quy mô các máy phát điện web. Số lượng worker dynos trong ứng dụng Kênh dường như ánh xạ khá gần với số lượng web dynos cần thiết trong ứng dụng Django kiểu cũ tương tự

Hỗ trợ WebSocket là một tính năng mới rất lớn của Django, nhưng nó chỉ làm trầy xước bề mặt của những gì Kênh có thể làm. Nhớ. Kênh là một tiện ích có mục đích chung để chạy các tác vụ nền. Do đó, nhiều tính năng từng yêu cầu Celery hoặc Python-RQ có thể được thực hiện bằng Kênh thay thế. Các kênh không thể thay thế hoàn toàn hàng đợi nhiệm vụ chuyên dụng. nó có một số hạn chế quan trọng, bao gồm cả việc phân phối nhiều nhất một lần, không làm cho nó phù hợp với mọi trường hợp sử dụng. Xem tài liệu để biết chi tiết đầy đủ. Tuy nhiên, Kênh có thể làm cho các tác vụ nền phổ biến trở nên đơn giản hơn nhiều. Ví dụ: bạn có thể dễ dàng sử dụng Kênh để thực hiện thu nhỏ hình ảnh, gửi email, tweet hoặc SMS, chạy các tính toán dữ liệu đắt tiền, v.v.

Đối với chính các Kênh. kế hoạch là bao gồm các Kênh trong Django 1. 10, dự kiến ​​phát hành vào mùa hè này. Điều này có nghĩa là bây giờ là thời điểm tuyệt vời để dùng thử và đưa ra phản hồi. đầu vào của bạn có thể giúp định hướng tính năng quan trọng này. Nếu bạn muốn tham gia, hãy xem hướng dẫn về Đóng góp cho Django, sau đó nhảy lên để chia sẻ phản hồi của bạn

Cuối cùng. xin chân thành cảm ơn Andrew Godwin vì công việc của anh ấy trên Kênh. Đây là một hướng đi mới cực kỳ thú vị cho Django và tôi rất vui khi thấy nó bắt đầu hình thành

Để biết thêm thông tin về Kênh, vui lòng xem tài liệu về Kênh, bao gồm rất nhiều thông tin chi tiết và tài liệu tham khảo, bao gồm

  • Câu trả lời cho một số câu hỏi thường gặp về kênh
  • Kế hoạch kỷ lục để tích hợp Kênh vào Django
  • Thông số kỹ thuật Giao diện cổng máy chủ không đồng bộ [ASGI] chính thức [nếu bạn thực sự muốn biết tất cả các chi tiết kỹ thuật. ]

Để biết thêm thông tin về Python trên Heroku, hãy truy cập Python trên Heroku trong Dev Center. Một vài bài báo đặc biệt hay mà tôi giới thiệu là

Chủ Đề