Dragon
古风汉服美女图集

将WordPress服务器仪表板转变为可拖放小部件的简易指南

在上一篇文章中,我们创建了插件的基本结构。现在是时候为每个小部件实现渲染类了。

回想一下,所有小部件提供程序都必须实现 Provider 接口。它们还必须位于名为 widget 的文件夹内,并位于名称空间 AX\StatBoard\Widget 下。如果我们想添加一种新的指标,只需创建一个相应的类,并创建一个对象并使用 add_provider 方法将其添加到 Widget 类中。

RAM 使用情况小部件

我们想要显示的第一条信息是当前正在使用的 RAM 量以及当前空闲的 RAM 量。

在本例中,free -m 是我们的朋友 - 它告诉我们 RAM 使用情况。 -m 开关是以兆字节为单位输出结果。

[vagrant@vagrant-centos64 ~]$ free -m
             total       used       free     shared    buffers     cached
Mem:           589        366        223          0          9         57
-/+ buffers/cache:        299        290
Swap:            0          0          0

我们将类命名为 Ram。相应的文件将为 widget/ram.php。我们在这里只是编写基础并实现 get_title

<?php
namespace AX\StatBoard\Widget;

class Ram implements Provider {
  function __construct() {
  }

  public function get_title() {
    return "Ram Usage";
  }

?>

接下来,我们将实现
get_metric 来实际运行必要的 shell 命令并提取信息。稍后我将解释
get_metric 的更多细节。

<?php
function get_metric() {
    $df = `free -m | grep -E "(Mem|Swap)" | awk '{print $1, $2, $3, $4}'`;
    $df = explode("\n", $df);
    if ( is_array( $df ) && 2 <= count( $df ) ) {
      $df = array_map( function ( $line ) {
        if ( empty( $line ) ) {
          return;
        }
        $segment = preg_split( '/\s+/', $line );

        return array(
          'type' => trim( $segment[0]," :" ),
          'total' => (int)$segment[1],
          'used' =>  (int)$segment[2],
          'free' =>  (int)$segment[3],
        );
      }, $df );
      return $df;
    }
    return false;
  }
?>

我们执行命令
free -m | grep -E "内存|交换" | awk '{print $1, $2, $3, $4}' .它的输出看起来与此类似。

[vagrant@vagrant-centos64 ~]$ free -m | grep -E "Mem|Swap" | awk '{print $1, $2, $3, $4}'
Mem: 589 541 47
Swap: 255 0 255
[vagrant@vagrant-centos64 ~]$

我们通过将行拆分为数组来使用 PHP 解析每一位信息。我们使用
array_map 循环遍历数组的所有元素,对于每一行,我们用空格分隔,然后返回一个包含元素的
关联数组:

  • type:第一个字段

  • total:第二个字段

  • used:第三个字段
  • free:第四个字段

现在,是
get_content 的时候了。

 public function get_content() {
    $metric = $this->get_metric();
    $data = array(
      array('Type', 'Used(MB)', 'Free(MB)')
    );

    foreach ($metric as $item) {
      if (empty($item)) {
        continue;
      }
      if ($item['type'] !== 'Mem' && $item['type'] !== 'Swap') {
        continue;
      }
      if ( 0 == ($item['free'] + $item['used'])) {
        continue;
      }

      $data[] = array(
        $item['type'],$item['used'], $item['free']
        );
    }
    $data   =  json_encode($data);
    echo <<<EOD
      <div id="widget_ram_usage"></div>
      <script type="text/javascript">
      google.setOnLoadCallback(function () {
        var data = google.visualization.arrayToDataTable({$data});
        var options = {
          isStacked: true
        };
        var chart = new google.visualization.ColumnChart(document.getElementById('widget_ram_usage'));
        chart.draw(data, options);
      })        
    </script>
EOD;
  }

我们使用堆积条形图来显示 RAM 使用情况。

首先,我们调用
get_metric() 来获取必要的数据。然后,我们只需循环它并格式化它以匹配 Google 图表数据要求。最后,我们使用
json_encode 将它们转换为 JavaScript 对象表示法(或 JSON)。然后,我们输出一个 HTML 代码,其中包含
div 元素来保存图表对象。

最后,我们调用相应的 Google Chart API 将图表渲染到
div 元素中。

安装的软件

我们将介绍的第二个小部件是显示已安装软件的小部件。它是一个小部件,旨在显示我们在服务器上有哪些常见软件包以及哪个版本。

例如,我们是否安装了 NodeJS,是否安装了 Ruby?我们使用的是哪个版本的 PHP?等等。

让我们使用以下初始内容创建
widget/software.php

<?php
namespace AX\StatBoard\Widget;

class Software implements Provider {
  function __construct() {
  }

  public function get_title() {
    return "Installed Software";
  }
  function get_metric() {
    $cmds = array();

    $package = array(
      'php'   => '-v', 
      'node'  => '-v',
      'mysql' => '-V', 
      'vim'   => '--version',
      'python' => '-V', 
      'ruby'  => '-v', 
      'java'  => '-version',
      'curl'  => '-V');
  
    foreach ( $package as $cmd=>$version_query ) {
      if ( NULL == $cmds[$cmd] = shell_exec( "which $cmd" ) ) {
        $cmds[ $cmd ] = 'Not installed';
        continue;
      }
      $version = shell_exec( "$cmd $version_query" );
      $version = explode( "\n", $version );
      if ( is_array( $version ) ) {
        $version = array_shift( $version );
      }
      $cmds[ $cmd ] .= '<br>' . $version;
    }
    return $cmds;
  }

因此,与往常一样,我们有
get_title ,它只返回一个简单的字符串。对于
get_metric(),我们想知道是否安装了特定的软件。如果有,则获取其版本信息。

为此,我们使用显示软件版本的开关创建一个命令数组。以PHP为例,
php -v 显示版本信息,
mysql --version 显示MySQL信息。

如果命令返回且错误或未找到命令,则
shell_exec 将返回
false。此时,我们可以判断该软件没有安装;否则,我们可以解析结果以显示版本信息。然后,我们逐行分割结果,并检索第一行作为版本信息。这是因为我们需要的信息只能在第一行找到。

对于其他应用程序,某些命令过于冗长,包含许多信息。一旦我们有了数据,就可以创建
get_content 方法了。

