SignalR實戰:在.NET Framework和.NET Core中如何使用SignalR?

来源:https://www.cnblogs.com/kimiliucn/archive/2023/08/22/17648543.html
-Advertisement-
Play Games

本文主要介紹SignalR在實際項目中的應用,以及.NET Framework和.NET Core中如何去使用SignalR。SignalR是一個開放源代碼庫,可用於簡化嚮應用添加實時Web功能,實時Web功能使伺服器端代碼能夠將內容推送到客戶端。 ...


官網文檔:https://learn.microsoft.com/zh-cn/aspnet/core/tutorials/signalr?view=aspnetcore-6.0&tabs=visual-studio
SignalR開源代碼:https://github.com/signalr


很多小伙伴問:在前後端分離項目中,後端是.NET Core前端是Vue如何使用SignalR?在前後端不分離項目中,.NET Framework MVC項目中又如何使用SignalR技術呢?那就來看看下麵這篇文章吧!本文主要介紹SignalR在實際項目中的應用,以及.NET Framework和.NET Core中如何去使用SignalR。


一、SignalR介紹

1.1-SignalR介紹

ASP.NET Core SignalR是一個開放源代碼庫,可用於簡化嚮應用添加實時Web功能,實時Web功能使伺服器端代碼能夠將內容推送到客戶端。


1.2-SignalR的應用

  • 需要從伺服器進行高頻更新的應用:包括游戲、社交網路、投票、拍賣、地圖和GPS引用。
  • 儀錶盤和監視應用:包括公司儀錶板、即時銷售更新或旅行報警
  • 協作應用:包括白板應用和團隊會議軟體。
  • 通知應用:社交網路、電子郵件、聊天、游戲、旅行報警和其他應用都需要使用的通知。

二、.NET Framework使用SignalR

參考文獻:
Owin介紹:https://www.cnblogs.com/Pinapple/p/6721361.html
Owin相關案例:https://blog.csdn.net/bifan546/article/details/77098896/


2.1-服務端(.NET Framework MVC)

(1)選擇要使用Signalr的項目,點擊【管理NuGet程式包】。
image.png

(2)搜索【SignalR】包,找到這個後然後點擊下載。
image.png
會自動安裝四個,註意他們之間有依賴關係:
image.png

(3).NET Framework平臺需要添加Owin相關的包。
OWIN 是一種定義 Web 伺服器和應用程式組件之間的交互的規範 。這一規範的目的是發展一個廣闊且充滿活力的、基於 Microsoft .NET Framework 的 Web 伺服器和應用程式組件生態系統。

Microsoft.Owin.Hosting
Microsoft.Owin.Cors
Microsoft.Owin.Host.HttpListener

(4)在Web項目(要使用的Signal)中創建一個【Startup.cs】文件。

using JiCai.Admin.Hubs;
using Microsoft.Owin;
using Owin;

[assembly: OwinStartup(typeof(JiCai.Admin.Startup))]
namespace JiCai.Admin
{
    public class Startup
    {
        /// <summary>
        /// 應用程式配置
        /// </summary>
        /// <param name="app"></param>
        public void Configuration(IAppBuilder app)
        {
            //啟用SignalR
            app.MapSignalR();
            //綁定多個Hub
            app.MapSignalR<DemoHub>("/demoHub");
        }
    }
}

例如:
image.png

(5)可以創建一個【Hubs】文件夾,專門放置Hub相關文件。[西瓜程式猿]這邊是創建一個Hub文件,名為【DemoHub.cs】。

