目录

Snap Telemetry

概览

Snap架构的主要特点

运行任务

Task Manifest

头部

The Workflow

远程target

Collect

具体的命名空间

动态度量指标的指定实例

动态查询

Tag

其余的Snap示例

配置文件

Snap应用的简易安装流程

系统要求

前置条件

安装

运行Snap

加载插件

运行任务

简要分析

Processor之filter

processor-tag

Add context

Logs-regexp

插件开发


Snap Telemetry

简介:

Snap是一个开放式遥测框架,旨在通过单一API简化系统数据的收集,处理和发布。该项目的目标是:

  • 使系统能够公开一组一致的遥测数据
  • 简化无处不在的存储系统中的遥测数据抽取
  • 允许灵活处理代理上的遥测数据(例如过滤和装饰)
  • 为小型或大型集群提供强大的遥测工作流集群控制

概览

Snap遥测框架是一个由多个部分组成的项目:

  • · 经过强化,经过广泛测试的守护进程snapteld和CLI,snaptel(在此repo中)
  • · 越来越多的成熟plugins(在插件目录中找到)
  • · 很多tasks收集和发布指标的示例(可在Examples文件夹中找到)

Snap架构的主要特点

  • 插件架构,可根据需求灵活扩展,可以自己研发、或将现有插件集成到snap。如上图所示,snap的按照流程分为三个主要部分:采集、处理、发布。这三个部分都是采用插件的形式存在,collect有collect插件、proccess有process的插件、publish有publish的插件,其中,collcet插件有可以分为两种:一种是普通的采集插件,这种就是按照特定的时间间隔进行采集;另一种可以理解是实时上报插件,采集完之后通过grpc通道实时上报到snap daemon。当我们新建一个plugin的时候,相当于新建了一个grpc server,调用关系如下:startPlugin-->buildGRPCServer-->rpc.RegisterCollectorServer
  • 动态更新,插件架构能够为snap提供非常灵活的插件更新服务,snap daemon可以动态加载和卸载plugin,因此可以在服务不停止的情况下加载更新各种插件。
  • Snap节点批量管理。可以按照特定的规则将多个snap节点组成一个群体,然后对这个群体进行管理(只需要操作一个节点),这个规则可以自己制定,比如可以按照下发的任务,ABC三个节点,A和B创建了cpu信息采集处理任务,C配置的是磁盘信息采集任务,那么AB分为一组,C是另一组,就可以对AB进行批量管理,当有新的节点D配置了cpu采集任务时,通过配置采集插件和导入采集任务时给定分组可以将D加入AB组,然后就可以对ABD批量管理了(A应该是可以加入其他组的,比如A和C都配置了采集网卡信息,那么他们就是一个组,批量操作只能针对这个分组策略,比如AC只能针对网卡信息采集处理操作,这个还没细看,待验证)。

运行任务

想要收集数据,首先需要通过加载一个Task Manifest的方式。Task Manifest包含了对一系列度量数据的收集间隔、数据的转换方式以及信息发布的位置的规范。

Task Manifest

任务清单中描述了一个具体的任务,可以是Json或者YAML。任务清单被划分为两部分,头部与工作流。

头部

---

version: 1

schedule:

type: "simple"

interval: "1s"

max-failures: 10

version:在这里代表的是清单解析器的版本,这里仅仅具备一个版本号:1.。

schedule:

schedule描述了运行任务的计划类型与时间间隔。在撰写文本时,Snap具备以下三种调度:

