相关文章推荐
近视的剪刀
·
如何使用nginx通过proxy_pass转 ...
·
2 月前
·
打酱油的柿子
·
Matlab ...
·
4 月前
·
爽快的小摩托
·
C#:值类型变量和引用类型变量在内存中的存储 ...
·
7 月前
·
光明磊落的烤土司
·
一篇文章教你顺利入门和开发chrome扩展程 ...
·
1 年前
·
路过的小熊猫
·
jquery监听刷新、关闭页面事件提示事件 ...
·
1 年前
·
Code
›
阿里云 Serverless 异步任务处理系统在数据分析领域的应用_ITPUB博客
kafka
大数据
异步
转码
http://blog.itpub.net/69981534/viewspace-2913135/
文武双全的热带鱼
1 年前
# 异步任务处理系统中的数据分析 数据处理、机器学习训练、数据统计分析是最为常见的一类离线任务。这类任务往往都是经过了一系列的预处理后,由上游统一发送到任务平台进行批量训练及分析。在处理语言方面,Python 由于其所提供的丰富的数据处理库,成为了数据领域最为常用的语言之一。函数计算原生支持 Python runtime,并支持快捷的引入第三方库,使得使用函数计算异步任务进行处理变得极为方便。 ## 数据分析场景常见诉求 数据分析场景往往具有执行时间长、并发量大的特点。在离线场景中,往往会定时触发一批大量的数据进行集中处理。由于这种触发特性,业务方往往会对资源利用率(成本)具有较高的要求,期望能够满足效率的同时,尽量降低成本。具体归纳如下: 1. 程序开发便捷,对于第三方包及自定义依赖友好; 1. 支持长时运行。能够查看执行过程中的任务状态,或登录机器进行操作。如果出现数据错误支持手动停止任务; 1. 资源利用率高,成本最优。 以上诉求非常适合使用函数计算异步任务。 ## 典型案例 - 数据库自治服务 ### 业务基本情况 阿里云集团内部的数据库巡检平台主要用于对 sql 语句的慢查询、日志等进行优化分析。整个平台任务分为离线训练及在线分析两类主要任务,其中在线分析业务的的计算规模达到了上万核,离线业务的每日执行时长也在数万核小时。由于在线分析、离线训练时间上的不确定性,很难提高集群整体资源利用率,并且在业务高峰来时需要极大的弹性算力支持。使用函数计算后,整个业务的架构图如下: ![image.png](https://intranetproxy.alipay.com/skylark/lark/0/2022/png/151014/1647505612325-df74c2f9-514e-4bca-95a7-de0e332b5953.png#clientId=u83d53b44-1b76-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=390&id=u78a68556&margin=%5Bobject%20Object%5D&name=image.png&originHeight=858&originWidth=1906&originalType=binary&ratio=1&rotation=0&showTitle=false&size=585058&status=done&style=none&taskId=ufef67ece-24a6-4d8e-a9d6-64acb00c3d9&title=&width=866.3636175857107) ### 业务痛点及架构演进 数据库巡检平台负责阿里巴巴全网各 Region 的数据库 SQL 优化及分析工作。Mysql 数据来源于各 Region 的各个集群,并统一在 Region 维度进行一次预聚合及存储。在进行分析时,由于需要跨 region 的聚合及统计,巡检平台首先尝试在内网搭建大型 Flink 集群进行统计分析工作。但是在实际使用中,遇到了如下问题: 1. 数据处理算法迭代繁琐。主要体现在算法的部署、测试及发布上。Flink 的 Runtime 能力极大限制了发布周期; 1. 对于常见的及一些自定义的第三方库,Flink 支持不是很好。算法所依赖的一些机器学习、统计的库在 Flink 官方 Python runtime 中要么没有,要么版本老旧,使用不便,无法满足要求; 1. 走 Flink 转发链路较长,Flink 排查问题困难; 1. 峰值时弹性速度及资源均较难满足要求。并且整体成本非常高。 在了解了函数计算后,针对 Flink 计算部分进行了算法任务的迁移工作,将核心训练及统计算法迁移至函数计算。通过使用函数计算异步任务所提供的相关能力,整个开发、运维及成本得到了极大的提升。 #### 迁移函数计算架构后的效果 1. 迁移函数计算后,系统能够完整承接峰值流量,快速完成每日分析及训练任务; 1. 函数计算丰富的 Runtime 能力支持了业务的快速迭代; 1. 计算上相同的核数成本变为了原来 Flink 的 1/3。 函数计算异步任务非常适用于这类数据处理任务。函数计算在降低运算资源的成本同时,能够将您从繁杂的平台运维工作中解放出来,专注于算法开发及优化。 # 函数计算异步任务最佳实践-Kafka ETL ETL 是数据处理中较为常见的任务。原始数据或存在于 Kafka 中,或存在于 DB 中,因为业务需要对数据进行处理后转储到其他存储介质(或存回原来的任务队列)。这类业务也属于明显的任务场景。如果您采用了云上的中间件服务(如云上的 Kafka),您就可以利用函数计算强大的触发器集成生态便捷的集成 Kafka,而无需关注诸如 Kafka Connector 的部署、错误处理等与业务无关的操作。 ## ETL 任务场景的需求 一个 ETL 任务往往包含 Source、Sink 及处理单元三个部分,因此 ETL 任务除了对算力的要求外,还需要任务系统具有极强的上下游连接生态。除此之外,由于数据处理的准确性要求,需要任务处理系统能够提供任务去重、Exactly Once 的操作语义。并且,对于处理失败的消息,需要能够进行补偿(如重试、死信队列)的能力。总结如下: 1. 任务的准确执行: 1. 任务重复触发支持去重; 1. 任务支持补偿,死信队列; 2. 任务的上下游: 1. 能够方便的拉取数据,并在处理后将数据传递至其他系统; 3. 算子能力的要求: 1. 支持用户自定义算子的能力,能够灵活的执行各种数据处理任务。 ## Serverless Task 对 ETL 任务的支持 函数计算支持的 Destinationg 功能可以很好的支持 ETL 任务对于便捷连接上下游、任务准确执行的相关诉求。函数计算丰富的 Runtime 支持也使得对于数据处理的任务变得极为灵活。在 Kafka ETL 任务处理场景中,我们主要用到的 Serverless Task 能力如下: 1. 异步目标配置功能: 1. 通过配置任务成功目标,支持自动将任务投递至下游系统(如队列中); 1. 通过配置任务失败目标,支持死信队列能力,将失败的任务投递至消息队列,等待后续的补偿处理; 2. 灵活的算子及第三方库支持: 1. Python 由于其丰富的统计、运算的第三方库的支持,在数据处理领域 Python 是用的最为广泛的语言之一。函数计算的 Python Runtime 支持对第三方库打包,使您能够快速的进行原型验证及测试上线。 ## Kafka ETL 任务处理示例 我们以简单的 ETL 任务处理为例,数据源来自 Kafka,经过函数计算处理后,将任务执行结果及上下游信息推送至消息服务 MNS。函数计算部分项目源码见:[https://github.com/awesome-fc/Stateful-Async-Invocation](https://github.com/awesome-fc/Stateful-Async-Invocation) ### 资源准备 #### Kafka 资源准备 1. 进入 Kafka 控制台,点击购买实例,之后部署。等待实例部署完成;![图片.png](https://intranetproxy.alipay.com/skylark/lark/0/2022/png/151014/1646826157512-cc49eaaa-b71d-486a-ae45-144210358ad7.png#clientId=u7b610aab-1f42-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=459&id=G86xG&margin=%5Bobject%20Object%5D&name=%E5%9B%BE%E7%89%87.png&originHeight=459&originWidth=2185&originalType=binary&ratio=1&rotation=0&showTitle=false&size=196331&status=done&style=none&taskId=u55e6d48c-c61f-4e3b-a85f-6bef94bedb2&title=&width=2185) 1. 进入创建好的实例中,创建一个测试用 Topic。 #### 目标资源准备(MNS) 进入 MNS 控制台,分别创建两个队列: 1. dead-letter-queue:作为死信队列使用。当消息处理失败后,执行的上下文信息将投递到这里; 1. fc-etl-processed-message:作为任务成功执行后的推送目标。 创建完成后,如下图所示: ![图片.png](https://intranetproxy.alipay.com/skylark/lark/0/2022/png/151014/1646826347835-bdf74ad3-fe38-4b44-a17a-010dbc856168.png#clientId=u7b610aab-1f42-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=234&id=u114ce0b5&margin=%5Bobject%20Object%5D&name=%E5%9B%BE%E7%89%87.png&originHeight=429&originWidth=1213&originalType=binary&ratio=1&rotation=0&showTitle=false&size=96674&status=done&style=none&taskId=u7bb4beb3-d1d2-462c-83d2-2b10aa7a1f9&title=&width=661) #### 部署 1. 下载安装 Serverless Devs: npm install @serverless-devs/s > 详细文档可以参考 [Serverless Devs 安装文档](https://github.com/Serverless-Devs/Serverless-Devs/blob/master/docs/zh/install.md) 2. 配置密钥信息: s config add > 详细文档可以参考 [阿里云密钥配置文档](https://github.com/devsapp/fc/blob/main/docs/zh/config.md) 3. 进入项目,修改 s.yaml 文件中的目标 ARN 为上述创建后的 MNS 队列 ARN,并修改服务角色为已存在的角色; 3. 部署:`s deploy -t s.yaml` #### 配置 ETL 任务 1. 进入kafka 控制台 - connector 任务列表标签页,点击创建 Connector;![图片.png](https://intranetproxy.alipay.com/skylark/lark/0/2022/png/151014/1646826499081-bc941cbb-77e4-4cb4-a100-38adf3d7456c.png#clientId=u7b610aab-1f42-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=472&id=u74a8fb78&margin=%5Bobject%20Object%5D&name=%E5%9B%BE%E7%89%87.png&originHeight=472&originWidth=2166&originalType=binary&ratio=1&rotation=0&showTitle=false&size=265764&status=done&style=none&taskId=u4c38635b-6ba9-44bc-9c63-d7228833236&title=&width=2166) 1. 在配置完基本信息、源的 Topic 后,配置目标服务。在这里面我们选择函数计算作为目标 ![图片.png](https://intranetproxy.alipay.com/skylark/lark/0/2022/png/151014/1646826581500-4f19a8e2-cf1e-445a-a0c7-70cb6adcc534.png#clientId=u7b610aab-1f42-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=1098&id=ud8d474e6&margin=%5Bobject%20Object%5D&name=%E5%9B%BE%E7%89%87.png&originHeight=1098&originWidth=2175&originalType=binary&ratio=1&rotation=0&showTitle=false&size=318335&status=done&style=none&taskId=u805b077b-4fc5-44e9-846f-ba2eb0cdd93&title=&width=2175)您可以根据业务需求配置发送批大小及重试次数。至此,我们已完成任务的基本配置。**注意:这里面的发送模式请选择“异步”模式。** 进入到函数计算异步配置页面,我们可以看到目前的配置如下: ![图片.png](https://intranetproxy.alipay.com/skylark/lark/0/2022/png/151014/1646826758355-d0f11414-1c89-4531-a585-805accfc9278.png#clientId=u38d8996b-b299-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=466&id=u473093fd&margin=%5Bobject%20Object%5D&name=%E5%9B%BE%E7%89%87.png&originHeight=466&originWidth=2168&originalType=binary&ratio=1&rotation=0&showTitle=false&size=187012&status=done&style=none&taskId=uac9f83af-d67e-46b2-bcd1-02b10a34dcc&title=&width=2168) #### 测试 ETL 任务 1. 进入kafka 控制台 - connector 任务列表标签页,点击测试;填完消息内容后,点击发送 ![图片.png](https://intranetproxy.alipay.com/skylark/lark/0/2022/png/151014/1646826863985-e03e909b-5ecc-4745-b439-f6e5428efee4.png#clientId=u38d8996b-b299-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=652&id=u6cd75e00&margin=%5Bobject%20Object%5D&name=%E5%9B%BE%E7%89%87.png&originHeight=652&originWidth=2185&originalType=binary&ratio=1&rotation=0&showTitle=false&size=265392&status=done&style=none&taskId=uce107609-d864-418b-b4a6-7248bd20ef4&title=&width=2185) 2. 发送多条消息后,进入到函数控制台。我们可以看到有多条消息在执行中。此时我们选择使用停止任务的方式来模拟一次任务执行失败: ![图片.png](https://intranetproxy.alipay.com/skylark/lark/0/2022/png/151014/1646826926864-d2b83dd0-2224-477b-8dc0-28ed557c5722.png#clientId=u38d8996b-b299-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=365&id=xUrEy&margin=%5Bobject%20Object%5D&name=%E5%9B%BE%E7%89%87.png&originHeight=365&originWidth=2159&originalType=binary&ratio=1&rotation=0&showTitle=false&size=184584&status=done&style=none&taskId=u23830fb8-70c9-433b-bbd3-9c01f7f70c6&title=&width=2159) 3. 进入到消息服务 MNS 控制台中,我们可以看到两个先前创建的队列中均有一条可用消息,分别代表一次执行和失败的任务内容: ![图片.png](https://intranetproxy.alipay.com/skylark/lark/0/2022/png/151014/1646827005652-620ba057-2e41-4971-88c7-b56cd98c4247.png#clientId=u38d8996b-b299-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=322&id=uc57f3d83&margin=%5Bobject%20Object%5D&name=%E5%9B%BE%E7%89%87.png&originHeight=322&originWidth=1897&originalType=binary&ratio=1&rotation=0&showTitle=false&size=112748&status=done&style=none&taskId=u4f3397e7-610f-4558-8767-395864bdc14&title=&width=1897) 4. 进入到队列详情中,我们可以看到两条消息内容。以成功的消息内容为例: ```json "timestamp":1646826806389, "requestContext":{ "requestId":"919889e7-60ff-408f-a0c7-627bbff88456", "functionArn":"acs:fc:::services/fc-etl-job.LATEST/functions/fc-job-function", "condition":"", "approximateInvokeCount":1 "requestPayload":"[{\"key\":\"k1\",\"offset\":1,\"overflowFlag\":false,\"partition\":5,\"timestamp\":1646826803356,\"topic\":\"connector-demo\",\"value\":\"k1\",\"valueSize\":4}]", "responseContext":{ "statusCode":200, "functionError":"" "responsePayload":"[\n {\n \"key\": \"k1\",\n \"offset\": 1,\n \"overflowFlag\": false,\n \"partition\": 5,\n \"timestamp\": 1646826803356,\n \"topic\": \"connector-demo\",\n \"value\": \"k1\",\n \"valueSize\": 4\n }\n]" 在这里面,我们可以看到 "responsePayload" 这一个 Key 中有函数返回的原始内容。一般情况下我们会将数据处理的结果作为 response 返回,所以在后续的处理中,可以通过读取 "responsePayload" 来获取处理后的结果。 "requestPayload" 这一个 Key 中是 Kafka 触发函数计算的原始内容,通过读取这条数据中的内容,便可以获取原始数据。 # 函数计算异步任务最佳实践-音视频处理 随着计算机技术和网络的发展,视频点播技术因其良好的人机交互性和流媒体传输技术倍受教育、娱乐等行业的青睐。当前云计算平台厂商的产品线不断成熟完善,如果想要搭建视频点播类应用,直接上云会扫清硬件采购、技术等各种障碍。以阿里云为例,典型的解决方案如下: ![image.png](https://intranetproxy.alipay.com/skylark/lark/0/2022/png/79424/1645423650711-92bcc6ac-823d-4be0-a4d8-6d464bf5201f.png#clientId=uf771e0a8-c23c-4&crop=0&crop=0&crop=1&crop=1&height=314&id=cN3iB&name=image.png&originHeight=771&originWidth=1109&originalType=binary&ratio=1&rotation=0&showTitle=false&size=94222&status=done&style=none&taskId=u56ed358d-5f6e-4b6d-9b77-1d4803df08b&title=&width=451) 在该解决方案中,对象存储OSS可以支持海量视频存储,采集上传的视频被转码以适配各种终端、CDN加速终端设备播放视频的速度。此外还有一些[内容安全](https://help.aliyun.com/product/28415.html)审查需求,例如鉴黄、鉴恐等。 音视频是典型的**长时处理场景**,非常适合使用函数计算任务。 ## 音视频处理的需求 在视频点播解决方案中,视频转码是最消耗计算力的一个子系统,虽然您可以使用云上专门的转码服务,但在某些场景下,您仍会选择自己搭建转码服务,例如: - 需要更弹性的视频处理服务。例如,已经在虚拟机或容器平台上基于FFmpeg部署了一套视频处理服务,但想在此基础上**提升资源利用率,实现具有明显波峰波谷、流量突增情况下的快弹及稳定性;** - **需要批量快速处理多个超大的视频。**例如,每周五定时产生几百个4 GB以上1080P的大视频,每个任务可能执行时长达数小时; - 对视频处理任务希望实时**掌握进度**;并在一些出现错误的情况下需要登录实例排查问题甚至**停止执行中的任务**避免资源消耗。 ## Serverless Task 对音视频场景的支持 上述诉求是典型的任务场景。而由于这类任务往往具有波峰波谷的特性,如何进行计算资源的运维,并尽可能的降低其成本,这部分的工作量甚至比实际视频处理业务的工作量还要大。Serverless Task 这一产品形态就是为了解决这类场景而诞生的,通过 Serverless Task,您可以快速构建高弹性、高可用、低成本免运维的视频处理平台。 在这个场景中,我们会用到的 Serverless Task 的主要能力如下: 1. 免运维 & 低成本:计算资源随用随弹,不使用不付费; 1. 长时执行任务负载友好:单个实例最长支持 24h 的执行时长; 1. 任务去重:支持触发端的错误补偿。对于单一任务,Serverless Task 能够做到自动去重的能力,执行更可靠; 1. 任务可观测:所有执行中、执行成功、执行失败的任务可追溯,可查询;支持任务的执行历史数据查询、任务日志查询; 1. 任务可操作:您可以停止、重试任务; 1. 敏捷开发 & 测试:官方支持 S 工具进行自动化一键部署;支持登录运行中函数实例的能力,您可以直接登录实例调试 ffmpeg 等第三方程序,所见即所得。 ## Serverless - FFmpeg 视频转码 项目源码:[https://github.com/devsapp/start-ffmpeg/tree/master/transcode/src](https://github.com/devsapp/start-ffmpeg/tree/master/transcode/src) ### 部署 1. 下载安装 Serverless Devs: npm install @serverless-devs/s > 详细文档可以参考 [Serverless Devs 安装文档](https://github.com/Serverless-Devs/Serverless-Devs/blob/master/docs/zh/install.md) 2. 配置密钥信息: s config add > 详细文档可以参考 [阿里云密钥配置文档](https://github.com/devsapp/fc/blob/main/docs/zh/config.md) 3. 初始化项目:`s init video-transcode -d video-transcode` 3. 进入项目并部署:`cd video-transcode && s deploy` ### 调用函数 1. 发起 5 次异步任务函数调用 ```go $ s VideoTranscoder invoke -e '{"bucket":"my-bucket", "object":"480P.mp4", "output_dir":"a", "dst_format":"mov"}' --invocation-type async --stateful-async-invocation-id my1-480P-mp4 VideoTranscoder/transcode async invoke success. request id: bf7d7745-886b-42fc-af21-ba87d98e1b1c $ s VideoTranscoder invoke -e '{"bucket":"my-bucket", "object":"480P.mp4", "output_dir":"a", "dst_format":"mov"}' --invocation-type async --stateful-async-invocation-id my2-480P-mp4 VideoTranscoder/transcode async invoke success. request id: edb06071-ca26-4580-b0af-3959344cf5c3 $ s VideoTranscoder invoke -e '{"bucket":"my-bucket", "object":"480P.mp4", "output_dir":"a", "dst_format":"flv"}' --invocation-type async --stateful-async-invocation-id my3-480P-mp4 VideoTranscoder/transcode async invoke success. request id: 41101e41-3c0a-497a-b63c-35d510aef6fb $ s VideoTranscoder invoke -e '{"bucket":"my-bucket", "object":"480P.mp4", "output_dir":"a", "dst_format":"avi"}' --invocation-type async --stateful-async-invocation-id my4-480P-mp4 VideoTranscoder/transcode async invoke success. request id: ff48cc04-c61b-4cd3-ae1b-1aaaa1f6c2b2 $ s VideoTranscoder invoke -e '{"bucket":"my-bucket", "object":"480P.mp4", "output_dir":"a", "dst_format":"m3u8"}' --invocation-type async --stateful-async-invocation-id my5-480P-mp4 VideoTranscoder/transcode async invoke success. request id: d4b02745-420c-4c9e-bc05-75cbdd2d010f 2、登录[FC 控制台](https://fcnext.console.aliyun.com/) ![](https://intranetproxy.alipay.com/skylark/lark/0/2022/png/151014/1646298663437-303dd06e-a57d-46b7-b910-09c9a1437864.png?x-oss-process=image%2Fresize%2Cw_937%2Climit_0#crop=0&crop=0&crop=1&crop=1&from=url&id=yLKWx&originHeight=455&originWidth=937&originalType=binary&ratio=1&rotation=0&showTitle=false&status=done&style=none&title=) 可以清晰看出每一次转码任务的执行情况: - A 视频是什么时候开始转码的, 什么时候转码结束 - B 视频转码任务不太符合预期, 我中途可以点击停止调用 - 通过调用状态过滤和时间窗口过滤,我可以知道现在有多少个任务正在执行, 历史完成情况是怎么样的 - 可以追溯每次转码任务执行日志和触发payload - 当您的转码函数有异常时候, 会触发 dest-fail 函数的执行,您在这个函数可以添加您自定义的逻辑, 比如报警 转码完毕后, 您也可以登录 OSS 控制台到指定的输出目录查看转码后的视频。 > 在本地使用该项目时,不仅可以部署,还可以进行更多的操作,例如查看日志,查看指标,进行多种模式的调试等,这些操作详情可以参考[函数计算组件命令文档](https://github.com/devsapp/fc#%E6%96%87%E6%A1%A3%E7%9B%B8%E5%85%B3) ### 详解异步任务专题往期文章推荐 [异步任务处理系统,如何解决业务长耗时、高并发难题?](https://mp.weixin.qq.com/s?__biz=MzI4NzI5MDM1MQ==&mid=2247511273&idx=1&sn=951b4af8cd0b1ac74f94db2d82cf4ed2&chksm=ebcd0a69dcba837fc7718fe1c5d2c0baf6e1ce75887e02387cc273ec2a755be1a4422c168dab#rd) [详解异步任务:函数计算的任务触发去重](https://mp.weixin.qq.com/s?__biz=MzI4NzI5MDM1MQ==&mid=2247512181&idx=1&sn=6a0a1814cca98938f6ceec3d8f20933e&chksm=ebcd16f5dcba9fe38397d596cd46eee79c68cbd30bea124f6269ebbc396f394beb93a5a58c75#rd) [详解异步任务:任务的状态及生命周期管理](https://mp.weixin.qq.com/s?__biz=MzI4NzI5MDM1MQ==&mid=2247513154&idx=1&sn=a5bba60f9e19d858b21b43f3e90bd9ad&chksm=ebcd12c2dcba9bd4fe0fb386cbe1d827d1bfd33e1fd246cd1614e85c2fecd52a51832fcd7250#rd) [详解异步任务 | 看 Serverless Task 如何解决任务调度&可观测性中的问题](https://mp.weixin.qq.com/s?__biz=MzI4NzI5MDM1MQ==&mid=2247514022&idx=1&sn=6f9ea9fb7c4e54fea49d52febde6c215&chksm=ebcd1126dcba98309fcfcf24df6f8b587647ff2162b78afaa0f4424c027c63a18773b2c8b133#rd) 更多内容关注 Serverless 微信公众号(ID:serverlessdevs),汇集 Serverless 技术最全内容,定期举办 Serverless 活动、直播,用户最佳实践。
来自 “ ITPUB博客 ” ,链接:http://blog.itpub.net/69981534/viewspace-2913135/,如需转载,请注明出处,否则将追究法律责任。
广播电视节目制作经营许可证(京) 字第1234号 中国互联网协会会员
推荐文章
近视的剪刀
·
如何使用nginx通过proxy_pass转发查询字符串参数?-腾讯云开发者社区-腾讯云
2 月前
打酱油的柿子
·
Matlab 多维矩阵的最大值或最小值及其位置索引_matlab 索引最大值-CSDN博客
4 月前
爽快的小摩托
·
C#:值类型变量和引用类型变量在内存中的存储方式 - BigBosscyb - 博客园
7 月前
光明磊落的烤土司
·
一篇文章教你顺利入门和开发chrome扩展程序(插件) - 掘金
1 年前
路过的小熊猫
·
jquery监听刷新、关闭页面事件提示事件 - 简书
1 年前