using Fenqibao.DTO;
using JiCai.Admin.Hubs.ConectionOperate;
using JiCai.Admin.Hubs.Models;
using log4net;
using Microsoft.AspNet.SignalR;
using Microsoft.AspNet.SignalR.Json;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace JiCai.Admin.Hubs
{
    /// <summary>
    /// 報表管理-總表Hub
    /// </summary>
    public class SummaryTableHub :  PersistentConnection
    {
        public readonly BaseService _base = new BaseService();
        private readonly ILog logger = LogManager.GetLogger(typeof(SummaryTableHub));
        private ConnectionManagement summaryTableCon = new ConnectionManagement();
        public CookieUserData LoginUserData
        {
            get
            {
                IOperator oper = ContainerManager.Resolve<IOperator>();
                LoginUser loginUser = oper as LoginUser;
                if (loginUser != null && loginUser.UserData != null)
                {
                    return loginUser.UserData;
                }
                return null;
            }
        }

        /// <summary>
        /// 連接成功後調用
        /// </summary>
        /// <param name="request"></param>
        /// <param name="connectionId"></param>
        /// <returns></returns>
        protected override Task OnConnected(IRequest request, string connectionId)
        {
            //獲得SignalR的連接id
            var connid = connectionId;
            //獲得用戶id
            var userid = LoginUserData.Id.ToString();
            Console.Write($"【{connid}】:已建立連接!");

            //判斷一下用戶是不是已經鏈接了
            var checkUserConn = summaryTableCon.IsConn(connid, userid);
            if (!checkUserConn)
            {
                //添加一個新的連接
                summaryTableCon.AddConnInfo(new SignalRConn()
                {
                    UserId = userid,
                    ConnectionId = connid
                });
            }
            //更新連接
            else
            {
                summaryTableCon.UpdateConnInfo(userid, connid);
            }

            return Connection.Send(connectionId, $"【用戶:{connectionId}】真正連接成功!");
            //return base.OnConnected(request, connectionId);
        }

        /// <summary>
        /// 接收到請求的時候調用
        /// </summary>
        /// <param name="request"></param>
        /// <param name="connectionId"></param>
        /// <param name="data"></param>
        /// <returns></returns>
        protected override async Task OnReceived(IRequest request, string connectionId, string data)
        {
            //獲得用戶id
            var userid = LoginUserData.Id.ToString();
            await Task.Factory.StartNew(async () =>
            {
                while (true)
                {
					var list = GetSummaryTableList(userid);
                    string json_jieshou_mes = "";
                    if (list != null && list.Count > 0)
                    {
						json_jieshou_mes = JsonConvert.SerializeObject(list);
                    }


                    await Connection.Send(connectionId, json_jieshou_mes);

                    //每5秒同步一次
                    await Task.Delay(5000);
                }
            }, TaskCreationOptions.LongRunning);

            //return base.OnReceived(request, connectionId, data);
        }

        /// <summary>
        /// 連接中斷的時候調用
        /// </summary>
        /// <param name="request"></param>
        /// <param name="connectionId"></param>
        /// <param name="stopCalled"></param>
        /// <returns></returns>
        protected override Task OnDisconnected(IRequest request, string connectionId, bool stopCalled)
        {
            Console.Write($"【{connectionId}】:已斷開連接!");
            //獲得SignalR的連接id
            var connid = connectionId;
            //關閉連接
            summaryTableCon.DelConnInfo(connid);
            return base.OnDisconnected(request, connectionId, stopCalled);
        }

        /// <summary>
        /// 連接超時重新連接的時候調用
        /// </summary>
        /// <param name="request"></param>
        /// <param name="connectionId"></param>
        /// <returns></returns>
        protected override Task OnReconnected(IRequest request, string connectionId)
        {
            return base.OnReconnected(request, connectionId);
        }


		/// <summary>
		/// 查詢數據
		/// </summary>
		/// <param name="userId"></param>
		/// <returns></returns>
		private List<SummaryTableDataModel> GetSummaryTableList(string userId)
        {
            var result = _base.Query<SummaryTableDataModel>($@"
			    select * from demo-data
					;
            ").ToList();
			return result;
        }
    }
}

(6)在Hubs/ConectionOperate文件夾中,[西瓜程式猿]這邊創建【ConnectionManagement】文件,用來管理所有連接。

using System.Collections.Generic;
using System.Linq;

namespace JiCai.Admin.Hubs.ConectionOperate
{
    /// <summary>
    /// 連接管理
    /// </summary>
    public class ConnectionManagement
    {
        /// <summary>
        /// 用戶連接集合
        /// </summary>
        public static  List<SignalRConn> SignalRConns { get; set; } = new List<SignalRConn>();

        /// <summary>
        /// 添加連接
        /// </summary>
        /// <param name="conn"></param>
        public  void AddConnInfo(SignalRConn conn)
        {
            SignalRConns.Add(conn);
        }

        /// <summary>
        /// 刪除連接
        /// </summary>
        /// <param name="connid"></param>
        public  void DelConnInfo(string connid)
        {
            var signalRConns = SignalRConns.FirstOrDefault(u => u.ConnectionId == connid);
            if (signalRConns != null)
            {
                SignalRConns.Remove(signalRConns);
            }
        }

        /// <summary>
        /// 更新鏈接(老的鏈接不起作用了)
        /// 場景:客戶端重連了,userid沒變,但是connid變了
        /// </summary>
        /// <param name="userId">用戶id</param>
        /// <param name="newConnsId">新的鏈接id</param>
        public  void UpdateConnInfo(string userId, string newConnsId)
        {
            var signalRConns = SignalRConns.FirstOrDefault(u => u.UserId.ToLower() == userId.ToLower());
            if (signalRConns != null)
            {
                signalRConns.ConnectionId = newConnsId;
            }
        }

        /// <summary>
        /// 判斷用戶是否已經鏈接
        /// </summary>
        /// <param name="connid">連接id</param>
        /// <param name="userid">用戶id</param>
        /// <returns></returns>
        public bool IsConn(string connid,string userid)
        {
            var userConn = SignalRConns.FirstOrDefault(u => u.ConnectionId.ToLower() == connid.ToLower() && u.UserId.ToLower() == userid.ToLower());
            return userConn == null ? false : true;
        }
    }
}

(7)在Hubs/ConectionOperate文件夾中,創建【SignalRConn.cs】文件用來作為SignalR和系統用戶的連接實體。

namespace JiCai.Admin.Hubs.ConectionOperate
{
    /// <summary>
    /// 連接
    /// </summary>
    public class SignalRConn
    {
        /// <summary>
        /// 系統用戶id
        /// </summary>
        public string UserId { get; set; }

        /// <summary>
        /// SignleR鏈接Id(每次鏈接SignalR都會分配一個id)
        /// </summary>
        public string ConnectionId { get; set; }
    }
}

2.2-客戶端(JS)

(1)下載相關jq/signalr相關包,分別是【jquery.signalR-2.4.3.js】和【jquery-1.6.4.min.js】。可以訪問下載(如果失效了,請聯繫我[西瓜程式猿])。

下載地址(編碼:yRLCRp81):https://yongteng.lanzoub.com/iXDlu1631ugd
密碼:44x5

文件截圖:
image


(2)創建一個js文件【data_list_table_hub.js】,用來連接signalR。

// 連接服務
var connection = $.connection("/summary_table_hub");

// 建立鏈接
connection.start(function () {
    //連接成功
    console.log("西瓜程式猿-【" + new Date().toLocaleString() +"】連接成功!");
    //發送消息
    connection.send("給我數據吧");
});

// 連接斷開
connection.disconnected(function () {
    console.log("西瓜程式猿-【" + new Date().toLocaleString() +"】連接斷開!");
});

// 接收伺服器發來的消息
connection.received(function (data) {
    console.log("西瓜程式猿-【" + new Date().toLocaleString() +"】接收伺服器發來的消息:");
    console.log(data);
    //顯示數據
    if (data != "" && checkJson(data)) {
        var obj = JSON.parse(data); 
        var html_box = "";
        for (var i = 0; i < obj.length; i++) {
            html_box += `<tr>
            <td>`+obj[i].project_name+`</td>
            <td>`+ obj[i].zuori_sum+`</td>
            <td>`+ obj[i].jinri_sum+`</td>
            <td>`+ obj[i].qunian_sum+`</td>
            <td>`+ obj[i].jinnian_sum+`</td>
            <td>`+ obj[i].sum+`</td>
            <td>`+ obj[i].yikaipiao_sum+`</td>
            <td>`+ obj[i].weikaipiao_sum +`</td>
            <td>`+ obj[i].yishoupiao_sum +`</td>
            <td>`+ obj[i].weishoupiao_sum +`</td>
            <td>`+ obj[i].kehu_yinghuikuan_sum+`</td>
            <td>`+ obj[i].kehu_yihuikuan_sum+`</td>
            <td>`+ obj[i].kehu_weihuikuan_sum +`</td>
            <td>`+ obj[i].fuwu_yingfukuan_sum+`</td>
            <td>`+ obj[i].fuwu_yifukuan_sum+`</td>
            <td>`+ obj[i].fuwu_weifukuan_sum+`</td>
            </tr>`
        }
        $("#last_async_date").text(new Date().toLocaleString());
        $("#table_body").html(html_box);
    }
});

//判斷是不是json字元串
function checkJson(str) {
    if (typeof str == 'string') {
        try {
            var obj = JSON.parse(str);
            // 等於這個條件說明就是JSON字元串 會返回true
            if (typeof obj == 'object' && obj) {
                return true;
            } else {
                //不是就返回false
                return false;
            }
        } catch (e) {
            return false;
        }
    }
    return false;
}

(3)創建要展示的文件,我這邊是用MVC模式,所有前臺文件是cshtml。

@{
    ViewBag.Title = "西瓜程式猿-明細表";
}

@section styles{
    <style>
        .table_tr_center {
            text-align: center !important;
        }

        .table_tr_bg_0 {
            background: rgb(253,226,226);
        }  
        
        .table_tr_bg_1 {
            background: rgb(218,226,246);
        }

        .table_tr_bg_2 {
            background: rgb(228,237,219);
        }

        .table_tr_bg_3 {
            background: rgb(249,229,215);
        }
    </style>
}

@section scripts{
    <script src="~/Scripts/jquery-1.6.4.min.js"></script>
    <script src="~/Scripts/jquery.signalR-2.4.3.js"></script>
    <script src="~/Scripts/controller/hubs/[email protected]"></script>
}

<div class="row">
    <div class="col-xs-12">
    </div>
    <div class="col-xs-12">
        <div>
            報表最新同步時間:<span id="last_async_date" style="color: #FF5722 !important;"></span>
        </div>
        <div class="layui-form">
            <table class="layui-table">
                <colgroup>
                    <col>
                    <col>
                    <col>
                    <col>
                    <col>
                    <col>
                    <col>
                    <col>
                    <col>
                    <col>
                    <col>
                    <col>
                    <col>
                </colgroup>
                <thead>
                    <tr>
                        <th colspan="2" class="table_tr_center table_tr_bg_0">項目信息</th>
                        <th colspan="5" class="table_tr_center table_tr_bg_1">銷售情況</th>
                        <th colspan="6" class="table_tr_center table_tr_bg_3">款項情況</th>
                    </tr>
                    <tr>
                        <th class="table_tr_center">項目名稱</th>
                        <th class="table_tr_center">服務商名稱</th>
                        <th class="table_tr_center">昨天訂單總額</th>
                        <th class="table_tr_center">今天訂單總額</th>
                        <th class="table_tr_center">去年訂單總額</th>
                        <th class="table_tr_center">今年訂單總額</th>
                        <th class="table_tr_center">訂單總額</th>

                        <th class="table_tr_center">客戶應回款總額</th>
                        <th class="table_tr_center">客戶已回款總額</th>
                        <th class="table_tr_center">客戶未回款總額</th>
                        <th class="table_tr_center">服務商應付款總額</th>
                        <th class="table_tr_center">服務商已付款總額</th>
                        <th class="table_tr_center">服務商未付款總額</th>
                    </tr>
                </thead>
                <tbody id="table_body">
                </tbody>
            </table>
        </div>
    </div>
</div>

(4)效果展示:
image.png


三、.NET Core WebAPI使用SignalR

場景:項目中需要服務端主動向客戶端發送通知消息,後端是使用.NETCore實現的,前端是使用Vue3全家桶項目,前後端分離模式。本次主要講使用SignalR如何來實現的。


3.1-服務端(.NET Core WebAPI)

1、右擊項目,點擊【管理NuGet程式包】安裝SignalR。
image.png

2、搜索【SignalR】,點擊【安裝】。
image.png
image.png

3、如果是.NET6以前的版本,在【Startup.cs】中配置:
image.png
如果是.NET6以後得版本,在【Program.cs】配置:

var builder = WebApplication.CreateBuilder(args);
//添加SignalR服務
builder.Services.AddSignalR();


app.UseEndpoints(endpoints =>
{
    endpoints.MapControllerRoute(
        name: "default",
        pattern: "{controller=Home}/{action=Index}/{id?}"
    );

    //添加SignalR端點
    endpoints.MapHub<ServerMonitorHub>("/serverMonitorHub");
});

image.png

4、創建SignalR中心

中心是一個類,用於處理客戶端<——>伺服器通信的高級管道。在SignalR_Demo項目文件夾中,創建Hubs文件夾。在Hubs文件夾中,使用已下代碼創建ChatHub類。

    public class ChatHub:Hub
    {
        /// <summary>
        /// 發送消息
        /// </summary>
        /// <param name="user">用戶名</param>
        /// <param name="message">發送信息</param>
        /// <returns></returns>
        public async Task SendMessage(string user,string message)
        {
            await Clients.All.SendAsync("ReceiveMessage", user, message);
        }
    }

3.2-客戶端(Vue3+Vite+TS)

(1)安裝SugbalR

npm install @latelier/vue-signalr

版本截圖:
image.png

(2)然後新建一個文件,用來封裝業務邏輯相關代碼。[西瓜程式猿]是在【src/utils】目錄下,創建了一個名為【signalr.ts】的文件,也可以是js文件,根據自己項目的需要去新建。
image.png
代碼:

import * as signalR  from '@microsoft/signalr';

//如果需要身份驗證
//.withUrl('/messageHub', {accessTokenFactory: () => sessionStorage.getItem('token')})
let connection;

// 建立連接
async function start(url) {
  try {
    connection = new signalR.HubConnectionBuilder()
      .withUrl(url)//跨域需要使用絕對地址
      .configureLogging(signalR.LogLevel.Information)
      .withAutomaticReconnect() // 設置自動重連機制
      .build();
  } catch(err) {
    console.log(err);
    setTimeout(start, 10000);//錯誤重連
  }
}

// 開始signalr連接
const connect = async (url) => {
  await start(url);
  console.log(`【西瓜程式猿-${new Date().toLocaleString()}:SignalR已連接成功!`);
};

// 調用服務端方法
async function send(methodName, param){
  try {
    await connection.invoke(methodName, param);
  } catch (err) {
    console.error(err);
  }
}

//斷開連接
const disconnect = async ()=>{
  await connection.stop();
  console.log(`【西瓜程式猿-${new Date().toLocaleString()}:SignalR已斷開連接!`);
};

export {
  connection,
  connect,
  send,
  disconnect
};

(3)然後再頁面進行使用。[西瓜程式猿]這裡以Echarts圖表展示為例子,首先先安裝Echarts包。

npm install echarts --save

版本截圖:
image.png

(4)然後再頁面寫入如下代碼,具體頁面樣式根據需要進行修改調整哦。

<!--
 * @Author: Kimi Liu
 * @Date: 2023-05-24 21:11:24
 * @LastEditors: Kimi Liu
 * @LastEditTime: 2023-07-07 16:18:55
 * @FilePath: \KimiToolBox\src\views\monitor\server\index.vue
 * @Description: 服務監控
 * Copyright (c) 2023 by kimiliu.cn, All Rights Reserved.
-->
<template>
  <!-- 麵包屑導航 -->
  <breadcrumb class="breadcrumb_container" />
  <div class="app_main_padding">

    <el-row>

      <el-col :span="24" class="card-box">
        <el-card>
          <template #header>
            <span><i class="el-icon-monitor"></i> 伺服器信息</span>
          </template>
          <div class="el-table el-table--enable-row-hover el-table--medium">
            <table cellspacing="0" style="width: 100%;">
              <tbody>
                <tr>
                  <td class="el-table__cell is-leaf"><div class="cell">主機名稱</div></td>
                  <td class="el-table__cell is-leaf"><div class="cell" v-if="serverInfo">{{serverInfo.HostName}}</div></td>
                  <td class="el-table__cell is-leaf"><div class="cell">系統名稱</div></td>
                  <td class="el-table__cell is-leaf"><div class="cell" v-if="serverInfo">{{serverInfo.OSDescription}}</div></td>
                </tr>
                <tr>
                  <td class="el-table__cell is-leaf"><div class="cell">IP地址</div></td>
                  <td class="el-table__cell is-leaf"><div class="cell" v-if="serverInfo">{{serverInfo.IpAddress}}</div></td>
                  <td class="el-table__cell is-leaf"><div class="cell">操作系統</div></td>
                  <td class="el-table__cell is-leaf"><div class="cell" v-if="serverInfo">{{serverInfo.OperatingSystem}} {{serverInfo.OsArchitecture}}</div></td>
                </tr>
              </tbody>
            </table>
          </div>
        </el-card>
      </el-col>


      <el-col :span="24" class="card-box">
        <el-card>
          <template #header>
            <span><i class="el-icon-monitor"></i> 應用信息</span>
          </template>
          <div class="el-table el-table--enable-row-hover el-table--medium">
            <table cellspacing="0" style="width: 100%;">
              <tbody>
                <tr>
                  <td class="el-table__cell is-leaf"><div class="cell">.NET Core版本</div></td>
                  <td class="el-table__cell is-leaf"><div class="cell" v-if="serverInfo">{{serverInfo.FrameworkDescription}}</div></td>
                  <td class="el-table__cell is-leaf"><div class="cell">記憶體占用</div></td>
                  <td class="el-table__cell is-leaf"><div class="cell" v-if="serverInfo">{{serverInfo.MemoryFootprint}}</div></td>
                </tr>
                <tr>
                  <td class="el-table__cell is-leaf"><div class="cell">環境變數</div></td>
                  <td class="el-table__cell is-leaf"><div class="cell" v-if="serverInfo">{{serverInfo.EnvironmentName}}</div></td>
                  <td class="el-table__cell is-leaf"><div class="cell">項目路徑</div></td>
                  <td class="el-table__cell is-leaf"><div class="cell" v-if="serverInfo">{{serverInfo.ContentRootPath}}</div></td>
                </tr>
              </tbody>
            </table>
          </div>
        </el-card>
      </el-col>

      <el-col :span="24" class="card-box">
        <el-card>
          <template #header>
            <span><i class="el-icon-monitor"></i> 磁碟狀態</span>
          </template>
          <div class="el-table el-table--enable-row-hover el-table--medium">
            <table cellspacing="0" style="width: 100%;" class="el-table__header">
              <thead>
                <tr>
                  <th class="el-table_2_column_12 is-leaf headWeight el-table__cell"><div class="cell">磁碟名稱</div></th>
                  <th class="el-table_2_column_11 is-leaf headWeight el-table__cell"><div>盤符路徑</div></th>
                  <th class="el-table_2_column_12 is-leaf headWeight el-table__cell"><div class="cell">文件系統</div></th>
                  <th class="el-table_2_column_12 is-leaf headWeight el-table__cell"><div class="cell">盤符類型</div></th>
                  <th class="el-table_2_column_12 is-leaf headWeight el-table__cell"><div class="cell">總大小</div></th>
                  <th class="el-table_2_column_12 is-leaf headWeight el-table__cell"><div class="cell">已用大小</div></th>
                  <th class="el-table_2_column_12 is-leaf headWeight el-table__cell"><div class="cell">可用大小</div></th>
                  <th class="el-table_2_column_12 is-leaf headWeight el-table__cell"><div class="cell">已使用率</div></th>
                </tr>
              </thead>
              <tbody v-if="diskInfos" class="table_tbody">
                <tr v-for="(sysFile, index) in diskInfos" :key="index">
                  <td class="el-table_2_column_11 el-table__cell"><div class="cell">{{ sysFile.DiskName }}</div></td>
                  <td class="el-table_2_column_11 el-table__cell"><div class="cell">{{ sysFile.RootPath }}</div></td>
                  <td class="el-table_2_column_11 el-table__cell"><div class="cell">{{ sysFile.DriveType }}</div></td>
                  <td class="el-table_2_column_11 el-table__cell"><div class="cell">{{ sysFile.FileSystem }}</div></td>
                  <td class="el-table_2_column_11 el-table__cell"><div class="cell">{{ sysFile.TotalSize }}</div></td>
                  <td class="el-table_2_column_11 el-table__cell"><div class="cell">{{ sysFile.UseSize }}</div></td>
                  <td class="el-table_2_column_11 el-table__cell"><div class="cell">{{ sysFile.ResidueSize }}</div></td>
                  <td class="el-table_2_column_11 el-table__cell"><div class="cell"><el-progress :percentage="sysFile.UseRate" :text-inside="true" :stroke-width="14" :color="customColor"/></div></td>
                </tr>
              </tbody>
            </table>
          </div>
        </el-card>
      </el-col>




      <el-col :span="24" class="card-box">
        <el-card class="box-card">
          <template #header>
            <span><i class="el-icon-monitor"></i> 狀態</span>
          </template>
          <div>
            <el-row>
              <el-col :xs="6" :sm="6" :md="6" :lg="6" :xl="6" style="margin-bottom: 10px" class="box_item">
                <div class="title">CPU使用率</div>
                <div class="content">
                  <el-progress type="dashboard" :percentage="cpuInfo.CPULoadVal" :color="customColor" />
                </div>
          
                <div class="footer">{{ cpuInfo.ProcessorCount }} 核心</div>
              </el-col>

              
              <el-col :xs="6" :sm="6" :md="6" :lg="6" :xl="6" style="margin-bottom: 10px" class="box_item">
                <div class="title">記憶體使用率</div>
                <div class="content">
                  <el-progress type="dashboard" :percentage="memoryInfo.UsedPercentage" :color="customColor" />
                </div>
                <div class="footer">{{ memoryInfo.UsedPhysicalMemory }} / {{ memoryInfo.TotalPhysicalMemory }}</div>
              </el-col>


              <el-col :xs="6" :sm="6" :md="6" :lg="6" :xl="6" style="margin-bottom: 10px" class="box_item">
                <div class="title">網路監控上傳</div>
                <div class="content">
                  <el-progress type="dashboard" :percentage="networkInfo.SentSize" :color="customColor" >
                    <template #default>
                      {{networkInfo.SentSize}} {{ networkInfo.SentSizeType }}
                    </template>
                  </el-progress>
                </div>
                <div class="footer"> {{networkInfo.SentSize}} {{ networkInfo.SentSizeType }}/S</div>
              </el-col>


              <el-col :xs="6" :sm="6" :md="6" :lg="6" :xl="6" style="margin-bottom: 10px" class="box_item">
                <div class="title">網路監控下載</div>
                <div class="content">
                  <el-progress type="dashboard" :percentage="networkInfo.ReceivedSize" :color="customColor" >
                    <template #default>
                      {{networkInfo.ReceivedSize}} {{ networkInfo.ReceivedSizeType }}
                    </template>
                  </el-progress>
                </div>
                <div class="footer"> {{networkInfo.ReceivedSize}} {{ networkInfo.ReceivedSizeType }}/S</div>
              </el-col>
            </el-row>
          </div>
        </el-card>
      </el-col>




      <el-col :span="24" class="card-box">
        <el-row :gutter="25">
            <el-col :xs="24" :sm="24" :md="12" :lg="12" :xl="12" style="margin-bottom: 10px">
              <el-card class="box-card">
                <template #header>
                  <span><i class="el-icon-monitor"></i> CPU使用率監控</span>
                </template>
                <div>
                  <div id="chart1" class="chart"></div>
                </div>
              </el-card>
            </el-col>
            <el-col :xs="24" :sm="24" :md="12" :lg="12" :xl="12" style="margin-bottom: 10px">
              <el-card class="box-card">
                <template #header>
                  <span><i class="el-icon-monitor"></i> 記憶體使用率監控</span>
                </template>
                <div>
                  <div id="chart2" class="chart"></div>
                </div>
              </el-card>
            </el-col>
          </el-row>
      </el-col>

    </el-row>
  </div>
</template>

<script lang="ts" setup>
import { ref, onMounted, onBeforeUnmount, reactive } from 'vue';
import { EChartsOption, init } from 'echarts';
import { connection, connect, disconnect } from '@/utils/signalr';
import { getApiUrl } from '@/apis/common/utils';

// CPU監控圖標
const cpuOption = reactive<EChartsOption>(
  {
    tooltip: {
      trigger: 'axis'
    },
    xAxis: {
      type: 'category',
      boundaryGap: false,
      data: []
    },
    yAxis: {
      type: 'value',
      min: 0,
      max: 100,
      interval: 20,
      axisLabel: {
        formatter: '{value}%'
      }
    },
    series: [
      {
        data: [],
        type: 'line'
      }
    ]
  }
);


const memoryOption = reactive<EChartsOption>(
  {
    tooltip: {
      trigger: 'axis'
    },
    xAxis: {
      type: 'category',
      boundaryGap: false,
      data: []
    },
    yAxis: {
      type: 'value',
      min: 0,
      max: 100,
      interval: 20,
      axisLabel: {
        formatter: '{value}%'
      }
    },
    series: [
      {
        data: [],
        type: 'line'
      }
    ]
  }
);


// 獲得伺服器信息
const serverInfo = ref();
const cpuInfo = ref();
const memoryInfo = ref();
const diskInfos = ref([]);
const networkInfo = ref();

const getServerInfos = (res) => {
  serverInfo.value = res.Server;
  cpuInfo.value = res.CPU;
  memoryInfo.value = res.Memory;
  diskInfos.value = res.Disk;
  networkInfo.value = res.Network;

  if(cpuOption.xAxis.data.length >= 8) {
    cpuOption.xAxis.data.shift();
    cpuOption.series[0].data.shift();

    memoryOption.xAxis.data.shift();
    memoryOption.series[0].data.shift();
  }

  cpuOption.xAxis.data.push(res.Time);
  cpuOption.series[0].data.push(res.CPU.CPULoadVal);

  memoryOption.xAxis.data.push(res.Time);
  memoryOption.series[0].data.push(res.Memory.UsedPercentage);

  // 獲取dom,斷言HTMLElement類型,否則會報錯
  const chartEle: HTMLElement = document.getElementById('chart1') as HTMLElement;
  const chart = init(chartEle);
  chart.setOption(cpuOption);

  // 獲取dom,斷言HTMLElement類型,否則會報錯
  const chartEle2: HTMLElement = document.getElementById('chart2') as HTMLElement;
  const chart2 = init(chartEle2);
  chart2.setOption(memoryOption);

  // 重置圖表大小
  window.addEventListener('resize', () => {
    chart.resize();
    chart2.resize();
  });
};

onMounted(async () => {
  // 建立連接
  connect('http://192.168.11:8080/serverMonitorHub');

  // 註冊方法(接收伺服器推送過來的數據)
  connection.on('ReceiveMessage', (res) => {
    console.log(`【${new Date().toLocaleString()}】:從伺服器同步成功!`);
    if(res) {
      const result = JSON.parse(res);
      getServerInfos(result);
    }
  });

  // 開始連接
  await connection.start();
});


// 卸載
onBeforeUnmount(() => {
  // 斷開連接
  disconnect();
});

// 進度條顏色
const customColor = ref('#ff7831');
</script>

<style lang="scss" scoped>
.card-box {
	padding-right: 15px;
	margin-bottom: 15px;
}

:deep(.el-card.is-always-shadow) {
  box-shadow: 0 5px 8px 0 rgba(0,0,0,0.03);
  border: none;
}

.chart {
  width: 100%;
  height: 300px;
}
.box_item {
  display: flex;
  justify-content: center;
  flex-direction: column;
  align-items: center;
}

.table_tbody {
  tr td{
    padding: 15px 0;
    text-align: center;
  }
}
</style>

(5)效果展示:
image.png



原文鏈接:https://www.cnblogs.com/kimiliucn/p/17648543.html

版權聲明:本文為原創文章,版權歸 [西瓜程式猿] 所有,轉載請註明出處,有任何疑問請私信咨詢。
原文鏈接:https://www.cnblogs.com/kimiliucn/p/17648543.html

您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 本文是區塊鏈瀏覽器系列的第四篇。 在[上一篇文章](https://mengbin.top/2023-08-13-blockBrowser/)介紹如何解析區塊數據時,使用`session`對客戶端上傳的pb文件進行區分,到期後自動刪除。 在這片文章中,會著重介紹下認證系統的實現,主要分為三部分: - ...
  • 需求背景 需要在前端頁面展示當前表欄位的所有上下游血緣關係,以進一步做數據診斷治理。大致效果圖如下: 首先這裡解釋什麼是表欄位血緣關係,SQL 示例: CREATE TABLE IF NOT EXISTS table_b AS SELECT order_id, order_status FROM t ...
  • 由於本人太弱,可能講解有誤,請讀者指出。 # 什麼是網路流 網路流是通過構建從源點到匯點的有向圖模型來解決圖論問題。從理論上講,網路流可以處理所有二分圖問題。 二分圖和網路流的難度都在於問題建模,一般不會特意去卡演算法效率,所以只需要背一兩個簡單演算法的模板就能應付大部分題目了。 # 最大流問題 ## ...
  • 1.已解密的登錄請求 推理: AppScan 識別了不是通過 SSL 發送的登錄請求。 測試請求和響應: 1.1.1 產生的原因 登錄介面,前端傳入的密碼參數沒有經過md5的加密就直接傳給了後端 1.1.2 解決方法 前端代碼傳參的時候做md5加密處理 2.會話標識未更新 推理: 測試結果似乎指示存 ...
  • ### 1. 建立工程 bh003_ble [源碼](https://github.com/densen2014/BlazorHybrid/tree/master/bh100days/bh003_ble?WT.mc_id=DT-MVP-5005078) ### 2. 添加 nuget 包 ``` ` ...
  • ## zhontai 項目 > 基於 .Net7.x + Vue 等技術的前後端分離後臺許可權管理系統,想你所想的開發理念,希望減少工作量,幫助大家實現快速開發 後端地址:https://github.com/zhontai/Admin.Core 前端地址:https://github.com/zho ...
  • 最近建立一個新項目準備寫一個小demo,新建項目時選的時.Net7。寫代碼的時候發現。Net7沒有系統的中文註釋,去官網下載的時候發現沒有關於.Net7的漢化文件包,最新的漢化包是。Net5的,可能是我沒找到,我這裡是把.Net5漢化包放到了.Net7下麵了,測試好用。 一、.NET Framewo ...
  • # C#文本轉語音(科大訊飛離線版) ### 引言 文本轉語音(Text To Speech),簡稱TTS,在很多業務場景會用到,比如廣播大廳,人機互動等。C#要實現TTS有不少選擇,比如調用System.Speech,此處就不細說了,下麵主要介紹一下C#調用科大訊飛的離線語音合成SDK來實現文本轉 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...