  public function get_content() {
  
    $cmds = $this->get_metric();
    $content = '';
    
    foreach ( $cmds as $cmd => $info ) {
      $content .= "<p><strong>$cmd</strong>&nbsp; $info</p>";
    }
    echo $content;
    
  }

我们只是展示此类数据的基本表格。这是显示时的仪表板:

磁盘使用情况

现在我们将解决磁盘使用问题。我们将处理此任务的类命名为 Disk。让我们先制作基本骨架。

<?php
namespace AX\StatBoard\Widget;

class Disk implements Provider {
  function __construct() {
  }
  public function get_title() {
    return "Disk Usage";
  }
}

与往常一样,我们必须实现
Provider 接口。我们在这里为我们的小部件设置一个标题。接下来是我们课程的核心:获取磁盘使用情况的方法。

<?php  
  function get_metric() {
    $df = `df -h`;
    $df = explode("\n", $df);
    if (is_array($df) && count($df)>=2) {
      array_shift($df); //Get rid the first line
      $df = array_map(function ($line) {
        if (empty($line)) {
          return NULL;
        }
        $segment=preg_split('/\s+/', $line);

        return array(
          'filesystem' => $segment[0],
          'size' => $segment[1],
          'used' => $segment[2],
          'available' => $segment[3],
          'use_percent' => $segment[4],
        );
      }, $df);
      return $df;
    }
    return false;
  }

在本系列的第一部分中,我们对
df 命令有了一些了解,因此理解以下命令应该很容易:

回想一下
df 输出:

[vagrant@vagrant-centos64 ~]$ df -h
Filesystem            Size  Used Avail Use% Mounted on
/dev/sda1             7.3G  1.4G  5.6G  20% /
tmpfs                 295M     0  295M   0% /dev/shm
/vagrant               60G   55G  4.9G  92% /vagrant
/data/GeoIP            60G   55G  4.9G  92% /data/GeoIP
/var/webapps           60G   55G  4.9G  92% /var/webapps
/var/www/html          60G   55G  4.9G  92% /var/www/html

我们将其逐行拆分,将其变成数组。我们循环遍历每一行,用空格分割整行,再次将其转换为数组。然后,我们只需将值映射为更友好、人类可读的关联数组。当我们有了这些数据后,我们可以将其放入
get_content.

