zoukankan      html  css  js  c++  java
  • snmp+mrtg实现对局域网内的linuxServer的监控

    已按此文成功监控多台服务器

    原文:http://bbs.chinaunix.net/viewthread.php?tid=243757

     

    上回看了platinum的"mrtg能做些什么"贴子,很受启发,于是也试着做了一个。但当时对snmpd不是很熟,所以没有做成功,后来仔细看了一下snmpd的有关文章和RH中的/etc/snmp/snmpd.conf文件,发现用snmp+mrtg可以很好的实现对局域网内服务器状态的监控。

        现在就以用snmp+mrtg监控一台局域网内的redhat机器(IP:192.168.13.103)的网卡、内存、CPU、DISKIO为例子,谈一下如何实现。基本的法办就是用一台redhat监控机器(IP:192.168.13.105),通过snmpwalk命令去抓目标服务器的状态数据,然后用mrtg画出图来。

        1、首先我们要把目标snmpd.conf文件的配好。这是用snmpwalk命令一抓取数据的关健。下面是目标机器(IP:192.168.13.103)上的/etc/snmp/snmpd.conf文件部份内容,红色的部份是我对snmpd.conf所做的改动。

       [root@wy1 root]# cat /etc/snmp/snmpd.conf

    ####

    # First, map the community name "public" into a "security name"

    #       sec.name  source          community

    com2sec notConfigUser  default       public  #定义community名称为 public,映射到安全名 notConfigUser。

    ####

    # Second, map the security name into a group name:

    #       groupName      securityModel securityName

    group   notConfigGroup v1           notConfigUser   #定义安全用户名notConfigUser映射到notConfigGroup组。

    group   notConfigGroup v2c           notConfigUser

    ####

    # Third, create a view for us to let the group have rights to: #定义一个view,来决定notConfigUser可以操作的范围。

    # Make at least  snmpwalk -v 1 localhost -c public system fast again. #定义可查看的snmp的范围。

    #       name           incl/excl     subtree         mask(optional)

    view    systemview    included   .1.3.6.1.2.1.1

    view    systemview    included   .1.3.6.1.2.1.25.1.1

    view all   included  .1

    ####

    # Finally, grant the group read-only access to the systemview view. #给notConfigGroup组所定义view名 all 以只读权限。

    #       group          context sec.model sec.level prefix read   write  notif

    access  notConfigGroup ""      any       noauth    exact  all  none none

    #access  notConfigGroup ""      any       noauth    exact  mib2 none none

    # -----------------------------------------------------------------------------

    # Here is a commented out example configuration that allows less

    # restrictive access.

    # YOU SHOULD CHANGE THE "COMMUNITY" TOKEN BELOW TO A NEW KEYWORD ONLY

    # KNOWN AT YOUR SITE.  YOU *MUST* CHANGE THE NETWORK TOKEN BELOW TO

    # SOMETHING REFLECTING YOUR LOCAL NETWORK ADDRESS SPACE.

    ##       sec.name  source          community

    #com2sec local     localhost       COMMUNITY

    #com2sec mynetwork NETWORK/24      COMMUNITY

    ##     group.name sec.model  sec.name

    #group MyRWGroup  any        local

    #group MyROGroup  any        mynetwork

    #

    #group MyRWGroup  any        otherv3user

    #...

    ##           incl/excl subtree                          mask

    #view all    included  .1                               80

    ## -or just the mib2 tree-

    #view mib2   included  .iso.org.dod.internet.mgmt.mib-2 fc

    #view mib2   included  .iso.org.dod.internet.mgmt.mib-2 fc

    ##                context sec.model sec.level prefix read   write  notif

    #access MyROGroup ""      any       noauth    0      all    none   none

    #access MyRWGroup ""      any       noauth    0      all    all    all

     

    其实配制一个snmpd.conf文件不算太难,

    (1)首选是定义一个共同体名(community),这里是public,及可以访问这个public的用户名(sec name),这里是notConfigUser。Public相当于用户notConfigUser的密码:)

    #       sec.name  source          community

    com2sec notConfigUser  default       public

    (2)定义一个组名(groupName)这里是notConfigGroup,及组的安全级别,把notConfigGroup这个用户加到这个组中。

                groupName      securityModel securityName

    group   notConfigGroup   v1           notConfigUser  

    group   notConfigGroup   v2c           notConfigUser

    (3)定义一个可操作的范围(view)名, 这里是all,范围是 .1

        #       name           incl/excl     subtree         mask(optional)

           view  all             included     .1

      (4)定义notConfigUser这个组在all这个view范围内可做的操作,这时定义了notConfigUser组的成员可对.1这个范围做只读操作。

         #       group          context sec.model sec.level prefix read   write  notif

    access  notConfigGroup ""      any       noauth    exact  all  none none

    ok,这样我们的snmpd.conf文件就基本配成了,用service  snmpd  restart重启snmpd服务。现在我们做一个测试,在监控机上打下面的命令:

      [root@wy2 root]# snmpwalk -v 1 192.168.13.103 -c public system

    SNMPv2-MIB::sysDescr.0 = STRING: Linux wy1 2.4.20-8smp #1 SMP Thu Mar 13 17:45:54 EST 2003 i686

    SNMPv2-MIB::sysObjectID.0 = OID: NET-SNMP-MIB::netSnmpAgentOIDs.10

    SNMPv2-MIB::sysUpTime.0 = Timeticks: (7565377) 21:00:53.77

    SNMPv2-MIB::sysContact.0 = STRING: Root <root@wuying.com>; (configure /etc/snmp/snmp.local.conf)

    SNMPv2-MIB::sysName.0 = STRING: wy1

    SNMPv2-MIB::sysLocation.0 = STRING: wy1.wuying.com (edit /etc/snmp/snmpd.conf)

    SNMPv2-MIB::sysORLastChange.0 = Timeticks: (10) 0:00:00.10

    ``````````````````````

    “Linux wy1 2.4.20-8smp”操作系统的信息已经出来了:)

    现在我们在目标机上来写一些脚本来显标MEM、CPU、DiskIO

    MEM数据的抓取脚本:

    [root@wy1 root]# cat  mfree.sh   

    #!/bin/sh

    /usr/bin/free -m | grep Mem |awk '{print $4}'

    /usr/bin/free -m | grep Mem |awk '{print $2}'

    [root@wy1 root]# sh  mfree.sh   (上面一个数据是内存使用量,下面的是内存总量,M)

    442

    1006

    CPU数据的抓取脚本

    [root@wy1 root]# cat  cpustat.sh

    #!/bin/sh

    idle=`sar  -u 1 3 | grep Average | awk '{print $6}'`

    used=`echo "101 - $idle" | bc -l -s`

    echo $used

    echo $idle

    DiskIO数据的抓取脚本

    [root@wy1 root]# cat  iostat.sh    (显示硬盘IO,k/s)

    #!/bin/sh

    used1=`sar -d 1 3 | tail -1 | awk '{print $4}'`

    used2=`echo "$used1 / 2" | bc -l`

    echo $used1

    echo $used2

    好现在我们已经能得到这数据了,怎么才能让监控主机通过snmpd得到这些数据呢?可以在目标主机的/etc/snmp/snmpd.conf文件下面加个这些行:

    exec .1.3.6.1.4.1.2021.53 mfree /bin/sh /root/mfree.sh

    exec .1.3.6.1.4.1.2021.54 cpustat /bin/sh /root/cpustat.sh

    exec .1.3.6.1.4.1.2021.55 iostat /bin/sh /root/iostat.sh

    service  snmpd  restart  重启目标主机上的snmpd服务。

    这样在监控主机上运行:

       [root@wy2 root]# snmpwalk -v 1  192.168.13.103 -c public .1.3.6.1.4.1.2021.53

    UCD-SNMP-MIB::ucdavis.53.1.1 = INTEGER: 1

    UCD-SNMP-MIB::ucdavis.53.2.1 = STRING: "mfree"

    UCD-SNMP-MIB::ucdavis.53.3.1 = STRING: "/bin/sh /root/mfree.sh"

    UCD-SNMP-MIB::ucdavis.53.100.1 = INTEGER: 0

    UCD-SNMP-MIB::ucdavis.53.101.1 = STRING: "442"

    UCD-SNMP-MIB::ucdavis.53.101.2 = STRING: "1006"

    UCD-SNMP-MIB::ucdavis.53.102.1 = INTEGER: 0

    其中UCD-SNMP-MIB::ucdavis.53.101.1 = STRING: "442" 中的442就是mfree.sh输出的第一个数据,1006是mfree.sh输出的第二个数据。OK,再做一下加工:

    [root@wy2 root]# snmpwalk -v 1  192.168.13.103 -c public .1.3.6.1.4.1.2021.53 | grep 53.101 | awk -F\" '{print $2}'

    443

    1006

    好,我们已经通过snmpd从监控主机上得到了目标主机上内存使用的这两个数据了:),其它的两个脚本也一样:

      CPU使用、空闲。

    [root@wy2 root]# snmpwalk -v 1  192.168.13.103 -c public .1.3.6.1.4.1.2021.54 | grep 54.101 | awk -F\" '{print $2}'

    1.17

    99.83

       DISK IO 状态:

    [root@wy2 root]# snmpwalk -v 1  192.168.13.103 -c public .1.3.6.1.4.1.2021.55 | grep 55.101 | awk -F\" '{print $2}'

    43.00000000000000000000

    43.00000000000000000000

    最后是编辑监控主机上的mrtg.cfg文件,在Target[xxxx]中加入上面的命令,下面是我的mrtg.cfg文件,前面的两个Target是我用/usr/bin/cfgmaker --global 'WorkDir: /home/httpd/mrtg/net' --global 'Options[_]:growright,bits' --ifref=ip public@192.168.13.103  命令生成的,后面的是我跟据platinum 的"mrtg能做些什么"的贴子内的mrtg.cfg文件改的。(在精华里,大家可以看一下)。

    [root@wy1 root]# cat /home/httpd/mrtg/103/mrtg.cfg

    # Created by

    # /usr/bin/cfgmaker --global 'WorkDir: /home/httpd/mrtg/net' --global 'Options[_]:growright,bits' --ifref=ip public@192.168.13.103

    ### Global Config Options

    #  for UNIX

    # WorkDir: /home/http/mrtg

    #  or for NT

    # WorkDir: c:\mrtgdata

    ### Global Defaults

    #  to get bits instead of bytes and graphs growing to the right

    # Options[_]: growright, bits

    WorkDir: /home/httpd/mrtg/103

    Options[_]:growright,bits

    Language: chinese

    ######################################################################

    # System: wy1

    # Description: Linux wy1 2.4.20-8smp #1 SMP Thu Mar 13 17:45:54 EST 2003 i686

    # Contact: Root <root@wuying.com>; (configure /etc/snmp/snmp.local.conf)

    # Location: wy1.wuying.com (edit /etc/snmp/snmpd.conf)

    ######################################################################

    ### Interface 2 >;>; Descr: 'eth0' | Name: '' | Ip: '192.168.13.103' | Eth: '00-06-5b-19-9d-ea' ###

    Target[192.168.13.103_192.168.13.103]: /192.168.13.103:public@192.168.13.103:

    SetEnv[192.168.13.103_192.168.13.103]: MRTG_INT_IP="192.168.13.103" MRTG_INT_DESCR="eth0"

    MaxBytes[192.168.13.103_192.168.13.103]: 1250000

    Xsize[192.168.13.103_192.168.13.103]: 300

    Ysize[192.168.13.103_192.168.13.103]: 100

    #kmg[192.168.13.103_192.168.13.103]: K/s,M/s

    #kilo[192.168.13.103_192.168.13.103]: 1024

    Title[192.168.13.103_192.168.13.103]: Traffic for eth0 192.168.13.103 -- wy1

    PageTop[192.168.13.103_192.168.13.103]: <H1>;Traffic for eth0 192.168.13.103 -- wy1</H1>;

    #Options[192.168.13.103_192.168.13.103]: growright,gauge,nopercent

    ### Interface 3 >;>; Descr: 'eth1' | Name: '' | Ip: '172.16.0.188' | Eth: '00-06-5b-19-9d-e9' ###

    Target[192.168.13.103_172.16.0.188]: /172.16.0.188:public@192.168.13.103:

    SetEnv[192.168.13.103_172.16.0.188]: MRTG_INT_IP="172.16.0.188" MRTG_INT_DESCR="eth1"

    Xsize[192.168.13.103_172.16.0.188]: 300

    Ysize[192.168.13.103_172.16.0.188]: 100

    MaxBytes[192.168.13.103_172.16.0.188]: 1250000

    #ShortLegend[192.168.13.103_172.16.0.188]:

    #kmg[192.168.13.103_172.16.0.188]: b/s,Kb/s

    #kilo[192.168.13.103_172.16.0.188]: 1024

    Title[192.168.13.103_172.16.0.188]: Traffic for eth1 172.16.0.188 -- wy1

    PageTop[192.168.13.103_172.16.0.188]: <H1>;Traffic for eth1 172.16.0.188 -- wy1</H1>;

    #Options[192.168.13.103_172.16.0.188]: growright,gauge,nopercent

    ###MEM status

    Target[wy1_mem]:`snmpwalk -v 1  192.168.13.103 -c public .1.3.6.1.4.1.2021.53 | grep 53.101 | awk -F\" '{print $2}'`

    #Targey[wy1_mem]: memTotalReal.0&memAvailReal.0:holdata@holdata.3322.org

    Xsize[wy1_mem]: 300

    Ysize[wy1_mem]: 100

    Ytics[wy1_mem]: 7

    MaxBytes[wy1_mem]: 1006

    Title[wy1_mem]:Memory State of WY1 IP 192.168.13.103 Server

    PageTop[wy1_mem]:<H1>;Memory State of WY1 IP 192.168.13.103 Server</H1>;

    ShortLegend[wy1_mem]: MB

    kmg[wy1_mem]: MB

    kilo[wy1_mem]:1024

    YLegend[wy1_mem]: Memory Usage

    Legend1[wy1_mem]: 可用内存

    Legend2[wy1_mem]: 总内存量

    Legend3[wy1_mem]: 可用内存

    Legend4[wy1_mem]: 总内存量

    LegendI[wy1_mem]: 可用内存

    LegendO[wy1_mem]: 总内存量

    Options[wy1_mem]: growright,gauge,nopercent

    ###cpu status

    Target[wy1_CPU]:`snmpwalk -v 1  192.168.13.103 -c public .1.3.6.1.4.1.2021.54 | grep 54.101 | awk -F\" '{print $2}'`

    #Targey[wy1_CPU]: memTotalReal.0&memAvailReal.0:holdata@holdata.3322.org

    Xsize[wy1_CPU]: 300

    Ysize[wy1_CPU]: 100

    Ytics[wy1_CPU]: 7

    MaxBytes[wy1_CPU]: 100

    Title[wy1_CPU]:CPU State of WY1 IP 192.168.13.103 Server

    PageTop[wy1_CPU]:<H1>;CPU State of WY1 IP 192.168.13.103 Server</H1>;

    ShortLegend[wy1_CPU]:

    kmg[wy1_CPU]: %

    #kilo[wy1_CPU]:1024

    YLegend[wy1_CPU]: CPU Usage

    Legend1[wy1_CPU]: 已用CPU:

    Legend2[wy1_CPU]: 可用CPU:  

    LegendI[wy1_CPU]: 已用CPU:

    LegendO[wy1_CPU]: 可用CPU:

    Options[wy1_CPU]: growright,gauge,nopercent

    ###iostat

    ###cpu status

    Target[wy1_IO]:`snmpwalk -v 1  192.168.13.103 -c public .1.3.6.1.4.1.2021.55 | grep 55.101 | awk -F\" '{print $2}'`

    #Targey[wy1_IO]: memTotalReal.0&memAvailReal.0:holdata@holdata.3322.org

    Xsize[wy1_IO]: 300

    Ysize[wy1_IO]: 100

    Ytics[wy1_IO]: 7

    MaxBytes[wy1_IO]: 10000

    Title[wy1_IO]: DISK IO State of WY1 IP 192.168.13.103 Server

    PageTop[wy1_IO]:<H1>;DISK IO State of WY1 IP 192.168.13.103 Server</H1>;

    ShortLegend[wy1_IO]:

    kmg[wy1_IO]: K/s,M/s

    kilo[wy1_IO]:1024

    YLegend[wy1_IO]: DISK IO SPEED

    Legend1[wy1_IO]: IO速度:

    Legend2[wy1_IO]: IO速度:

    LegendI[wy1_IO]: IO速度:

    LegendO[wy1_IO]: IO速度:

    Options[wy1_IO]: growright,gauge,nopercent

    用indexmaker -o  /home/httpd/mrtg/103/index.html  /home/httpd/mrtg/103/mrtg.cfg  生成网页,在crontab 中加入*/5 * * * * mrtg /home/httpd/mrtg/103/mrtg.cfg  

    OK,这样我们就能通过mrtg生成的图来监控我们linux的机器的状态了。在IE中打入http://192.168.13.105/mrtg/103,mrtg的监控网页就出来啦(/home/httpd/是我appache的主目录)。当然我们也能用snmpwalk命令来得到安装有snmp服务的win2000机器的状态的数据:

    如:

    [root@wy1 103]# snmpwalk -v 1 192.168.1.5 -c public HOST-RESOURCES-MIB::hrStorage   

    HOST-RESOURCES-MIB::hrMemorySize.0 = INTEGER: 3800424 KBytes

    HOST-RESOURCES-MIB::hrStorageIndex.1 = INTEGER: 1

    HOST-RESOURCES-MIB::hrStorageIndex.2 = INTEGER: 2

    HOST-RESOURCES-MIB::hrStorageIndex.3 = INTEGER: 3

    HOST-RESOURCES-MIB::hrStorageIndex.4 = INTEGER: 4

    HOST-RESOURCES-MIB::hrStorageIndex.5 = INTEGER: 5

    HOST-RESOURCES-MIB::hrStorageIndex.6 = INTEGER: 6

    HOST-RESOURCES-MIB::hrStorageType.1 = OID: HOST-RESOURCES-TYPES::hrStorageRemovableDisk

    HOST-RESOURCES-MIB::hrStorageType.2 = OID: HOST-RESOURCES-TYPES::hrStorageFixedDisk

    HOST-RESOURCES-MIB::hrStorageType.3 = OID: HOST-RESOURCES-TYPES::hrStorageFixedDisk

    HOST-RESOURCES-MIB::hrStorageType.4 = OID: HOST-RESOURCES-TYPES::hrStorageFixedDisk

    HOST-RESOURCES-MIB::hrStorageType.5 = OID: HOST-RESOURCES-TYPES::hrStorageCompactDisc

    HOST-RESOURCES-MIB::hrStorageType.6 = OID: HOST-RESOURCES-TYPES::hrStorageVirtualMemory

    HOST-RESOURCES-MIB::hrStorageDescr.1 = STRING: A:\

    HOST-RESOURCES-MIB::hrStorageDescr.2 = STRING: C:\ Label:  Serial Number 581e89fe

    HOST-RESOURCES-MIB::hrStorageDescr.3 = STRING: D:\ Label:New Volume  Serial Number 1cde6e55

    HOST-RESOURCES-MIB::hrStorageDescr.4 = STRING: E:\ Label:DATA_BAK  Serial Number 30d29147

    HOST-RESOURCES-MIB::hrStorageDescr.5 = STRING: F:\

    HOST-RESOURCES-MIB::hrStorageDescr.6 = STRING: Virtual Memory

    HOST-RESOURCES-MIB::hrStorageAllocationUnits.1 = INTEGER: 0 Bytes

    HOST-RESOURCES-MIB::hrStorageAllocationUnits.2 = INTEGER: 4096 Bytes

    HOST-RESOURCES-MIB::hrStorageAllocationUnits.3 = INTEGER: 4096 Bytes

    HOST-RESOURCES-MIB::hrStorageAllocationUnits.4 = INTEGER: 4096 Bytes

    HOST-RESOURCES-MIB::hrStorageAllocationUnits.5 = INTEGER: 0 Bytes

    HOST-RESOURCES-MIB::hrStorageAllocationUnits.6 = INTEGER: 65536 Bytes

    HOST-RESOURCES-MIB::hrStorageSize.1 = INTEGER: 0

    HOST-RESOURCES-MIB::hrStorageSize.2 = INTEGER: 2050287

    HOST-RESOURCES-MIB::hrStorageSize.3 = INTEGER: 15703529

    HOST-RESOURCES-MIB::hrStorageSize.4 = INTEGER: 53263499

    HOST-RESOURCES-MIB::hrStorageSize.5 = INTEGER: 0

    HOST-RESOURCES-MIB::hrStorageSize.6 = INTEGER: 89444

    HOST-RESOURCES-MIB::hrStorageUsed.1 = INTEGER: 0

    HOST-RESOURCES-MIB::hrStorageUsed.2 = INTEGER: 1233681

    HOST-RESOURCES-MIB::hrStorageUsed.3 = INTEGER: 591593

    HOST-RESOURCES-MIB::hrStorageUsed.4 = INTEGER: 23583930

    HOST-RESOURCES-MIB::hrStorageUsed.5 = INTEGER: 0

    HOST-RESOURCES-MIB::hrStorageUsed.6 = INTEGER: 50611

    HOST-RESOURCES-MIB::hrStorageAllocationFailures.1 = Counter32: 0

    HOST-RESOURCES-MIB::hrStorageAllocationFailures.2 = Counter32: 0

    HOST-RESOURCES-MIB::hrStorageAllocationFailures.3 = Counter32: 0

    HOST-RESOURCES-MIB::hrStorageAllocationFailures.4 = Counter32: 0

    HOST-RESOURCES-MIB::hrStorageAllocationFailures.5 = Counter32: 0

    HOST-RESOURCES-MIB::hrStorageAllocationFailures.6 = Counter32: 0

    上面显示的是192.168.1.5这台win2000SERVER的磁盘信息,可以看到盘符、每个分区的空间总量、使用量等。其它的相应建值还有:

    Diskused:                                 HOST-RESOURCES-MIB::hrStorageUsed

    CPU:                                    HOST-RESOURCES-MIB::hrProcessorLoad

    RUN Proc:                               HOST-RESOURCES-MIB::hrSWRunName

    SYS Uptime:                             HOST-RESOURCES-MIB::hrSystemUptime SNMPv2-MIB::sysUpTime

    SYS Date:                               HOST-RESOURCES-MIB::hrSystemDate

    SYS Device:                             HOST-RESOURCES-MIB::hrDeviceDescr

    SYS Descr:                              SNMPv2-MIB::sysDescr

    SYS Name:                               SNMPv2-MIB::sysName

    netcard speed:                          IF-MIB::ifSpeed

    netcard physcal address:                IF-MIB::ifPhysAddress

    这样我们就可以用一台linux主机通过snmp+mrtg来监控多台局域网内的linux/win2000SERVER了:)。

    MRTG监控网络配置文件解释

    WorkDir    //设置工作目录,存放mrtg采集的数据和生成的统计文件
    Language   //设置WEB页面显示的语言=中文gb2312
    growright  //指示流量图随时间的变化向右增长
    Target     //指示监控的设备;
    设备的表示方式=port:community@router;
    commnunity    //表示snmp通讯密码
    Directory   指示该监控设备的相关文件保存的位置=/var/www/mrtg/eth0/;
    默认保存在WorkDir工作目录下
    MaxBytes    //指示被采集的数据的最大值.如果采集的数据大于该值,MRTG将忽略,并保持上次采集的数据值
    Kmg    //指示乘数单位(默认每千位跳一个单位)
    YLegend        //指示Y轴的单位标记(不能用中文)
    ShortLegend    //指示数据的单位标记
    Legend1,2,3,4分别指示四条不同颜色的统计线的文字说明
    Legend1:     //每5分钟数据平均流入量的文字说明
    Legend2:     //每5分钟数据平均流出量的文字说明
    Legend3:     //每5分钟数据最大流入量的文字说明
    Legend4:     //每5分钟数据最大流出量的文字说明
    LegendI,O    //分别说明数据的流出和流出的文字说明
    WithPeak    //指示如何显示流量图
    共有四条不同颜色的统计线,绿色表示平均流入,蓝色表示平均流出,墨绿色表示最大流入,紫色表示最大流出
    n代表不设置w代表每周图表,代表每月图表,y代表每年图表
    不设置WithPeak,只有绿色流入和蓝色流出
    PageTop    //指示增加到Web页头部的HTML代码,用于定制网页
    以空格开始的<TABLE></TABLE>部分都是PageTop的内容,都直接作为HTML代码

    现在我最关心的是如何让snmp包穿过防火墙,(我用snmpwalk试了一下,抓不到公司在IDC防火墙后的SERVER的snmp数据)。不然,我就可以用snmp去监控公司在IDC防火墙后的SERVER的状态了。听说perl能实现,但不知用什么样的方法来实现?

  • 相关阅读:
    原型链
    原型规则总结
    使用typeof能得到的哪些类型
    eslint 规则中文注释
    两张图片互相切换
    输入框获得焦点与失去焦点、阴影效果
    vue 写table的几种方式
    vue 注意事项
    angular 接口定义封装
    @NgModule
  • 原文地址:https://www.cnblogs.com/d9394/p/10611806.html
Copyright © 2011-2022 走看看