Một ngày nọ, một số nhà phát triển giỏi tại công ty của tôi đã sẵn sàng tung ra một trang cập nhật trạng thái. Chúng tôi đã thử nghiệm rộng rãi nhưng bây giờ chúng tôi chuẩn bị đưa nó ra quy mô lớn.
Tôi đã lo lắng về sự phụ thuộc của nó vào một máy chủ API đã hoạt động gần đây. Chúng tôi chưa xác định được nguyên nhân gốc rễ của các vấn đề của chúng tôi về phía API và ứng dụng này sử dụng tính năng bỏ phiếu – nghĩa là ứng dụng liên tục yêu cầu API cung cấp dữ liệu mới. Nếu API đó không hoạt động, nó sẽ mang theo ứng dụng của chúng tôi và tải tăng lên từ ứng dụng của chúng tôi có thể làm trầm trọng thêm các sự cố mà chúng tôi đang gặp phải.
Một cách để tránh bỏ phiếu là tích hợp SignalR, một công cụ kết nối liên tục sử dụng ổ cắm web và các công nghệ liên quan để cho phép máy chủ đẩy cập nhật cho khách hàng.
Công nghệ này được viết bằng .NET và hầu hết các tài liệu bạn tìm thấy trên web đều sử dụng C#. Hướng dẫn này sẽ trình bày cách triển khai JavaScript cơ bản.
SignalR nguồn mở tạo kết nối liên tục giữa máy khách và máy chủ. Nó sử dụng ổ cắm web trước, sau đó là longpolling và các công nghệ khác khi không có ổ cắm web.
Khi máy khách và máy chủ đã tạo kết nối, SignalR có thể được sử dụng để “phát” thông báo tới máy khách. Khi khách hàng nhận được những tin nhắn đó, nó có thể thực hiện các chức năng như cập nhật cửa hàng.
Ví dụ phổ biến nhất được đưa ra cho ổ cắm web là một ứng dụng trò chuyện – dữ liệu mới phải được hiển thị cho người dùng mà không cần làm mới trang. Nhưng nếu máy chủ của bạn nhận được bất kỳ cập nhật nào về việc thay đổi dữ liệu mà bạn cần hiển thị cho khách hàng, thì đây có thể là dịch vụ dành cho bạn.
Có lẽ vì được phát triển bởi Microsoft, SignalR có sự tích hợp rất rõ ràng trên nền tảng đám mây Azure. Giống như các ứng dụng chức năng khác, bạn sẽ tạo trình kích hoạt “vào” và ràng buộc “ra” để phát tin nhắn.
chi phí
Bởi vì tôi là người đầu tiên xem xét công nghệ này ở quy mô lớn tại công ty của mình, nên tôi phải tìm hiểu một chút về chi phí cho dịch vụ này. Azure tính phí khoảng $50/tháng cho một “đơn vị” dịch vụ SignalR – 1000 kết nối đồng thời và một triệu tin nhắn mỗi ngày. Ngoài ra còn có một dịch vụ miễn phí cho những người chơi xung quanh hoặc doanh nghiệp nhỏ.
Thật là tốt khi tôi đào sâu vào những con số đó, như bạn sẽ thấy một chút bên dưới.
Tạo một trung tâm SignalR
Bắt đầu nào. Chúng tôi sẽ cần một trung tâm SignalR, hai ứng dụng chức năng và mã máy khách để thêm vào ứng dụng web của chúng tôi.
Truy cập SignalR -> Thêm và điền thông tin chi tiết của bạn. Phải mất một giây để nhân viên xây dựng dịch vụ của bạn. Đảm bảo rằng bạn cung cấp cho dịch vụ một tên tài nguyên hợp lý, vì bạn sẽ sử dụng nó với các ứng dụng còn lại của mình. Đồng thời lấy Khóa -> Chuỗi kết nối để sử dụng trong ràng buộc của chúng tôi.
Vì chúng tôi đang làm việc với Azure nên chúng tôi sẽ tạo các ứng dụng chức năng để giao tiếp với SignalR. Cách đây ít lâu, tôi đã viết một bài đăng blog bắt đầu về các ứng dụng chức năng Azure.
Hướng dẫn này giả định rằng bạn đã biết cách làm việc với các ứng dụng chức năng. Đương nhiên, bạn có thể làm việc với các thư viện này mà không cần phép thuật ràng buộc, nhưng bạn sẽ phải tự dịch mã .NET của riêng mình!
ứng dụng kết nối
Điều đầu tiên chúng tôi cần là một cách để khách hàng yêu cầu quyền kết nối với dịch vụ SignalR của chúng tôi. Mã cho chức năng này không thể cơ bản hơn:
module.exports = function (context, _req, connectionInfo) {
context.res = { body: connectionInfo }
context.done()
}
Tất cả điều kỳ diệu xảy ra trong các liên kết, nơi chúng tôi sử dụng dịch vụ SignalR của mình. Kích hoạt là một yêu cầu HTTP mà khách hàng của chúng tôi có thể gọi.
{
"bindings": [
{
"authLevel": "function",
"type": "httpTrigger",
"direction": "in",
"name": "req",
"methods": ["get"]
},
{
"type": "signalRConnectionInfo",
"name": "connectionInfo",
"hubName": "your-signalr-service-name",
"connectionStringSetting": "connection-string",
"direction": "in"
}
]
}
Mã khách hàng
Để truy cập phương thức này, khách hàng của chúng tôi sẽ gọi:
import * as signalR from '@microsoft/signalr'
const { url: connectionUrl, accessToken } = await axios
.get(url-to-your-connection-app)
.then(({ data }) => data)
.catch(console.error)
Ứng dụng chức năng của chúng tôi sẽ trả về một url
và accessToken
, sau đó chúng tôi có thể sử dụng để kết nối với dịch vụ SignalR của mình. Lưu ý rằng chúng tôi đã tạo liên kết với hubName
của dịch vụ SignalR của chúng tôi – điều đó có nghĩa là bạn có thể có nhiều kết nối đến các trung tâm khác nhau trong một máy khách.
Bây giờ chúng tôi đã sẵn sàng để bắt đầu gửi tin nhắn. Một lần nữa chúng ta sẽ bắt đầu với ứng dụng chức năng. Nó nhận một trình kích hoạt và đưa ra một thông báo SignalR.
Trình kích hoạt có thể là một trình kích hoạt khác bằng cách đăng thông báo, sự kiện từ trung tâm sự kiện hoặc bất kỳ trình kích hoạt nào khác mà Azure hỗ trợ. Tôi cần kích hoạt thay đổi cơ sở dữ liệu.
{
"bindings": [
{
"type": "cosmosDBTrigger",
"name": "documents",
"direction": "in",
[...]
},
{
"type": "signalR",
"name": "signalRMessages",
"hubName": "your-signalr-service-name",
"connectionStringSetting": "connection-string",
"direction": "out"
}
]
}
Và mật mã. Một lần nữa, chết đơn giản.
module.exports = async function (context, documents) {
const messages = documents.map(update => {
return {
target: 'statusUpdates',
arguments: [update]
}
})
context.bindings.signalRMessages = messages
}
Tin nhắn SignalR mất một target
và arguments
vật. Khi trình kích hoạt của bạn bắt đầu kích hoạt, đó là mọi thứ bạn cần để bắt đầu với SignalR trên máy chủ! Microsoft đã làm cho tất cả những điều này trở nên rất dễ dàng đối với chúng tôi.
Mã khách hàng
Về phía khách hàng, mọi thứ phức tạp hơn một chút, nhưng không phải là không thể quản lý được. Đây là phần còn lại của mã máy khách:
const connection = new signalR.HubConnectionBuilder()
.withUrl(connectionUrl, { accessTokenFactory: () => accessToken })
// .configureLogging(signalR.LogLevel.Trace)
.withAutomaticReconnect()
.build()
connection.on('statusUpdates', data => {
// do something with the data you get from SignalR
})
connection.onclose(function() {
console.log('signalr disconnected')
})
connection.onreconnecting(err =>
console.log('err reconnecting ', err)
)
connection
.start()
.then(res => // Potential to do something on initial load)
.catch(console.error)
chúng tôi tiêu thụ connectionUrl
và accessToken
chúng tôi đã nhận được từ chức năng kết nối trước đó, sau đó xây dựng kết nối của mình bằng các giá trị đó.
Sau đó, chúng tôi nghe tin nhắn bằng khóa chung (đối với tôi, đó là statusUpdates
) và cung cấp trình xử lý cho các hàm đóng và kết nối lại.
Cuối cùng, chúng tôi bắt đầu kết nối. Ở đây chúng tôi có thể cung cấp một chức năng tải ban đầu. Tôi cần một cái để lấy dữ liệu ban đầu để hiển thị trạng thái hiện tại. Nếu đang xây dựng một ứng dụng trò chuyện, bạn có thể cần tìm nạp các tin nhắn ban đầu tại đây.
Đây là (gần như, có thể là) mọi thứ bạn cần để bắt đầu trong JavaScript với SignalR trên Azure!
Nhưng có lẽ bạn cũng như tôi, cần gửi rất nhiều tin nhắn cho nhiều người dùng.
Khi tôi lần đầu tiên đưa sản phẩm này vào sản xuất, trên một nhóm nhỏ người dùng, tôi đã làm hỏng mọi kết nối với mỗi bản cập nhật. Vì mã máy khách có thể xác định phạm vi các thông báo mà nó nghe nên tôi đã sử dụng một cái gì đó như statusUpdates-${userId}
để khách hàng chỉ nhìn thấy các cập nhật của riêng mình.
Điều đó có thể hoạt động tốt nếu bạn có âm lượng rất thấp và điều tổng quát hơn là tuyệt vời nếu mọi người trong hệ thống của bạn cần cùng một thông báo. Nhưng trạng thái tôi làm việc là đặc biệt đối với một cá nhân.
Hãy nhớ cách Azure tính phí cho mỗi “đơn vị” và mỗi đơn vị có một triệu tin nhắn? Tôi đã đạt được điều đó trong vài giờ thử nghiệm điều này trong thời gian không bận rộn.
Azure tính mỗi tin nhắn SignalR phải gửi dưới dạng một tin nhắn. Nghĩa là, nếu năm kết nối được kết nối với trung tâm của bạn và bạn gửi mười tin nhắn, thì số đó được tính là 50 chứ không phải 10. Điều này khiến tôi ngạc nhiên và cũng cần thêm vài giờ nghiên cứu.
Chúng tôi có thể xác định phạm vi mã chức năng SignalR của mình để chỉ gửi cho một số người dùng nhất định. Đầu tiên, chúng tôi cập nhật ứng dụng kết nối để chấp nhận userId
như một tham số truy vấn:
{
"type": "signalRConnectionInfo",
"name": "connectionInfo",
"userId": "{userId}",
"hubName": "your-signalr-service-name",
"connectionStringSetting": "connection-string",
"direction": "in"
}
Sau đó, chúng tôi cập nhật chức năng phát sóng để chỉ gửi cho người dùng đó:
const messages = documents.map(update => {
return {
target: 'statusUpdates',
userId: update.user.id,
arguments: [update]
}
})
Dịch vụ truyền phát sẽ không biết ai đã kết nối, vì vậy bạn cần kích hoạt dịch vụ này bằng thứ gì đó có quyền truy cập vào một ID duy nhất mà khách hàng cũng sẽ có quyền truy cập.
Mã máy khách chỉ cần chuyển vào userId dưới dạng tham số truy vấn:
const { url: connectionUrl, accessToken } = await axios
.get(`${url-to-your-connection-app}&userId=${userId}`)
.then(({ data }) => data)
.catch(console.error)
Tôi thề với bạn, nơi duy nhất trên toàn bộ internet mà tôi tìm thấy cho tôi biết cách yêu cầu kết nối bằng cách sử dụng userId
là một câu trả lời cho câu hỏi Stack Overflow này.
Internet thật tuyệt vời và tài liệu JavaScript Azure rất khó kiếm.
Bài đăng này ban đầu xuất hiện trên wilkie.tech.