simple、windowed、cron与[streaming] (#streaming-schedule)。

Snap的设计方式可以轻松地放入自定义调度程序。如果使用自定义计划,则可能需要清单的计划部分中有更多的键/值对。

Simple schedule

参数:interval string类型,每个执行计划之间的间隔时间,必须要大于0.

count:unit 运行次数,默认为0,代表无限制。

例:永远运行的schedule

"version": 1,

"schedule": {

"type": "simple",

"interval": "1s"

},

"max-failures": 10,

跑x次的schedule

"version": 1,

"schedule": {

"type": "simple",

"interval": "1s",

"count": 1

},

"max-failures": 1,

Windowed Schedule

Windowded schedule相对于simple schedule而言,加了一个起始和终止时间

"version": 1,

"schedule": {

"type": "windowed",

"interval": "1s",

"start_timestamp": "2016-10-27T16:00:00+01:00",

"stop_timestamp": "2016-10-28T16:30:00+01:00"

},

"max-failures": 10,

会永远运行的schedule

"version": 1,

"schedule": {

"type": "windowed",

"interval": "1s",

"start_timestamp": "2016-10-27T16:00:00+01:00"

},

"max-failures": 10,

某个时刻停止

"version": 1,

"schedule": {

"type": "windowed",

"interval": "1s",

"stop_timestamp": "2016-10-28T16:30:00+01:00"

},

"max-failures": 10,

某个时刻开始跑n次

"version": 1,

"schedule": {

"type": "windowed",

"interval": "1s",

"start_timestamp": "2016-10-27T16:00:00+01:00",

"count": 1

},

"max-failures": 1,

Cron Schedule

Cron Schedule是以Cron表达式类型来进行任务调度的,需要注意的是和Linux中的crontab略有不同:crontab的时程表如下:

f1 f2 f3 f4 f5 program  其中 f1 是表示分钟,f2 表示小时,f3 表示一个月份中的第几日,f4 表示月份,f5 表示一个星期中的第几天。program 表示要执行的程序。

Snap中的cron的时程表如下:

秒 分 时 日 月

常用的时程表达式

"version": 1,

"schedule": {

"type": "cron",

"interval" : "0 30 * * * *"

},

"max-failures": 10,

 Streaming Schedule

---

version: 1

schedule:

type: "streaming"

不支持interval和count类型的字段

The Workflow

---

collect:

metrics:

/intel/mock/foo: {}

/intel/mock/bar: {}

/intel/mock/*/baz: {}

config:

/intel/mock:

user: "root"

password: "secret"

tags:

/intel/mock:

experiment: "experiment 11"

/intel/mock/bar:

os: "linux"

process:

-

plugin_name: "passthru"

publish:

-

plugin_name: "file"

config:

file: "/tmp/published"

Workflow是一个DAG,它描述了任务的方式和内容。它总是以collect为根,包含了任意数量的process es和publish es。

远程target

工作流中的Process和Publish节点也可以通过“target”键定位远程Snap节点。这样做的目的是允许从正在进行数据收集的节点 卸载资源密集型的Workflow步骤。修改上面的示例我们有

---

collect:

metrics:

/intel/mock/foo: {}

/intel/mock/bar: {}

/intel/mock/*/baz: {}

config:

/intel/mock:

user: "root"

password: "secret"

tags:

/intel/mock:

experiment: "experiment 11"

/intel/mock/bar:

os: "linux"

process:

-

plugin_name: "passthru"

target: "127.0.0.1:8082"

publish:

-

plugin_name: "file"

target: "127.0.0.1:8082"

config:

file: "/tmp/published"

如果为工作流中的步骤指定了目标,则该步骤将在ip:port target指定的远程实例上执行。工作流中的每个节点都是独立评估的,因此工作流可以远程完成任何,全部或全部步骤(如果target省略了键,则该步骤默认为本地)

Collect

Collect部分描述了由命名空间指示的请求度量标准。

命名空间的元素由命名空间分隔符分隔,命名空间分隔符可以在任务清单中设置为不同的字符,并具有区分任务清单格式的一些限制。命名空间中的第一个字符定义命名空间分隔符。

具体的命名空间

完全按照度量目录中显示的方式声明度量标准名称(请参阅参考资料snaptel metric list)。

Task Manifest中要请求的度量指标

实际的收集指标

intel/mock/foo

/intel/mock/foo

/intel/mock/bar

/intel/mock/bar

/intel/mock/*/baz

(dynamic metric)

/intel/mock/host0/baz

/intel/mock/host1/baz

/intel/mock/host2/baz

/intel/mock/host3/baz

/intel/mock/host4/baz

/intel/mock/host5/baz

/intel/mock/host6/baz

/intel/mock/host7/baz

/intel/mock/host8/baz

/intel/mock/host9/baz

(collect metrics for all instances of the dynamic metric)

动态度量指标的指定实例

指定动态度量指标指的是value在命名空间中提供代替动态元素(例如,hostname,cgroup id等)。重要的是要记住,当在度量目录中显示时,动态元素由星号表示。当任务清单包含动态度量的特定实例时,仅收集该实例。如果该值不存在,则任务将出错。

任务清单中请求的度量标准

收集的指标

/ intel / mock / host0 / baz

(“/ intel / mock / * / baz”的具体实例)

/ intel / mock / host0 / baz

(只收集这一个指标)

动态查询

*表示完全匹配,:表示and。

/intel/mock/(foo;bar)

/intel/mock/foo

/intel/mock/bar

/intel/mock/(host0;host1;host2)/baz

/intel/mock/host0/baz

/intel/mock/host1/baz

/intel/mock/host2/baz

命名空间是另一个嵌套对象的键,它可能包含特定版本的插件,例如:

---

/foo/bar/baz:

version: 4

如果没有诶出具体的版本号,snap会自动选择一个最近的版本号

Tag

Tag描述了度量指标其他的元数据。和config类似,tag也可以直接在命名空间的分支处描述,分支下所有的子节点都会接受到给出的标签。

---

metrics:

/intel/perf/foo: {}

/intel/perf/bar: {}

/intel/perf/baz: {}

tags:

/intel/perf:

experiment: "experiment 11"

/intel/perf/bar:

os: "linux"

其余的Snap示例

配置文件

snapteld.conf,默认全局配置文件易于自定义(取消注释所需参数并调整其值)

snap-config-sample.yaml,YAML格式的全局配置文件示例

snap-config-sample.json,JSON格式的全局配置文件示例

snaptel-config-sample.json,snaptel配置文件。请注意,不建议使用它。

Snap应用的简易安装流程

系统要求

Linux、amd64/ OS X

意味着GoLand只能用来查看代码,不能用来debug。另外要在Windows系统上使用make需要用到MINGW,并不太熟悉。

前置条件

Snap需要安装swagger for go 才能在成功构建后更新OpenAPI文件。

手动安装Swagger,go get 类似于git clone 和go

go get -u github.com/go-swagger/go-swagger/cmd/swagger

安装

在RedHat中直接进行安装:

$ curl -s https://packagecloud.io/install/repositories/intelsdi-x/snap/script.rpm.sh | sudo bash

$ sudo yum install -y snap-telemetry

运行Snap

service snap-telemetry start

systemctl start snap-telemetry

如果您从二进制文件安装Snap,则可以通过以下命令启动Snap守护程序:

$ sudo mkdir -p /var/log/snap

$ sudo snapteld --plugin-trust 0 --log-level 1 --log-path /var/log/snap &

要查看服务日志:

$ tail -f /var/log/snap/snapteld.log

加载插件

Snap通过使用插件获得力量。该插件目录中包含了所有已知的捕捉插件与链接到他们的回购和释放网页的集合。

首先,让我们下载文件和psutil插件(同时确保安装了psutil):

$ export OS=$(uname -s | tr '[:upper:]' '[:lower:]')

$ export ARCH=$(uname -m)

$ curl -sfL "https://github.com/intelsdi-x/snap-plugin-publisher-file/releases/download/2/snap-plugin-publisher-file_${OS}_${ARCH}" -o snap-plugin-publisher-file

$ curl -sfL "https://github.com/intelsdi-x/snap-plugin-collector-psutil/releases/download/8/snap-plugin-collector-psutil_${OS}_${ARCH}" -o snap-plugin-collector-psutil

接下来使用snaptel以下命令将插件加载到Snap守护程序中:

$ snaptel plugin load snap-plugin-publisher-file

Plugin loaded

Name: file

Version: 2

Type: publisher

Signed: false

Loaded Time: Fri, 14 Oct 2016 10:53:59 PDT

$ snaptel plugin load snap-plugin-collector-psutil

Plugin loaded

Name: psutil

Version: 8

Type: collector

Signed: false

Loaded Time: Fri, 14 Oct 2016 10:54:07 PDT

验证插件是否已加载:

$ snaptel plugin list

NAME      VERSION    TYPE         SIGNED     STATUS    LOADED TIME

file      2          publisher    false      loaded    Fri, 14 Oct 2016 10:55:20 PDT

psutil    8          collector    false      loaded    Fri, 14 Oct 2016 10:55:29 PDT

查看可用的指标:

$ snaptel metric list

NAMESPACE                                VERSIONS

/intel/psutil/cpu/cpu-total/guest        8

/intel/psutil/cpu/cpu-total/guest_nice   8

/intel/psutil/cpu/cpu-total/idle         8

/intel/psutil/cpu/cpu-total/iowait       8

/intel/psutil/cpu/cpu-total/irq          8

/intel/psutil/cpu/cpu-total/nice         8

/intel/psutil/cpu/cpu-total/softirq      8

/intel/psutil/cpu/cpu-total/steal        8

/intel/psutil/cpu/cpu-total/stolen       8

/intel/psutil/cpu/cpu-total/system       8

/intel/psutil/cpu/cpu-total/user         8

/intel/psutil/load/load1                 8

/intel/psutil/load/load15                8

/intel/psutil/load/load5                 8

...

运行任务

要收集数据,您需要通过加载一份Task Manifest来创建任务。任务清单包含一组规范的收集规范,数据转换方式以及信息发布的位置。有关更多信息,请参阅任务文档

现在,下载并加载psutil示例:

$ curl https://raw.githubusercontent.com/intelsdi-x/snap/master/examples/tasks/psutil-file.yaml -o /tmp/psutil-file.yaml

$ snaptel task create -t /tmp/psutil-file.yaml

Using task manifest to create task

Task created

ID: 8b9babad-b3bc-4a16-9e06-1f35664a7679

Name: Task-8b9babad-b3bc-4a16-9e06-1f35664a7679

State: Running

注意:在后续命令中,使用CLI输出中的任务ID代替<task_id>。

这将启动通过psutil收集度量标准的任务,然后将数据发布到文件。要查看发布到文件的数据(要退出CTRL + C):

$ tail -f /tmp/psutil_metrics.log

或者直接使用Snap收集的数据流snaptel task watch <task_id>:

$ snaptel task watch 8b9babad-b3bc-4a16-9e06-1f35664a7679

NAMESPACE                             DATA             TIMESTAMP

/intel/psutil/cpu/cpu-total/idle      451176.5         2016-10-14 11:01:44.666137773 -0700 PDT

/intel/psutil/cpu/cpu-total/system    33749.2734375    2016-10-14 11:01:44.666139698 -0700 PDT

/intel/psutil/cpu/cpu-total/user      65653.2578125    2016-10-14 11:01:44.666145594 -0700 PDT

/intel/psutil/load/load1              1.81             2016-10-14 11:01:44.666072208 -0700 PDT

/intel/psutil/load/load15             2.62             2016-10-14 11:01:44.666074302 -0700 PDT

/intel/psutil/load/load5              2.38             2016-10-14 11:01:44.666074098 -0700 PDT

干得好 - 你完成了这个例子。根据您之前开始snap-telemetry服务的方式,使用适当的命令停止守护程序:

init.d服务: service snap-telemetry stop

系统服务: systemctl stop snap-telemetry

snapteld手动运行:sudo pkill snapteld

当您准备好继续前进时,请浏览Examples文件夹中可用的Snap的其他用法。

简要分析

Processor之filter

Filter是根据在Manifest里面的标签和过滤规则来过滤的。

假设过滤规则是这样的:

{

"version": 1,

"schedule": {

"type": "simple",

"interval": "1s"

},

"max-failures": 1,

"workflow": {

"collect": {

"tags": {

"/intel/psutil/cpu/cpu-total/idle": {

"other": "otherval",

"foo": "fooval"

},

"/intel/psutil/cpu/cpu-total/iowait": {

"baz": "bazval"

},

"/intel/psutil/cpu/cpu-total/stolen": {

"bar": "barval",

"foo": "valfoo",

"baz": "otherval"

},

"/intel/psutil/cpu/cpu-total/system": {

"bar": "barval",

"foo": "foovalue"

}

},

"metrics": {

"/intel/psutil/cpu/cpu-total/idle": {},

"/intel/psutil/cpu/cpu-total/iowait": {},

"/intel/psutil/cpu/cpu-total/stolen": {},

"/intel/psutil/cpu/cpu-total/system": {}

},

"process": [

{

"plugin_name": "tags-filter",

"config": {

"foo.allow": "foovalue,fooval",

"baz.allow": "bazval",

"bar.deny": "barval"

},

"publish": [

{

"plugin_name": "file",

"config": {

"file": "/tmp/psutil-tags-filter-file.log"

}

}

]

}

]

}

}

}

由"bar.deny": "barval"可知,带着barval类型的标签最后不会出现在我们的publisher中,结合来看,即我们的publishers中不会出现"/intel/psutil/cpu/cpu-total/idle": {},     "/intel/psutil/cpu/cpu-total/iowait": {},这两项metric,到file publisher中查看处理过后的数据:

[{

"timestamp": "2019-05-16T14:16:33.475892533+08:00",

"namespace": "/intel/psutil/cpu/cpu-total/idle",

"data": 1.0197728507e+08,

"unit": "",

"tags": {

"foo": "fooval",

"other": "otherval",

"plugin_running_on": "compute"

},

"version": 0,

"last_advertised_time": "2019-05-16T14:16:33.479258438+08:00"

},

{

"timestamp": "2019-05-16T14:16:33.475894903+08:00",

"namespace": "/intel/psutil/cpu/cpu-total/iowait",

"data": 9050.86,

"unit": "",

"tags": {

"baz": "bazval",

"plugin_running_on": "compute"

},

"version": 0,

"last_advertised_time": "2019-05-16T14:16:33.479258994+08:00"

}]

Tags-filter.go 中返回了

rules[tag].allowedValues = values以及rules[tag].deniedValues = values

metric数组某一个元素是否添加到数组,只要判断某一项指标打上的某一类型的具体标签是否在allow数组中且不在deny数组中

processor-tag

tag插件的目的在于处理数据,并将数据打上标签。

注意:可以在task manifest中来直接打上标签 (即不需要使用tag插件),并且不是每一个snap publisher的插件都支持tagging(打标签)。

Add context

我们可以看到最开始的架构图中间的process部分存在filter与add context的解释。filter是有对应的repo的,但是与filter不同的是,在process插件列表中并不存在对应的插件,这是需要我们根据实际的业务场景自己开发的。processor与snapteld之间有提供对应的grpc接口,数据通过grpc在snapteld与插件之间传递,processor负责处理数据,处理完成后返回给snapteld。

Change-detector里面,有一点点获取到上下文的意思。

Change detector该插件的目的是检测现在和前一时刻度量值的变化。度量的当前值被设置为度量的数据,度量的先前值被保留在度量的tag中。

该插件通过以下参数进行配置:

Rules –正则表达式。多个正则表达式用|来分割

正则表达式的特殊字符需要进行转义

Logs-regexp

使用正则表达式来处理日志

以前也使用过对应的插件,在logstash中也存在grok插件,logstash在获取日志是整个一串获取,如果把日志中每个字段代表的意思分割开来在传给elasticsearch。这样呈现出来的数据更加清晰,而且也能让kibana更方便的绘制图形。

filter {

if [type] == "apache" {

grok {

match => ["message" => "%{IPORHOST:addre} %{USER:ident} %{USER:auth} \[%{HTTPDATE:timestamp}\] \"%{WORD:http_method} %{NOTSPACE:request} HTTP/%{NUMBER:httpversion}\" %{NUMBER:status} (?:%{NUMBER:bytes}|-) \"(?:%{URI:http_referer}|-)\" \"%{GREEDYDATA:User_Agent}\""]

remove_field => ["message"]

}

date {

match => [ "timestamp", "dd/MMM/YYYY:HH:mm:ss Z" ]

}

}

}

%是grok pattern内置的正则表达式,:后面跟的是变量名,该变量名可以直接传到es在es中显示,插件解析后会直接返回json数据。

logstash的grok插件解析后得到的json结果如下:

{

"addre": [

[

"192.168.10.97"

]

],

"HOSTNAME": [

[

"192.168.10.97",

"192.168.10.175"

]

...........中间省略多行...........

"http_referer": [

[

"http://192.168.10.175/"

]

],

插件开发

https://github.com/intelsdi-x/snap-plugin-lib-go

snap telemetry-Intel 网络遥测框架简介相关推荐

  1. 网络爬虫框架Scrapy简介

    作者: 黄进(QQ:7149101) 一. 网络爬虫 网络爬虫(又被称为网页蜘蛛,网络机器人),是一种按照一定的规则,自动地抓取万维网信息的程序或者脚本:它是一个自动提取网页的程序,它为搜索引擎从万维 ...

  2. Python网络爬虫框架 Scrapy简介

    Scrapy 网络爬虫框架 Scrapy的安装 cmd 执行: pip install scrapy测试安装: scrapy -h Scrapy介绍 Scrapy不是一个函数功能库,而是一个爬虫框架. ...

  3. 爬虫基础(五)-----scrapy框架简介

    ---------------------------------------------------摆脱穷人思维 <五> :拓展自己的视野,适当做一些眼前''无用''的事情,防止进入只关 ...

  4. 几种常用深度学习框架简介

    几种常用深度学习框架简介 一.TensorFlow 1.1 Tensorflow简介 1.2 使用文档 1.3 预训练模型 二.Pytorch 2.1 Pytorch简介 2.2 使用文档 2.3 预 ...

  5. 网络增强现实开发简介 Introduction to Web AR development

    搭配webXR.mindAR.three.js和tensorflow.js 你会学到: 获得构建不同类型的网络增强现实应用程序的实践经验,包括图像效果.人脸效果和世界效果 获得关于增强现实如何在网络浏 ...

  6. 如何独立开发一个网络请求框架

    (原创出处为本博客:http://www.cnblogs.com/linguanh/) 目录:   前言 准备工作  开发模式 开发原则 线程 高并发 TCP/UDP 本类介绍  开发选择 功能列表 ...

  7. 【Android 热修复】热修复原理 ( 热修复框架简介 | 将 Java 字节码文件打包到 Dex 文件 )

    文章目录 一. 热修复框架简介 1.类替换 2.so 替换 3.资源替换 4.全平台支持 5.生效时间 6.性能损耗 7.总结 二. 将 Java 字节码文件打包到 Dex 文件 一. 热修复框架简介 ...

  8. java的网络工具netty简介

    2019独角兽企业重金招聘Python工程师标准>>> java的网络工具netty简介 Netty是一个NIO的客服端服务器框架,它可以简单.快速的搭建器一个协议包客服端服务器的应 ...

  9. teamtalk的conn框架简介及netlib线程安全问题

    2019独角兽企业重金招聘Python工程师标准>>> 最近把teamtalk的conn_map改成了智能指针,但改了总要多方面试试有没有问题,总不能编译通过,能正常启动就万事大吉了 ...

  10. 六款值得推荐的Android开源框架简介

    六款值得推荐的Android开源框架简介 技术不再多,知道一些常用的.不错的就够了.下面就是最近整理的"性价比"比较高的Android开源框架,应该是相对实用的. 1.volley ...

最新文章

  1. Python-ORM实战
  2. 【Android应用开发】RecycleView API 翻译 (文档翻译)
  3. suoi46 最大和和 (线段树)
  4. SQL Server 中使用 Try Catch 处理异常
  5. 玩转linux文件描述符和重定向,玩转Linux文件描述符和重定向
  6. 【MySQL】MySQL常见的读写分离方法
  7. micropython 人脸识别检测_Flask实战!从后台管理到人脸识别,六款优质Flask开源项目介绍...
  8. 【数据结构笔记05】堆栈及其顺序存储、链式存储
  9. c语言课程建设与改革,C语言程序设计课程教学改革的研究与实践
  10. makefile函数集锦
  11. 阿里大淘系模型治理方案分享
  12. AD9833数字信号发生器模块
  13. 数字信号处理(4)- 自适应滤波器
  14. silvaco-mobility models(1)
  15. 微信群二维码活码生成源码
  16. python平方根计算_Python计算平方根
  17. Windows提权基本原理
  18. 20162327WJH使用队列:模拟票务站台代码分析
  19. 游戏术语扫盲贴(手游人必懂)
  20. 创龙基于Xilinx Artix-7系列FPGA处理器的HDMI、SFP接口

热门文章

  1. 周末闲暇用javascript写个网页“斗兽棋”小游戏
  2. 自学编程,他从阿里校招生到高级技术专家
  3. python神经网络反向传播_神经网络及反向传播算法
  4. 蘑菇街iOS客户端应用源码
  5. html甘特图制作,AnyGantt实例教程——在Web网页中快速创建甘特图
  6. molten php 上传,molten:PHP 应用透明链路追踪工具
  7. 线性代数(4):伴随矩阵、逆矩阵和矩阵的秩
  8. 警告记录 - [Timing 38-316] Clock period ‘10.000‘ specified during out-of-context synthesis of instance
  9. PS非常火焰的火焰字效果
  10. 计算机语言里的堆栈是什么意思,汇编语言中的堆栈是什么?