  public function get_content() {
    $metric = $this->get_metric();
    $data = array(
      array( 'Disk', 'Space' )
    );

    $disk_container = array();
    $data_partition = array(
      array('Filesystem', 'Free(GB)', 'Used(GB)')
    );    
    foreach ( $metric as $disk ) {
      $size = intval( $disk['size'] );
      if ( 'M' == substr( $disk['size'], -1 ) ) {
        $size = round( $size / 1024, 2 );
      }
      $used = intval( $disk['used'] );
      if ('M' == substr( $disk['used'], -1 ) ) {
        $used = round( $used / 1024, 2 );
      }

      if ( empty( $size ) ) {
        continue;
      }
      $data[] = array( $disk['filesystem'], $size );
      $data_partition[] = array($disk['filesystem'], $size - $used, $used);
    }
  }

我们遍历度量数组并尝试将 MB 空间转换为 GB 空间。我们构建一个数组来匹配图表数据格式要求。数据数组应如下所示:

[  ['File System', 'Free', 'Used',
  ['/dev/sda1', 10, 24],
  ['/dev/sda2', 28, 19]]

一旦我们有了数据,我们就开始渲染图表。我们将制作两个图表:

  1. 第一个图表显示了每个已安装文件系统的总空间。对于此数据,我们将使用饼图。

  2. 第二个图表用于显示每个已安装文件系统的磁盘使用情况。为此,我们将使用条形图。

为此,我们将方法修改为以下内容:

  public function get_content() {
    $metric = $this->get_metric();
    $data = array(
      array('Disk', 'Space')
    );

    $disk_container = array();
    $data_partition = array(
      array('Filesystem', 'Free(GB)', 'Used(GB)')
    );    
    foreach ($metric as $disk) {
      $size = intval($disk['size']);
      if ('M' == substr($disk['size'], -1)) {
        $size = round($size / 1024, 2);
      }
      $used = intval($disk['used']);
      if ('M' == substr($disk['used'], -1)) {
        $used = round($used / 1024, 2);
      }

      if (empty($size)) {
        continue;
      }
      $data[] = array($disk['filesystem'], $size);
      $data_partition[] = array($disk['filesystem'], $size - $used, $used);
    }
    $data = json_encode($data);
    $data_partition = json_encode($data_partition);

    echo <<<EOD
      <div id="widget_disk_usage"></div>
      <div id="widget_disk_partion"></div>
      <script type="text/javascript">
      google.load("visualization", "1", {packages:["corechart"]});
      google.setOnLoadCallback(function () {
        var data = google.visualization.arrayToDataTable({$data});
        var options = {
          is3D: true,
        };
        var chart = new google.visualization.PieChart(document.getElementById('widget_disk_usage'));
        chart.draw(data, options);

        var data2 = google.visualization.arrayToDataTable({$data_partition});
        var options2 = {
          isStacked: true
        };
        var chart2 = new google.visualization.ColumnChart(document.getElementById('widget_disk_partion'));
        chart2.draw(data2, options2);

      })        
    </script>
EOD;
  }

我们创建了两个
div 元素来包含信息

      <div id="widget_disk_usage"></div>
      <div id="widget_disk_partion"></div>

然后,使用图表 API 的绘制方法将图表渲染在这些元素内。这里最令人困惑的事情可能是我们图表的数据格式。

结果如下:

服务器信息

这个小部件向我们显示信息:Linux 内核、CPU 架构、正常运行时间、IP 地址。我们这里不需要图表,一个简单的数据表就可以完成这项工作。调用该类是Server。这是 widget/server.php

的第一个内容

<?php
namespace AX\StatBoard\Widget;
use DateTime;

class Server implements Provider {
  function __construct() {
  }

  public function get_title() {
    return "Server Info";
  }
  
  /**
   * Return server info: OS, Kernel, Uptime, and hostname
   * @return array with 3 metric:
   *          * hostname
   *          * os
   *          * uptime
   */
  function get_metric() {
    $server = array();
    $server['hostname'] = `hostname`;
    $server['os']       = `uname -sr`;
    $server['core']     = `grep -c ^processor /proc/cpuinfo`;
    $total_uptime_sec = time() - `cut -d. -f1 /proc/uptime`;
    
    $now = new DateTime("now");
    $server['uptime'] = $now->diff(new DateTime("@$total_uptime_sec"))->format('%a days, %h hours, %i minutes and %s seconds');

    // Get the external ip with ifconfig.me, a website that show you ip address in plaintext
    // when sending request with curl header
    $server['ip'] = `curl ifconfig.me`;
    $server['ram'] = `free -m | grep Mem | awk '{print $2}'`;
    $server['cpu'] =`cat /proc/cpuinfo | grep "model name" | awk '{print $4,$5,$6,$7}'`;

    return $server;
  }

}

至此,您应该熟悉
get_title()。我们只是返回这个小部件的标题。

首先,我们使用语句
use DateTime 因为我们位于名称空间
AX\StatBoard\Widget 中,并且
DateTime 类来自全局命名空间。每次我们想要使用
DateTime 时,我们都必须输入
\DateTime。因此,我们使用命名空间导入来使
DateTime 名称在我们当前的命名空间中可用。

将其视为符号链接。在
get_metric 中,我们运行 shell 命令来获取结果并将其分配回来。

主机名

显示您的服务器主机名。

名称-sr

显示Linux内核信息:

[vagrant@vagrant-centos64 ~]$ uname -sr
Linux 2.6.32-358.23.2.el6.x86_64

grep -c ^处理器/proc/cpuinfo

-c 开关打印输入字符串中匹配行的计数。 /proc/cpuinfo 包含处理器信息。我们 grep 它并计算文字处理器的出现次数。这是我的 32 核结果。

$ grep -c ^processor /proc/cpuinfo
32

剪切-d。 -f1 /proc/uptime

此命令显示服务器已启动并运行的秒数。我们将秒数转换为“x 天 y 小时 z 分钟”的格式,以使其更加用户友好。

使用 DateTime::diff 我们可以轻松实现这一点。我们创建一个带有当前时间戳的 DateTime 对象,另一个带有时间戳的对象是当前时间戳减去正常运行时间的秒数。然后使用 format 方法将其格式化为人类友好的字符串。

这是我的结果,正常运行时间为 26194091 秒。

$ cut -d. -f1 /proc/uptime
26194091

卷曲 ifconfig.me

ifconfig.me 是一项在浏览器内直接访问时显示您的 IP 地址的服务。如果您使用 curl 向其发送请求,它将以单个字符串形式返回您的 IP 地址。

[vagrant@vagrant-centos64 ~]$ curl ifconfig.me
76.102.253.237

CPU型号

如上所述,
/proc/cpuinfo存储了CPU信息。我们可以从中提取CPU型号。例如:

[vagrant@vagrant-centos64 ~]$ cat /proc/cpuinfo | grep "model name" | awk '{print $4,$5,$6,$7}'
Intel(R) Core(TM) i5-4250U CPU

一旦我们在数组中获得了所有可用数据,我们就会返回它并向
get_content 方法提供这些数据。这是
get_content,仅显示数据:

  public function get_content() {
    $server = $this->get_metric();
    echo <<<EOD
    <strong>Ip Address</strong>&nbsp;{$server['ip']}<br>
    <strong>CPU</strong>&nbsp; {$server['cpu']}<br>
    <strong>Number of Core</strong>&nbsp; {$server['core']}<br>
    <strong>Ram</strong>&nbsp; {$server['ram']}<br>
    <strong>Hostname</strong>&nbsp;{$server['hostname']}<br>
    <strong>OS</strong> {$server['os']}<br>
    <strong>Uptime</strong> {$server['uptime']}<br>
EOD;
  }

这是仪表板上的小部件。

处理器

监控我们的处理器是其中之一我们可以展示的最重要的东西。我们想知道某个特定进程正在使用多少 CPU 和/或消耗了多少内存。我们将我们的类称为
Process,首先从
get_title
get_metric 开始。我将在代码后面解释
get_metric 的更多细节:

<?php
namespace AX\StatBoard\Widget;

class Process implements Provider {


  public function get_title() {
    return "Processes";
  }

  /**
   * Return server info: OS, Kernel, Uptime, and hostname
   * @return array with 3 metric:
   *          * hostname
   *          * os
   *          * uptime
   */
  function get_metric() {
    $processes = array();
    $output = `ps -eo pcpu,pmem,pid,user,args,time,start | grep -v '\[' | sort -k 1 -r | head -30 | awk '{print $4,$3,$1,$2,$7,$6,$5}'`;
    $output = explode("\n", $output);
    if (!is_array($output) || count($output)<2) {
      return false;      
    }
    array_shift($output);
    foreach ($output as $line) {
      //$line = preg_split('/\s+/', $line);
      $line = explode(' ', $line);
      if (count($line)<6) {
        continue;
      }
      //var_dump($line);
      //echo count($line);
      if (empty($processes[$line[6]])) {
        $processes[$line[6]] = array_combine(array('user', 'pid', '%cpu', '%mem','start','time', 'command'), $line);
      } else {
        $processes[$line[6]]['%cpu'] += $line[2];
        $processes[$line[6]]['%mem'] += $line[3];
      }
    }

    return $processes;
  }

}

显示进程正在运行的命令是 ps。它通过开关
-e 提供了广泛的信息,因为它允许我们查看每个进程。对于我们的小部件,我们只需要提取 COU、内存、PID、用户、参数、时间和启动。

我们可以结合
-o 表示用户定义的格式,例如:
ps -eo pcpu,pmem,pid,user,args,time,start。如果您尝试该命令,您将得到一些奇怪的过程,例如:

[vagrant@vagrant-centos64 ~]$ ps -eo pcpu,pmem,pid,user,args,time,start
%CPU %MEM   PID USER     COMMAND                         TIME  STARTED
 0.0  0.2     1 root     /sbin/init                  00:00:00 06:50:39
 0.0  0.0     2 root     [kthreadd]                  00:00:00 06:50:39
 0.0  0.0     3 root     [migration/0]               00:00:00 06:50:39

注意 [kthread]、[migration/0]。基本上,这意味着该命令无法在文件系统中找到。它可能是一些内部系统进程或内核线程,我们可能永远不想关心它。因此,我们应该使用
grep 来消除这些进程。 grep 有
-v 开关使我们能够反转匹配。它返回的结果不包含我们传递给它的字符串。

[vagrant@vagrant-centos64 ~]$ ps -eo pcpu,pmem,pid,user,args,time,start | grep -v '\['
%CPU %MEM   PID USER     COMMAND                         TIME  STARTED
 0.0  0.2     1 root     /sbin/init                  00:00:00 06:50:39
 0.0  0.1   292 root     /sbin/udevd -d              00:00:00 06:50:41
 0.0  0.1   811 root     /sbin/dhclient -H vagrant-c 00:00:00 06:50:48
 0.0  0.2   948 root     /sbin/rsyslogd -i /var/run/ 00:00:00 06:50:50
 0.0  0.1   966 rpc      rpcbind                     00:00:00 06:50:50
 0.0  0.2   984 rpcuser  rpc.statd                   00:00:00 06:50:50
 0.0  0.0  1011 root     rpc.idmapd                  00:00:00 06:50:51
 0.0  0.2  1073 root     /usr/sbin/VBoxService       00:00:00 06:50:51

为了使数据看起来不错,我们应该按内存或CPU对进程进行排序。在我们的教程中,我们按
%MEM 排序。我们可以使用Linux的sort命令来做到这一点。
%MEM 是第二列。

就像索引为零的数组一样,第二个元素通过索引键 1 访问。我们可以使用
sort -k 1。它按从最低到最高的顺序排序。我们实际上关心的是首先消耗大量内存的进程。为此,我们应该使用
sort -k 1 -r 反转顺序。一旦我们得到结果,我们可能只需要前30个过程。当然,这取决于您,因为您可以选择包含所有内容,但我想保持简短。 30 听起来是一个合理的数字。

最后,我们使用 awk 来格式化输出。这是我们的命令和示例输出:

[vagrant@vagrant-centos64 ~]$ ps -eo pcpu,pmem,pid,user,args,time,start | grep -v '\[' | sort -k 1 |  head -30 | awk '{print $4,$3,$1,$2,$7,$6,$5}'
root 1151 0.0 0.0 00:00:00 -d /sbin/udevd
root 1152 0.0 0.0 00:00:00 -d /sbin/udevd
root 292 0.0 0.0 00:00:00 -d /sbin/udevd
root 811 0.0 0.0 vagrant-c -H /sbin/dhclient
root 1 0.0 0.1 06:50:39 00:00:00 /sbin/init
root 2153 0.0 0.1 -q -1 /sbin/dhclient
root 3642 0.0 0.1 00:00:00 -s /usr/sbin/anacron
vagrant 3808 0.0 0.1 pcpu,pmem,pid,user,a -eo ps
vagrant 3810 0.0 0.1 1 -k sort
vagrant 3811 0.0 0.1 00:00:00 -30 head
vagrant 3812 0.0 0.1 $4,$3,$1,$2,$7,$ {print awk
root 948 0.0 0.1 /var/run/ -i /sbin/rsyslogd
rpc 966 0.0 0.1 06:50:50 00:00:00 rpcbind
root 1073 0.0 0.2 06:50:51 00:00:00 /usr/sbin/VBoxService
root 1105 0.0 0.2 06:50:51 00:00:00 /usr/sbin/sshd
root 1121 0.0 0.2 06:50:52 00:00:00 crond
rpcuser 984 0.0 0.2 06:50:50 00:00:00 rpc.statd
496 1088 0.0 0.3 -p -d memcached
vagrant 3544 0.0 0.3 00:00:00 vagrant@pts/0 sshd:
vagrant 3545 0.0 0.3 06:59:27 00:00:00 -bash
root 1113 0.0 1.7 06:50:52 00:00:00 /usr/sbin/httpd
apache 1157 0.0 4.2 06:50:53 00:00:01 /usr/sbin/httpd
apache 3438 0.0 4.2 06:55:39 00:00:01 /usr/sbin/httpd

一旦我们得到结果,我们将它分成一个数组并使用
foreach 循环它。我们将同名进程分组到一个元素中,并将 CPU 百分比和内存添加到其中。

<?php
//...
// inside get_content

    foreach ( $output as $line ) {
      //$line = preg_split( '/\s+/', $line );
      $line = explode( ' ', $line );
      if ( 6 > count( $line ) ) {
        continue;
      }
      if ( empty( $processes[ $line[6] ] ) ) {
        $processes[ $line[6]] = array_combine( array( 'user', 'pid', '%cpu', '%mem','start','time', 'command' ), $line );
      } else {
        $processes[ $line[6] ]['%cpu'] += $line[2];
        $processes[ $line[6] ]['%mem'] += $line[3];
      }
    }
//...    

我们使用
array_combine 从两个数组创建一个关联数组:一个用于键,一个用于值。

剩下的就是实现
get_content 方法。只是提醒我们必须实现三个方法:
get_title,

get_metric

get_content.对于这个过程,我们只想展示一个简单的表格。

我们的
get_content 方法很简单。

  public function get_content() {
    $processes = $this->get_metric();
    $html = '<table class="wp-list-table widefat"><thead><tr>
      <th>User</th>
      <th>Pid</th>
      <th>%CPU</th>
      <th>%Mem</th>
      <th>Command</th>
      </tr></thead><tbody>';
    foreach ($processes as $process) {
      $html .= "<tr>
        <td>{$process['user']}</td>
        <td>{$process['pid']}</td>
        <td>{$process['%cpu']}</td>
        <td>{$process['%mem']}</td>
        <td>{$process['command']}</td>
        </tr>";
    }
    $html .= '</tbody></table>';
    echo $html;
  }

这是我们得到的结果:

平均负载

Linux 有一个命令可以显示过去一分钟、五分钟和 15 分钟内 CPU 和 IO 的平均负载。让我们把它压缩成一个小部件。称之为
Cpuload,并创建我们的
widget/cpuload.php

<?php
namespace AX\StatBoard\Widget;

class Cpuload implements Provider {
  function __construct() {
  }

  public function get_title() {
    return "CPU Load";
  }
  function get_metric() {    $number_of_core = intval(`/bin/grep -c processor /proc/cpuinfo`);    $loadAvg = `cat /proc/loadavg | /usr/bin/awk '{print $1,$2,$3}'`;    $loadAvg = explode(' ', $loadAvg);    if ($loadAvg <3) {      return false;    }    $loadTimes = array('1 min', '5 mins', '15 mins');    return array_map(      function ($loadtime, $value, $number_of_core) {        return array($loadtime, round($value * 100 / $number_of_core, 2), $value);      },        $loadTimes,        $loadAvg,        array_fill(0, 3, $number_of_core)      );  }

}

首先,我们通过读取
/proc/cpuinfo 来统计 CPU 核心数,并统计包含“processor”一词的行数。我们在
服务器信息

部分对此进行了介绍。

在Linux中,
/proc/loadavg保存平均负载信息。前三列分别是1分钟、5分钟和15分钟的负载。
awk 在这里用于过滤掉我们需要的字段。

  ~  cat /proc/loadavg
0.01 0.04 0.05 1/217 16089
  ~  cat /proc/loadavg | awk '{print $1, $2, $3}'
0.01 0.04 0.05

将上面的结果用空格分割,构建一个包含三个元素的数组。计算所有核心的平均负载。因此,为了获得结果,我们使用
array_map 循环遍历
$loadAvg 数组,并除以我们拥有的核心数。请注意,我们创建了 2 个与
$loadAvg 长度相同的额外数组,一个用于键,另一个用于保存核心数,以便将所有这些一一传递给
array_map

get_content 的时间:

  public function get_content() {
    $metrics = $this->get_metric();
    if ( ! $metrics ) {
      return false;
    }
    // see https://google-developers.appspot.com/chart/interactive/docs/gallery/barchart#Data_Format for more detai of format
    $data = array( array( 'Duration', '% Load' ) );
    foreach ( $metrics as $key=>$metric ) {
      array_push( $data, array( $metric[0], $metric[1] ) );
    }
    $data = json_encode( $data );
    echo <<<EOD
<div id="avg_load"></div>
<script type="text/javascript">
      google.load("visualization", "1", {packages:["corechart"]});
      google.setOnLoadCallback(drawChart);
      function drawChart() {
        var data = google.visualization.arrayToDataTable($data);

        var options = {    
          hAxis: {
            titleTextStyle: {color: 'red'},
            minValue:0,
            maxValue:100
          }
        };

        var chart = new google.visualization.BarChart(document.getElementById('avg_load'));
        chart.draw(data, options);
      }
    </script>
EOD;
  }

我们使用条形图并从数组创建一个数据数组,然后使用
json_encode 将其转换为与条形图数据格式匹配的 JavaScript 表示法数组。

例如:

[ ["Duration","% Load"], ["1 min",20], ["5 mins",11], ["15 mins",3]]

这是渲染图表时的结果:

以太网接口

我们要处理的下一个小部件是以太网接口。某些服务器可以有多个以太网接口,并为其分配不同的 IP 地址。

看到这些信息非常有用。我们将这个类称为 Ethernet,从 widget/ethernet.php 的基本内容开始。

<?php
/**
 * Adopt from https://github.com/afaqurk/linux-dash/blob/master/sh/ip.php
 *
 */
namespace AX\StatBoard\Widget;

class Ethernet implements Provider {
  function __construct() {
  }

  public function get_title() {
    return "Ethernet";
  }

  function get_metric() {
    $ethernet = array();
    $output = shell_exec("ip -oneline link show | awk '{print $2}' | sed 's/://'");
    if (!$output) { // It didn't work with "ip" , so we do it with ifconfig
      $output = shell_exec(
        'ifconfig | /bin/grep -B1 "inet addr" | /usr/bin/awk \'' .
        '{ if ( $1 == "inet" ) { print $2 }' .
        'else if ( $2 == "Link" ) { printf "%s:",$1 } }\' | /usr/bin/awk' .
        ' -F: \'{ print $1","$3 }\''
      );
      $output = trim($output, " \n");
      $output = `ifconfig | grep "Link encap" | awk '{ print $1 }'`;
      $interfaces = explode("\n", $output);
      $output = `ifconfiga | grep "inet addr" | awk '{ print $2 }' | sed 's/addr://'`;
      $addreses = explode("\n", $output);
      $output = trim($output, " \n");
      return array_combine($interfaces, $addreses);
    }

    $output = trim($output, " \n");
    $interfaces = explode("\n", $output);
    $addreses = array();
    foreach ($interfaces as $interface) {
      $output = shell_exec("ip -oneline -family inet addr show $interface | awk '{print $4}' | cut -d'/' -f1");
      $addreses[] = $output;
    }
    return array_combine($interfaces, $addreses);
  }

}

所以小部件的标题将是
以太网。对于
get_metric,我们将尝试获取所有以太网接口名称,然后获取每个接口的 IP 地址,并将设备名称和 IP 地址结合起来返回。

我们需要处理两种情况:如果服务器使用 ifconfig 或服务器使用 ip 实用程序。较新的服务器很可能有
ip 而不是
ifconfig; 因此,我们应该首先运行 ip 来获取以太网设备。

$output = shell_exec("ip -oneline link show | awk '{print $2}' | sed 's/://'");

使用 IP 实用程序

带有
ip 命令和
-oneline 将仅在一行中显示输出,其中
link
show 将列出所有设备。我们使用 awk 获取第二列,其中包含设备名称;但是它包含 : 字符。我们使用
sed 将 : 替换为空字符串。

这是我们在命令行上运行它们时的结果:

[vagrant@vagrant-centos64 sbin]$ ip -oneline link show
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 16436 qdisc noqueue state UNKNOWN \    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP qlen 1000\    link/ether 08:00:27:08:c2:e4 brd ff:ff:ff:ff:ff:ff
3: eth1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP qlen 1000\    link/ether 08:00:27:eb:11:e4 brd ff:ff:ff:ff:ff:ff
[vagrant@vagrant-centos64 sbin]$ ip -oneline link show | awk '{print $2}'
lo:
eth0:
eth1:
[vagrant@vagrant-centos64 sbin]$ ip -oneline link show | awk '{print $2}' | sed 's/://'
lo
eth0
eth1
[vagrant@vagrant-centos64 sbin]$

如果
shell_exec 成功运行,我们将继续使用 IP 实用程序。上面的输出被逐行分割成一个数组。

$output = trim($output, " \n");
$interfaces = explode("\n", $output);

然后我们循环遍历该数组,并再次使用
ip 命令
ip -oneline -family inet addr show device_name 来获取分配给设备的 IP 地址。

    $addreses = array();
    foreach ($interfaces as $interface) {
      $output = shell_exec("ip -oneline -family inet addr show $interface | awk '{print $4}' | cut -d'/' -f1");
      $addreses[] = $output;
    }

IP 地址出现在第四列中,因此 awk 用于打印该值。然后我们使用 cut 将命令按 / 分割,并使用 swich -f1 获取第一个元素。

查看输出,看看当我们在命令行上运行它们时,它们是如何工作的:

[vagrant@vagrant-centos64 sbin]$ ip -oneline -family inet addr show eth1
3: eth1    inet 192.168.1.111/24 brd 192.168.1.255 scope global eth1
[vagrant@vagrant-centos64 sbin]$ ip -oneline -family inet addr show eth1 | awk '{print $4}'
192.168.1.111/24
[vagrant@vagrant-centos64 sbin]$ ip -oneline -family inet addr show eth1 | awk '{print $4}' | cut -d'/' -f1
192.168.1.111
[vagrant@vagrant-centos64 sbin]$

当我们在接口数组中获取设备名称并在地址数组中获取 IP 地址时,我们创建一个关联数组,其中接口名称作为键,IP 地址作为值。

return array_combine($interfaces, $addreses);

使用 ifconfig 的服务器

在 ifconfig 的情况下,
ip
shell_exec 将返回
false。在这种情况下,我们改为运行 ifconfig。

我们上面介绍的逻辑完全相同 - 它只是获取信息的不同实用程序。

    if (!$output) { // It didn't work with "ip" , so we do it with ifconfig
      $output = `ifconfig | grep "Link encap" | awk '{ print $1 }'`;
      $interfaces = explode("\n", $output);
      $output = `ifconfig | grep "inet addr" | awk '{ print $2 }' | sed 's/addr://'`;
      $addreses = explode("\n", $output);
      $output = trim($output, " \n");
      return array_combine($interfaces, $addreses);
    }

让我们在终端中运行上面的命令,以便我们了解发生了什么。

请注意,
ifconfig 直接在输出中显示 IP 地址,因此我们可以直接获取它们,而不是在设备数组上运行循环并获取每个设备的 IP 地址。

[vagrant@vagrant-centos64 sbin]$ ifconfig
eth0      Link encap:Ethernet  HWaddr 08:00:27:08:C2:E4
          inet addr:10.0.2.15  Bcast:10.0.2.255  Mask:255.255.255.0
          inet6 addr: fe80::a00:27ff:fe08:c2e4/64 Scope:Link
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:4230 errors:0 dropped:0 overruns:0 frame:0
          TX packets:2575 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:1000
          RX bytes:444488 (434.0 KiB)  TX bytes:2288676 (2.1 MiB)

eth1      Link encap:Ethernet  HWaddr 08:00:27:EB:11:E4
          inet addr:192.168.1.111  Bcast:192.168.1.255  Mask:255.255.255.0
          inet6 addr: fe80::a00:27ff:feeb:11e4/64 Scope:Link
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:4470 errors:0 dropped:0 overruns:0 frame:0
          TX packets:2449 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:1000
          RX bytes:1689803 (1.6 MiB)  TX bytes:271675 (265.3 KiB)

lo        Link encap:Local Loopback
          inet addr:127.0.0.1  Mask:255.0.0.0
          inet6 addr: ::1/128 Scope:Host
          UP LOOPBACK RUNNING  MTU:16436  Metric:1
          RX packets:264 errors:0 dropped:0 overruns:0 frame:0
          TX packets:264 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:0
          RX bytes:15840 (15.4 KiB)  TX bytes:15840 (15.4 KiB)

[vagrant@vagrant-centos64 sbin]$ ifconfig | grep "Link encap"
eth0      Link encap:Ethernet  HWaddr 08:00:27:08:C2:E4
eth1      Link encap:Ethernet  HWaddr 08:00:27:EB:11:E4
lo        Link encap:Local Loopback
[vagrant@vagrant-centos64 sbin]$ ifconfig | grep "Link encap" | awk '{ print $1 }'
eth0
eth1
lo

[vagrant@vagrant-centos64 sbin]$ ifconfig | grep "inet addr"
          inet addr:10.0.2.15  Bcast:10.0.2.255  Mask:255.255.255.0
          inet addr:192.168.1.111  Bcast:192.168.1.255  Mask:255.255.255.0
          inet addr:127.0.0.1  Mask:255.0.0.0
[vagrant@vagrant-centos64 sbin]$ ifconfig | grep "inet addr" | awk '{ print $2 }'
addr:10.0.2.15
addr:192.168.1.111
addr:127.0.0.1
[vagrant@vagrant-centos64 sbin]$ ifconfig | grep "inet addr" | awk '{ print $2 }' | sed 's/addr://'
10.0.2.15
192.168.1.111
127.0.0.1
[vagrant@vagrant-centos64 sbin]$

一旦我们有了数据,将它们放入
get_content 中就很容易了,因为我们在这里只显示了一个简单的表格。

  public function get_content() {
    $interfaces = $this->get_metric();
    $html = '<table class="wp-list-table widefat"><thead><tr>
      <th>Interface</th>
      <th>IP</th>
      </tr></thead><tbody>';
    foreach ( $interfaces as $interface => $ip ) {
      $html .= "<tr>
        <td>{$interface}</td>
        <td>{$ip}</td>
        </tr>";
    }
    $html .= '</tbody></table>';
    echo $html;
  }

以下是它向管理员显示的方式:

网络流量

网络流量或网络 IO 显示通过计算机网络传输包的状态。该信息是从 netstat 中提取的。

[vagrant@vagrant-centos64 sbin]$ netstat -i
Kernel Interface table
Iface       MTU Met    RX-OK RX-ERR RX-DRP RX-OVR    TX-OK TX-ERR TX-DRP TX-OVR Flg
eth0       1500   0     4828      0      0      0     2933      0      0      0 BMRU
eth1       1500   0     4806      0      0      0     2679      0      0      0 BMRU
lo        16436   0      276      0      0      0      276      0      0      0 LRU

让我们在文件
widget/networkio.php 中获取我们的基本类
Networkio

<?php
/**
 * Adopt from https://github.com/afaqurk/linux-dash/blob/master/sh/ip.php
 *
 */
namespace AX\StatBoard\Widget;

class Networkio implements Provider {
  function __construct() {
  }

  public function get_title() {
    return "Network IO";
  }
  function get_metric() {    $ethernet = array();       $output = `netstat -i | grep -v -E '(Iface|Interface)' | awk '{print $1","$4","$8}'`;    $lines = explode("\n", $output);    foreach ($lines as $line) {      $line = explode(',', $line);      if (count($line)<3) {        continue;      }      $ethernet[] = array($line[0], intval($line[1]), intval($line[2]));    }    return $ethernet;  }}

我将在本文后面解释它们。现在,让我们尝试评估我们在上面代码中使用的命令。

我将尝试运行多个命令,以便在我们将结果传递到另一个管道后您可以理解其中的差异。

[vagrant@vagrant-centos64 sbin]$ netstat -i
Kernel Interface table
Iface       MTU Met    RX-OK RX-ERR RX-DRP RX-OVR    TX-OK TX-ERR TX-DRP TX-OVR Flg
eth0       1500   0     5727      0      0      0     3400      0      0      0 BMRU
eth1       1500   0     5004      0      0      0     2797      0      0      0 BMRU
lo        16436   0      292      0      0      0      292      0      0      0 LRU

[vagrant@vagrant-centos64 sbin]$ netstat -i | grep -v -E '(Iface|Interface)'
eth0       1500   0     5736      0      0      0     3405      0      0      0 BMRU
eth1       1500   0     5004      0      0      0     2797      0      0      0 BMRU
lo        16436   0      292      0      0      0      292      0      0      0 LRU

[vagrant@vagrant-centos64 sbin]$ netstat -i | grep -v -E '(Iface|Interface)' | awk '{print $1","$4","$8}'
eth0,5760,3420
eth1,5004,2797
lo,292,292
[vagrant@vagrant-centos64 sbin]$

netstat 返回很多东西,我们使用 grep 来排除包含单词
Iface
Kernel 的行(前两行),我们只关心最后三行是我们的以太网设备及其包传输。 awk 用于打印出第一、四、八列的数据,分别表示接口名称、RX-OK、TX-OK。

在我们的get_metric中,我们将结果逐行拆分到一个数组中。因为每一行都包含用逗号分隔的数据,所以它们被再次分割成一个数组。

我们确保只接受具有三个元素的数组。我们还尝试将数字字符串转换为整数值。数据将依次输入
get_content

  public function get_content() {
    $interfaces = $this->get_metric();
    $data = array_merge(array(array('Interface', 'Receive(package)', 'Transfer(package)')), $interfaces);
    $data = json_encode($data); 
    echo <<<EOD
      <div id="nio_chart"></div>
      <script type="text/javascript">
      google.setOnLoadCallback(function () {
        var data = google.visualization.arrayToDataTable({$data});

        var options = {
        };

        var chart = new google.visualization.ColumnChart(document.getElementById('nio_chart'));
        chart.draw(data, options);
      })        
    </script>
EOD;

  }

我们之前使用了条形图,并尝试将指标数据数组格式化为条形图数据格式,然后进行渲染。

数据数组的每一行代表一个组条形图,其中包含条形图名称及其相应的值。在我们的例子中,每一行都是接口名称以及 RX 栏和 TX 栏。

这是我们得到的:

输入/输出统计

现在,我们处理 io stat。 IO 表示输入/输出。我们将了解每秒执行多少次读/写操作。我们还处理 io_wait。 IO等待是CPU空闲等待从硬盘读取结果的时间。

比如你正在读取MySQL数据,CPU会空闲下来等待结果。 io wait 按1秒或1000毫秒计算。如果您的代码需要 100 毫秒从硬盘读取数据,则 io_wait 为 100/1000 = 10%。 IO 等待越少,系统性能越好。

为了继续执行此操作,请确保系统上有 sysstat 软件包。

  1. 对于 Arch Linux,使用 pacman -S sysstat 安装 </p> <p>
  2. </p> <li>对于 Debian/Ubuntu,您可以使用 <code>apt-get install sysstat 获取它们

  3. 对于 Fedora/Centos,您可以使用 yum install sysstat
  4. 对于其他发行版,:请使用您的发行版包管理器进行安装

</p> <p>安装完成后,让我们评估一下我们将使用的一些命令。首先是第一件事:</p> </p> <pre>[vagrant@vagrant-centos64 sbin]$ iostat Linux 2.6.32-358.23.2.el6.x86_64 (vagrant-centos64.vagrantup.com) 04/27/2014 _x86_64_ (1 CPU) avg-cpu: %user %nice %system %iowait %steal %idle 0.05 0.00 0.25 0.04 0.00 99.66 Device: tps Blk_read/s Blk_wrtn/s Blk_read Blk_wrtn sda 0.18 7.62 1.04 157826 21584 </pre> <p> 第四行包含IO状态数据。我们对第四列中的 <code>iowait 值感兴趣。从第七行开始的数据包含硬盘驱动器每秒的读/写块。
</p> </p> <p> 如果您有许多硬盘连接到服务器,您将拥有多个设备:sdb、sdc 等。 </p> </p> <p> 数据是块的数量,而不是实际大小(以兆字节为单位)。我们必须找到块大小来计算总大小。 </p> </p> <p> 块大小存储在/sys/block/sda/queue/physical_block_size <b>中。 </b> </p> </p> <pre>[vagrant@vagrant-centos64 ~]$ cat /sys/block/sda/queue/physical_block_size 512 [vagrant@vagrant-centos64 ~]$ </pre> <p> 所以我的sda的块大小是512。我们将每秒读取的块数与块大小相乘,得到读/写数据的实际大小。 </p> </p> <p>有了上面的基本知识,让我们在 <code>widget/iostat.php 中创建我们的类 Iostat

<?php

namespace AX\StatBoard\Widget;

class Iostat implements Provider {
  function __construct() {
  }

  public function get_title() {
    return "Disk IO";
  }


  /**
   * Make sure we install package sysstat
   * yum install sysstat
   * or apt-get install sysstat
   *
   * Return IO Stat information. CPU waiting time, disk read/write
   *
   */
  function get_metric() {
    $metric = array();

    $output = `iostat`;
    $number_of_core = intval(`/bin/grep -c processor /proc/cpuinfo`);

    $lines = explode("\n", $output);
    //We should have more than  4 lines
    if (!is_array($lines) || sizeof($lines)<4) {
      return false;
    }
    $avg_cpu = preg_split("/\s+/", $lines[3]);
    $metric['cpu'] = array(
      'user'    => floatval($avg_cpu[0]) * $number_of_core,
      'system'  => floatval($avg_cpu[2]) * $number_of_core,
      'io_wait' => floatval($avg_cpu[3]) * $number_of_core,
      'other'   => 100 - ($avg_cpu[0] + $avg_cpu[2] + $avg_cpu[3])
    );
    
    if (sizeof($lines) >=7) {
      for ($i=6,$l = sizeof($lines);$i<$l; $i++) {
        $line = preg_split("/\s+/", $lines[$i]);
        if (!is_array($line) || sizeof($line)<5) {
          continue;
        }
        // Calculate block size
        $block_size = shell_exec("cat /sys/block/{$lines[1]}/queue/physical_block_size");

        $metric['disk'][$line[0]] = array(
          'read'  => floatval($line[2]) * $block_size / 1024,
          'write' => floatval($line[3]) * $block_size / 1024,
        );

      }  
    }

    return $metric;
  }

}

我们只是尝试将我们的理论应用到 PHP 代码中。获取 iostat 的输出,将其转换为数组,每一行都是一个元素。

第四行被空格分割并映射到关联数组中。第七行之后的所有行都放入另一个关联数组中,其中键是设备名称(sda,sdb,sdc,..),如果以兆字节为单位读取和写入,则值是一个数组。

一旦我们获得了指标,将其输入 get_content 并渲染我们的图表。

  public function get_content() {
    $metric = $this->get_metric();
    $disk_io = array(
      array('Disk', 'Read(MB)', 'Write(MB)'),
    );
    foreach ($metric['disk'] as $disk=>$stat) {
      $disk_io[] = array($disk, $stat['read'], $stat['write']);
    }
    $disk_io = json_encode($disk_io);   
  
    $cpu_io = json_encode(array(
      array('CPU Time', 'Percent'),
      array('IO Wait', $metric['cpu']['io_wait']),
    ));
    
    echo <<<EOD
      <div id="widget_disk_io"></div>
      <div id="widget_cpu_io_wait"></div>
      <script type="text/javascript">
      google.load('visualization', '1', {packages:['gauge']});
      google.setOnLoadCallback(function () {
        var data = google.visualization.arrayToDataTable({$cpu_io});
        var goptions = {
          redFrom: 80, redTo: 100,
          yellowFrom:50, yellowTo: 80,
          minorTicks: 5
        };
        var chart = new google.visualization.Gauge(document.getElementById('widget_cpu_io_wait'));
        chart.draw(data, goptions);

        var data2 = google.visualization.arrayToDataTable({$disk_io});
        var chart2 = new google.visualization.ColumnChart(document.getElementById('widget_disk_io'));
        chart2.draw(data2, {});
      })        
    </script>
EOD;

  }

对于磁盘 IO 读写,我们使用了条形图。对于 IO 等待,我们使用仪表图来使其看起来更漂亮。我们认为 80-100 的 IOwait 是一个严重事件,并用红色突出显示。



智能AI问答 智能助手能迅速回答你的编程问题,提供实时的代码和解决方案,帮助你解决各种难题。不仅如此,它还能提供编程资源和学习指导,帮助你快速提升编程技能。无论你是初学者还是专业人士,AI智能助手都能成为你的可靠助手,助力你在编程领域取得更大的成就。 我要提问 <span>相关标签:</span> php JavaScript ruby mysql 架构 json html print 关联数组 sort foreach 命名空间 format cURL 字符串 循环 接口 堆 线程 对象 事件 linux ubuntu centos debian WordPress 来源: <span>收藏</span> <span>点赞</span> <span>上一篇:WooCommerce 初学者:掌握订单创建第 3 部分</span> <span>下一篇:Envato WordPress 工具包:精明的 WordPress 开发人员必备的工具箱</span> 本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn 作者最新文章 </p> <ul> <li> <span></span> 如何正确设置技嘉B85主板的BIOS启动顺序? 2024-01-28 21:06:22 </li> <li> <span></span> 遇到主板针脚弯曲的情况应该怎么应对? 2024-01-28 16:39:11 </li> <li> <span></span> 如何在技嘉B85主板上安装操作系统? 2024-01-28 16:36:10 </li> <li> <span></span> 铭瑄主板与华硕主板哪个更优? 2024-01-28 16:33:11 </li> <li> <span></span> 玩转雾锁王国的秘籍给新手指引 2024-01-28 15:33:26 </li> <li> <span></span> 华硕B550开机显示CPU风扇故障提示? 2024-01-28 13:36:20 </li> <li> <span></span> VITA GM 系列 ATX 3.1 金牌全模组电源,1000W 版本全汉上架,售价999 元 2024-01-28 13:33:27 </li> <li> <span></span> 闻泰科技有望再次成为三星手机最大的ODM供应商,接单突破4000万 2024-01-28 13:03:18 </li> <li> <span></span> 阴阳师流光追月神御魂怎么搭配 2024-01-28 12:39:23 </li> <li> <span></span> 冬季幻想作弊码完全指南 2024-01-28 12:39:10 </li> </ul> <p> 最新问题 李老师,第八节的JSON 原始数据 头这个是怎么弄出来的 李老师,第八节的JSON 原始数据 头这个是怎么弄出来的 <span>茶壶来自于2024-01-23 22:03:34</span> 0 0 275 Symfony 6 / Doctrine - 驱动程序发生异常:找不到驱动程序 我尝试设置我的symfony项目,一切都很好,因为我尝试创建学说数据库。我在.env文件中写入了DATABASE_URL,这是正确的,我想是这样,但是无论是我的本地xampp服务器... <span>P粉729518806来自于2024-01-21 16:23:09</span> 0 1 439 发现 XLSX 软件包漏洞,但没有更新的软件包 我在js代码中使用xls x。从去年开始就运转良好。今天我开始遇到问题并且构建失败。由于:$yarnaudit:yarnaudit v1.22.19┌───────────────... <span>P粉221046425来自于2024-01-21 16:14:05</span> 0 1 379 Vue 3中的组件导入 嗨,我正在尝试在Vue3中导入一个组件。这是我的组件结构:+src+App.vue+components+Navbar.vue在我的App.vue中我尝试过:<scripts... <span>P粉564192131来自于2024-01-21 16:26:14</span> 0 2 358 如何在Vue3中访问js文件中的Vue实例? 在Vue2中,我能够访问我的Vue实例以使用在Vue中注册的组件。测试.jsimportVuefrom'vue'exportfunctionrenderLogin(){Vue.to... <span>P粉609866533来自于2024-01-21 16:04:50</span> 0 2 510 Cookie权限导致iframe错误 我目前在我的网页中使用一个简单的iframe,使用bootstrap来适应小屏幕(=yout ubeShort)和大屏幕(=普通横向视频):<divclass="c... <span>P粉447002127来自于2024-01-21 16:00:06</span> 0 1 384 向 Vite 库构建添加类型:分步指南 我按照vite文档使用库模式,并且能够生成一个工作组件库。我使用vue-ts预设创建了项目,并在我的组件中定义了道具及其类型,并使用了一些接口。但是当我构建库时,没有包含任何类型。... <span>P粉426780515来自于2024-01-21 15:57:45</span> 0 2 390 为什么这段代码不能简单地打印出字母A到Z? <?phpfor($i='a';$i<='z';$i++)echo"$i\n";此代码段提供以下输出(换行符被空格替换):abcdefghijklm... <span>P粉239089443来自于2024-01-21 15:39:33</span> 0 2 309 Node.js 连接到 MySQL Docker 容器 ECONNREFUSED 在将此问题标记为重复之前,请注意我确实阅读了其他答案,但它并没有解决我的问题。我有一个包含两个服务的Dockercompose文件:version:"3"ser... <span>P粉296080076来自于2024-01-21 15:41:18</span> 0 1 357 将 CSV 文件导入 Firebase:分步指南 我发现我们可以将json文件导入到firebase中。我想知道是否有办法导入CSV文件(我的文件可能包含大约50K甚至更多记录,大约10列)。在firebase中拥有这样的文件是否... <span>P粉891237912来自于2024-01-21 15:21:20</span> 0 1 407 相关专题 更多> </p> <ul> <li> <span>php文件怎么打开</span> </li> <li> <span>php怎么取出数组的前几个元素</span> </li> <li> <span>php反序列化失败怎么办</span> </li> <li> <span>php怎么连接mssql数据库</span> </li> <li> <span>php连接mssql数据库的方法</span> </li> <li> <span>html怎么上传</span> </li> <li> <span>PHP出现乱码怎么解决</span> </li> <li> <span>php怎么在浏览器运行</span> </li> </ul> <p>

声明:本文为原创文章,版权归所有,欢迎分享本文,转载请保留出处!

2024-01-29

2024-01-29

古风汉服美女图集
扫一扫二维码分享