使用 Deno、WebSockets、Chart.js 和 Materialize 构建实时图表
介绍
[!警告]此演示包含不受支持的Materialize (0.26.x)
版本的示例。
这是一个由 Deno、Web Sockets、Chart.js 和Materialize提供支持的实时图表的独立示例。
Deno是一个简单且安全的 JavaScript 和 TypeScript 运行时,它使用 V8 引擎。与 Materialize 一样,Deno 也是用 Rust 编写的。
在此演示中,我们将构建一个简单的实时仪表板应用程序,用于显示来自 Deno Web Socket 服务器的实时数据。然后,Deno 将连接到 Materialize 并运行我们的实时物化视图TAIL以获取最新数据,并使用 Chart.js 将其显示在实时图表中。
概述
以下是该项目的简要概述:
- 持续生成用户得分事件的模拟服务。
- Redpanda 实例将用户得分事件存储在主题中。
- 物化实例连接到 Redpanda 实例并从实时物化视图中的主题中提取数据,我们只需使用 SQL 即可实时查询。
- Deno 后端服务连接到 Materialize 并 TAIL 实时物化视图以获取最新数据并将其显示在实时图表中。
- 通过 Web 套接字连接到 Deno 应用程序并使用 Chart.js 在实时图表中显示数据的前端服务。
以下是该项目的图表:
先决条件
要运行此演示,您需要安装以下内容。
运行演示
首先,克隆存储库:
git clone git clone https://github.com/bobbyiliev/materialize-tutorials.git
cd materialize-tutorials
git checkout lts
然后就可以访问目录了:
cd mz-deno-live-dashboard
这样您就可以构建图像:
docker-compose build
最后,您可以运行所有容器:
docker-compose up -d
启动容器并生成演示数据可能需要几分钟。
之后,您可以http://localhost
在浏览器中访问以查看演示:
接下来,让我们回顾一下 Materialize 设置和 Deno 后端设置。
实现设置
Deno 服务将在启动时执行以下 DDL 语句,因此我们不必手动运行它们:
- 创建 Kafka 源:在 Materialize 中创建源并不会真正启动数据提取。您可以将非物化源视为 Materialize 连接源所需的元数据,但不处理任何数据:
CREATE SOURCE score
FROM KAFKA BROKER 'redpanda:9092' TOPIC 'score_topic'
FORMAT BYTES;
- 创建一个非物化视图,本质上只为我们提供它们所包含的语句的别名
SELECT
:
CREATE VIEW score_view AS
SELECT
*
FROM (
SELECT
(data->>'user_id')::int AS user_id,
(data->>'score')::int AS score,
(data->>'created_at')::double AS created_at
FROM (
SELECT CAST(data AS jsonb) AS data
FROM (
SELECT convert_from(data, 'utf8') AS data
FROM score
)
)
);
- 创建物化视图:
CREATE MATERIALIZED VIEW score_view_mz AS
SELECT
(SUM(score))::int AS user_score,
user_id
FROM score_view GROUP BY user_id;
要检查视图和源是否已创建,请启动 Materialize CLI:
docker-compose run mzcli
这只是一个预先安装了 postgres-client 的 docker 容器的快捷方式,如果您已经安装了,
psql
您可以运行psql -U materialize -h localhost -p 6875 materialize
。
然后检查观点和来源:
SHOW VIEWS;
-- Output:
-- +-----------------+
-- | score_view |
-- | score_view_mz |
-- +-----------------+
SHOW sources;
-- Output:
-- +-----------------+
-- | score |
-- +-----------------+
使用TAIL
接下来,为了实时查看结果,我们可以使用TAIL
:
COPY ( TAIL score_view_mz ) TO STDOUT;
您将看到实时生成的新用户分数流。
我们还可以启动TAIL
没有快照的查询,这意味着运行查询后您只会看到最新的记录:
COPY ( TAIL score_view_mz WITH (SNAPSHOT = false) ) TO STDOUT;
这就是我们将在 Deno 应用程序中使用的功能,用于获取最高用户分数并将其显示在实时图表中。
有关该TAIL
函数如何工作的更多信息,请参阅Materialize 文档。
德诺
现在我们已经准备好了 Materialize,让我们回顾一下 Deno 设置。
我们将使用两个 Deno 模块:
- 连接到 Materialize 的 Postgres 模块。
- Web Sockets 模块用于创建与我们的前端服务的 Web Socket 连接。
backend
您可以在目录中找到代码。
import { WebSocketClient, WebSocketServer } from "https://deno.land/x/websocket@v0.1.4/mod.ts";
import { Client } from "https://deno.land/x/postgres/mod.ts";
// Specify your Materialize connection details
const client = new Client({
user: "materialize",
database: "materialize",
hostname: "materialized",
port: 6875,
});
await client.connect();
console.log("Connected to Postgres");
// Start a transaction
await client.queryObject('BEGIN');
// Declare a cursor without a snapshot
await client.queryObject(`DECLARE c CURSOR FOR TAIL score_view_mz WITH (SNAPSHOT = false)`);
const wss = new WebSocketServer(8080);
wss.on("connection", async function (ws: WebSocketClient) {
console.log("Client connected");
setInterval(async () => {
const result = await client.queryObject<{ mz_timestamp: string; mz_diff: number, user_id: number, user_score: number}>(`FETCH ALL c`);
for (const row of result.rows) {
let message = { user_id: row.user_id, user_score: row.user_score };
broadcastEvent(message);
}
} , 1000);
});
// Broadcast a message to all clients
const broadcastEvent = (message: any) => {
wss.clients.forEach((ws: WebSocketClient) => {
ws.send(JSON.stringify(message));
});
}
代码概要:
- 由于 Materialize 与 Postgres 连接兼容,我们首先
Client
从https://deno.land/x/postgres/mod.ts
模块中导入一个类。我们将使用该类连接到 Materialize 实例。 - 接下来,我们创建一个新
Client
实例并将 Materialize 的凭证传递给它。 - 然后我们调用
connect()
客户端实例上的方法来连接到Materialize。 - 接下来,我们调用
queryObject()
客户端实例上的方法来启动一个事务,同时调用queryObject()
客户端实例上的方法来声明一个没有快照的游标。 - 最后,我们创建一个新
WebSocketServer
实例并将要监听的端口传递给它。 - 然后我们在实例
connection
上定义一个事件处理程序WebSocketServer
,当客户端连接时调用该事件处理程序。 - 然后,我们设置一个间隔来从 Materialize 获取最新数据并将其广播给所有客户端。
前端设置
对于前端,我们不会使用任何 JavaScript 框架,而只使用Chart.js 库。
借助 Web 套接字连接,我们现在可以从 Materialize 接收最新数据并将其显示在实时图表中。
<!DOCTYPE html>
<html lang="en">
<head>
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
</head>
<body>
<div class="w-full mt-10">
<canvas id="myChart"></canvas>
</div>
<script>
const ctx = document.getElementById("myChart");
const myChart = new Chart(ctx, {
type: "bar",
data: {
labels: [ "Player 1", "Player 2", "Player 3", "Player 4", "Player 5", "Player 6" ],
datasets: [
{
label: "# of points",
data: [0, 0, 0, 0, 0, 0],
backgroundColor: [
"rgba(255, 99, 132, 0.2)",
"rgba(54, 162, 235, 0.2)",
"rgba(255, 206, 86, 0.2)",
"rgba(75, 192, 192, 0.2)",
"rgba(153, 102, 255, 0.2)",
"rgba(255, 159, 64, 0.2)",
],
borderColor: [
"rgba(255, 99, 132, 1)",
"rgba(54, 162, 235, 1)",
"rgba(255, 206, 86, 1)",
"rgba(75, 192, 192, 1)",
"rgba(153, 102, 255, 1)",
"rgba(255, 159, 64, 1)",
],
borderWidth: 1,
},
],
},
options: {
scales: {
y: {
beginAtZero: true,
},
},
},
});
webSocket = new WebSocket("ws://127.0.0.1:8080");
webSocket.onmessage = function (message) {
const data = message.data;
const dataObj = JSON.parse(data);
const dataArray = Object.values(dataObj);
console.log(dataArray);
index = dataArray[0] - 1;
myChart.data.datasets[0].data[index] = dataArray[1];
myChart.update();
};
</script>
</body>
</html>
代码概要:
- 我们首先使用库定义新图表
Chart.js
:new Chart()
并传递不同的配置选项。 - 然后我们创建一个新
WebSocket
实例,并将 Web Socket 服务器的 URL 传递给它webSocket = new WebSocket("ws://backend:8080");
- 最后,我们在实例
onmessage
上定义一个事件处理程序WebSocket
,当收到消息并更新图表时调用该事件处理程序。
frontend
您可以在目录中找到代码。
结论
您可以让 Deno 应用程序保持运行,以便它订阅 Materialize 实例并实时更新图表。
下一步,您可以查看基于相同用户评论模拟数据的 Materialize + dbt + Redpanda 演示:
有用的资源:
社区
如果您有任何问题或意见,请加入Materialize Slack 社区!
鏂囩珷鏉ユ簮锛�https://dev.to/bobbyiliev/building-a-live-chart-with-deno-websockets-chartjs-and-materialize-3cd7