Apache DolphinScheduler - 快速扩展 TaskPlugin 从入门到放弃

目前在大数据生态中,调度系统是不可或缺的一个重要组件。Apache DolphinScheduler 作为一个顶级的 Apache 项目,其稳定性和易用性也可以说是名列前茅的。而对于一个调度系统来说,能够支持的可调度的任务类型同样是一个非常重要的因素,在调度、分布式、高可用、易用性解决了的情况下,随着业务的发展或者各种需求使用到的组件增多,用户自然而然会希望能够快速、方便、简洁地对 Apache Dolphinscheduler 可调度的任务类型进行扩充。本文便带大家了解如何方便、极速扩充一个 Apache DolphinScheduler Task,如图底部一栏是我们本次需要讨论的他们是如何从 0 到 1 扩展的 Task 插件!

先吃点凉菜……

一、什么是 SPI 服务发现(What is SPI)

SPI 全称为 (Service Provider Interface) ,是 JDK 内置的一种服务提供发现机制。大多数人可能会很少用到它,因为它的定位主要是面向开发厂商的,在 java.util.ServiceLoader 的文档里有比较详细的介绍,其抽象的概念是指动态加载某个服务实现。

二、为什么要引入 SPI(Why did we introduce SPI)

不同的企业可能会有自己的组件需要通过 task 去执行,大数据生态中最为常用数仓工具 Apache Hive 来举例,不同的企业使用 Hive 方法各有不同。有的企业通过 HiveServer2 执行任务,有的企业使用 HiveClient 执行任务,而 Apache DolphinScheduler 提供的开箱即用的 Task 中并没有支持 HiveClient 的 Task,所以大部分使用者都会通过 Shell 去执行。然而,Shell 哪有天然的TaskTemplate 好用呢?所以,Apache DolphinScheduler 为了使用户能够更好地根据企业需求定制不同的 Task,便支持了 TaskSPI 化。

我们首先要了解一下 Apache DolphinScheduler 的 Task 改版历程,在 DS 1.3.x 时,扩充一个 Task 需要重新编译整个 Apache DolphinScheduler,耦合严重,所以在 Apache DolphinScheduler 2.0.x 引入了 SPI。前面我们提到了 SPI 的抽象概念是动态加载某个服务的实现,这里我们具象一点,将 Apache DolphinScheduler 的 Task 看成一个执行服务,而我们需要根据使用者的选择去执行不同的服务,如果没有的服务,则需要我们自己扩充,相比于 1.3.x 我们只需要完成我们的 Task 具体实现逻辑,然后遵守 SPI 的规则,编译成 Jar 并上传到指定目录,即可使用我们自己编写的 Task。

三、谁在使用它(Who is using it)

1、Apache DolphinScheduler

  • task

  • datasource

2、Apache Flink

  • flink sql connector:用户实现了一个flink-connector后,Flink也是通过SPI来动态加载

3、Spring Boot

  • spring boot spi

4、Jdbc

  • jdbc4.0以前, 开发人员还需要基于 Class.forName("xxx") 的方式来装载驱动,jdbc4也基于spi的机制来发现驱动提供商了,可以通过META-INF/services/java.sql.Driver文件里指定实现类的方式来暴露驱动提供者

5、更多

  • dubbo

  • common-logging

四、Apache DolphinScheduler SPI Process

剖析一下上面这张图,我给 Apache DolphinScheduler 分为逻辑 Task 以及物理 Task,逻辑 Task 指 DependTask,SwitchTask 这种逻辑上的 Task;物理 Task 是指 ShellTask,SQLTask 这种执行任务的 Task。而在 Apache DolphinScheduler中,我们一般扩充的都是物理 Task,而物理 Task 都是交由 Worker 去执行,所以我们要明白的是,当我们在有多台 Worker 的情况下,要将自定义的 Task 分发到每一台有 Worker 的机器上,当我们启动 Worker 服务时,worker 会去启动一个 ClassLoader 来加载相应的实现了规则的 Task lib,可以看到 HiveClient 和 SeatunnelTask 都是用户自定义的,但是只有 HiveTask 被 Apache DolphinScheduler TaskPluginManage 加载了,原因是 SeatunnelTask 并没有去遵守 SPI 的规则。SPI 的规则图上也有赘述,也可以参考 java.util.ServiceLoader 这个类,下面有一个简单的参考(摘出的一部分代码,具体可以自己去看看)

public final class ServiceLoader<S> implements Iterable<S> {//scanning dir prefixprivate static final String PREFIX = "META-INF/services/";//The class or interface representing the service being loadedprivate final Class<S> service;//The class loader used to locate, load, and instantiate providersprivate final ClassLoader loader;//Private inner class implementing fully-lazy provider lookupprivate class LazyIterator implements Iterator<S> {Class<S> service;ClassLoader loader;Enumeration<URL> configs = null;String nextName = null;//......private boolean hasNextService() {if (configs == null) {try {//get dir all classString fullName = PREFIX + service.getName();if (loader == null)configs = ClassLoader.getSystemResources(fullName);elseconfigs = loader.getResources(fullName);} catch (IOException x) {//......}//......}}}
}
  • Ps:当然下文会有更简便的方式来实现 SPI——注解 @AutoService

好,接下来正式开始我们的正餐——如何扩展一个 Task Plugin

翠花,上热菜~

一、业务背景

我们需要实现一个 Lock 分布式锁的插件,方便多个工作流同时执行某一段业务时,有一定的业务同步阻塞功能,以免出现并发问题。如图是项目结构图

二、Maven 依赖

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><parent><artifactId>dolphinscheduler-task-plugin</artifactId><groupId>org.apache.dolphinscheduler</groupId><version>3.1.7</version></parent><modelVersion>4.0.0</modelVersion><artifactId>dolphinscheduler-task-lock</artifactId><packaging>jar</packaging><properties><maven.compiler.source>8</maven.compiler.source><maven.compiler.target>8</maven.compiler.target></properties><dependencies><dependency><groupId>org.apache.dolphinscheduler</groupId><artifactId>dolphinscheduler-spi</artifactId></dependency><dependency><groupId>org.apache.dolphinscheduler</groupId><artifactId>dolphinscheduler-task-api</artifactId></dependency></dependencies>
</project>

三、创建 Task 通道工厂(TaskChannelFactory)

首先我们需要创建任务服务的工厂,其主要作用是帮助构建 TaskChannel 以及 TaskPlugin 参数,同时给出该任务的唯一标识,ChannelFactory 在 Apache DolphinScheduler 的 Task 服务组中,其作用属于是在任务组中的承上启下,交互前后端以及帮助 Worker 构建 TaskChannel

package org.apache.dolphinscheduler.plugin.task.lock;/** Licensed to the Apache Software Foundation (ASF) under one or more* contributor license agreements.  See the NOTICE file distributed with* this work for additional information regarding copyright ownership.* The ASF licenses this file to You under the Apache License, Version 2.0* (the "License"); you may not use this file except in compliance with* the License.  You may obtain a copy of the License at**    http://www.apache.org/licenses/LICENSE-2.0** Unless required by applicable law or agreed to in writing, software* distributed under the License is distributed on an "AS IS" BASIS,* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.* See the License for the specific language governing permissions and* limitations under the License.*/import com.google.auto.service.AutoService;
import org.apache.dolphinscheduler.plugin.task.api.TaskChannel;
import org.apache.dolphinscheduler.plugin.task.api.TaskChannelFactory;
import org.apache.dolphinscheduler.spi.params.base.PluginParams;
import java.util.List;@AutoService(TaskChannelFactory.class)
public class LockTaskChannelFactory implements TaskChannelFactory {/*** 创建任务通道, 基于该通道执行任务* @return 任务通道*/@Overridepublic TaskChannel create() {return new LockTaskChannel();}/*** 返回当前任务的全局唯一标识* @return 任务类型名称*/@Overridepublic String getName() {return "LOCK";}/*** 前端页面需要用到的渲染, 一般也同步到* @return*/@Overridepublic List<PluginParams> getParams() {return null;}
}
  • Tips:这个注解就是我们上文提到过的,我们在文章末尾会稍微讲解下 @AutoService(TaskChannelFactory.class)

四、创建 TaskChannel

有了工厂之后,我们会根据工厂创建出 TaskChannel,TaskChannel 包含如下两个方法,一个是取消,一个是创建,目前不需要关注取消,主要关注创建任务

package org.apache.dolphinscheduler.plugin.task.lock;/** Licensed to the Apache Software Foundation (ASF) under one or more* contributor license agreements.  See the NOTICE file distributed with* this work for additional information regarding copyright ownership.* The ASF licenses this file to You under the Apache License, Version 2.0* (the "License"); you may not use this file except in compliance with* the License.  You may obtain a copy of the License at**    http://www.apache.org/licenses/LICENSE-2.0** Unless required by applicable law or agreed to in writing, software* distributed under the License is distributed on an "AS IS" BASIS,* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.* See the License for the specific language governing permissions and* limitations under the License.*/import org.apache.dolphinscheduler.common.utils.JSONUtils;
import org.apache.dolphinscheduler.plugin.task.api.TaskChannel;
import org.apache.dolphinscheduler.plugin.task.api.TaskExecutionContext;
import org.apache.dolphinscheduler.plugin.task.api.parameters.AbstractParameters;
import org.apache.dolphinscheduler.plugin.task.api.parameters.ParametersNode;
import org.apache.dolphinscheduler.plugin.task.api.parameters.resource.ResourceParametersHelper;public class LockTaskChannel implements TaskChannel {@Overridepublic void cancelApplication(boolean status) {}@Overridepublic LockTask createTask(TaskExecutionContext taskRequest) {return new LockTask(taskRequest);}@Overridepublic AbstractParameters parseParameters(ParametersNode parametersNode) {return JSONUtils.parseObject(parametersNode.getTaskParams(), LockParameters.class);}@Overridepublic ResourceParametersHelper getResources(String parameters) {return null;}
}

五、创建 Task 实现

通过 TaskChannel 我们得到了可执行的物理 Task,但是我们需要给当前 Task 添加相应的实现,才能够让 Apache DolphinScheduler 去执行你的任务,首先在编写 Task 之前我们需要先了解一下 Task 之间的关系

通过上图我们可以看到,基于 Yarn 执行任务的 Task 都会去继承 AbstractYarnTask,不需要经过 Yarn 执行的都会去直接继承 AbstractTaskExecutor,主要是包含一个 AppID,以及 CanalApplication setMainJar 之类的方法,想知道的小伙伴可以自己去深入研究一下,如上可知我们实现的 LockTask 就需要继承 AbstractTask,在构建 Task 之前,我们需要构建一下适配 LockTask 的 LockParameters 对象用来反序列化

这里其实主要根据自己的业务情况来增加需要的参数,顺便提醒下:如果自己在 DS 的上一层还有 SDK 封装的话,记得补齐这边对应的参数 TaskParams

package org.apache.dolphinscheduler.plugin.task.lock;/** Licensed to the Apache Software Foundation (ASF) under one or more* contributor license agreements.  See the NOTICE file distributed with* this work for additional information regarding copyright ownership.* The ASF licenses this file to You under the Apache License, Version 2.0* (the "License"); you may not use this file except in compliance with* the License.  You may obtain a copy of the License at**    http://www.apache.org/licenses/LICENSE-2.0** Unless required by applicable law or agreed to in writing, software* distributed under the License is distributed on an "AS IS" BASIS,* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.* See the License for the specific language governing permissions and* limitations under the License.*/import org.apache.dolphinscheduler.plugin.task.api.parameters.AbstractParameters;public class LockParameters extends AbstractParameters {private String key;private Long timeout;private Integer lockType;public Integer getLockType() {return lockType;}public void setLockType(Integer lockType) {this.lockType = lockType;}public String getKey() {return key;}public void setKey(String key) {this.key = key;}public Long getTimeout() {return timeout;}public void setTimeout(Long timeout) {this.timeout = timeout;}@Overridepublic boolean checkParameters() {// 创建 Task 时,会调用该方法进行参数校验return key != null && !key.isEmpty() && timeout != null && lockType != null;}
}

继续把常量类也提一嘴,这个就是在 Task 实现类里如需要用到一些常量可以在这里定义

package org.apache.dolphinscheduler.plugin.task.lock;/** Licensed to the Apache Software Foundation (ASF) under one or more* contributor license agreements.  See the NOTICE file distributed with* this work for additional information regarding copyright ownership.* The ASF licenses this file to You under the Apache License, Version 2.0* (the "License"); you may not use this file except in compliance with* the License.  You may obtain a copy of the License at**    http://www.apache.org/licenses/LICENSE-2.0** Unless required by applicable law or agreed to in writing, software* distributed under the License is distributed on an "AS IS" BASIS,* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.* See the License for the specific language governing permissions and* limitations under the License.*/public class LockConstants {public static final String LOG_TASK_NAME = "lock";
}

现在真的看 Task 实现类了……主要关注 handle 核心方法,这里如果有 redisson 相关报红的只需要注入下即可,当然这里因为不是 Bean 容器,所以需要从外面通过静态类单例模式来引入即可

package org.apache.dolphinscheduler.plugin.task.lock;/** Licensed to the Apache Software Foundation (ASF) under one or more* contributor license agreements.  See the NOTICE file distributed with* this work for additional information regarding copyright ownership.* The ASF licenses this file to You under the Apache License, Version 2.0* (the "License"); you may not use this file except in compliance with* the License.  You may obtain a copy of the License at**    http://www.apache.org/licenses/LICENSE-2.0** Unless required by applicable law or agreed to in writing, software* distributed under the License is distributed on an "AS IS" BASIS,* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.* See the License for the specific language governing permissions and* limitations under the License.*/import org.apache.dolphinscheduler.common.enums.LockType;
import org.apache.dolphinscheduler.common.redis.LockClient;
import org.apache.dolphinscheduler.common.utils.JSONUtils;
import org.apache.dolphinscheduler.plugin.task.api.*;
import org.apache.dolphinscheduler.plugin.task.api.parameters.AbstractParameters;
import org.apache.dolphinscheduler.plugin.task.lock.LockParameters;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import java.util.concurrent.TimeUnit;/*** lock task*/
public class LockTask extends AbstractTask {protected LockParameters lockParameters;protected TaskExecutionContext taskRequest;public LockTask(TaskExecutionContext taskRequest) {super(taskRequest);this.taskRequest = taskRequest;}@Overridepublic void init() {logger.info(LockConstants.LOG_TASK_NAME + " task params {}", taskRequest.getTaskParams());lockParameters = JSONUtils.parseObject(taskRequest.getTaskParams(), LockParameters.class);if (!lockParameters.checkParameters()) {throw new TaskException(LockConstants.LOG_TASK_NAME + " task params is not valid");}}@Overridepublic void handle(TaskCallBack taskCallBack) throws TaskException {try {run();} catch (Exception e) {logger.error(LockConstants.LOG_TASK_NAME + " task failure", e);setExitStatusCode(TaskConstants.EXIT_CODE_FAILURE);throw new TaskException("run " + LockConstants.LOG_TASK_NAME + " task error", e);}}/*** 核心处理* @param*/private void run() {Integer lockType = lockParameters.getLockType();if (lockType == LockType.LCOKED.getCode()) {lockHandle();} else if (lockType == LockType.UNLOCKED.getCode()) {unlockHandle();} else {setExitStatusCode(TaskConstants.EXIT_CODE_FAILURE);}}/*** 加锁处理* @param*/private void lockHandle() {boolean islock = false;RedissonClient redissonClient = LockClient.get();String key = lockParameters.getKey();Long timeout = lockParameters.getTimeout();RLock lock = redissonClient.getLock(key);try {islock = lock.tryLock(timeout, TimeUnit.SECONDS);setExitStatusCode(TaskConstants.EXIT_CODE_SUCCESS);} catch (Exception e) {throw new RuntimeException(e);} finally {if (!islock) {lock.forceUnlock();}}}/*** 解锁处理* @param*/private void unlockHandle() {RedissonClient redissonClient = LockClient.get();String key = lockParameters.getKey();RLock lock = redissonClient.getLock(key);if (lock.isLocked()) {lock.forceUnlock();}setExitStatusCode(TaskConstants.EXIT_CODE_SUCCESS);}@Overridepublic void cancel() throws TaskException {}@Overridepublic AbstractParameters getParameters() {return lockParameters;}
}

六、遵守 SPI 规则

方法一

(1)Resource下创建META-INF/services文件夹,创建接口全类名相同的文件

└── META-INF
    └── services
        └── org.apache.dolphinscheduler.spi.task.TaskChannelFactory

(2)在文件中写入实现类的全限定类名

org.apache.dolphinscheduler.plugin.task.lock.LockTaskChannelFactory

方法二(推荐)

使用上文一直提到的 @AutoService 注解,只要加在工厂类头上即可,注意别引入错了 package 是 google 旗下的。这样一来就会在编译的时候自动出现在 target 里

import com.google.auto.service.AutoService;@AutoService(TaskChannelFactory.class)
public class LockTaskChannelFactory implements TaskChannelFactory {…}

七、打包 & 部署

mvn clean install

Tips:当然在其他的 Api-Server 等其他 Xxx-Server 里,如果用到了该插件也是需要放在其路径下,重点在 worker-server 和 api-server,其余看情况。好了,本次教程到此结束~

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/73120.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

java文件命令行报错: 找不到或无法加载主类XXX报错及解决

前言 之前遇到过几次&#xff0c;后面稀里糊涂的解决了。今天详细记录一下&#xff0c;可能不全或有些错误&#xff0c;还请各位指正。 你要启动一个类的话首先要有类。 在这里&#xff0c;类有两种&#xff0c; 一个是带包名&#xff08;package&#xff09;的还有一个是没包…

算法刷题记录-双指针/滑动窗口(LeetCode)

809. Expressive Words 思路 根据题目描述&#xff0c;我们可以知道&#xff0c;如果要将某个单词定义为可扩张&#xff08;stretchy&#xff09;&#xff0c;需要满足如下两个条件&#xff1a; 所以&#xff0c;我们在实现的时候&#xff0c;可以通过两个指针p1和p2&#x…

Jmeter进阶使用指南-分布式测试

当你需要模拟大量并发用户并测试应用程序的性能时&#xff0c;JMeter的分布式测试功能非常有用。分布式测试允许你使用多个JMeter实例来模拟并发用户&#xff0c;从而提供更高的负载。 下面是一个详细的介绍和讲解分布式测试的步骤&#xff1a; 准备主机和从机&#xff1a; 首…

计算机竞赛 基于深度学习的植物识别算法 - cnn opencv python

文章目录 0 前言1 课题背景2 具体实现3 数据收集和处理3 MobileNetV2网络4 损失函数softmax 交叉熵4.1 softmax函数4.2 交叉熵损失函数 5 优化器SGD6 最后 0 前言 &#x1f525; 优质竞赛项目系列&#xff0c;今天要分享的是 &#x1f6a9; **基于深度学习的植物识别算法 ** …

Vue3---uni-app--高德地图引用BUG

先给报错信息&#xff1a;module libs/map//libs/map_min.js is not defined, require args is /libs/map_min.js 查看我引用方法&#xff1a; 本人查阅资料发现 是 require 使用的是 commonJS方式引用说这个适配Vue2可我项目是Vue3应该使用ES6语法糖 然后我有跑了项目发现BU…

蓝桥杯官网练习题(凑算式)

类似填空题&#xff1a; ①算式900&#xff1a; https://blog.csdn.net/s44Sc21/article/details/132746513?spm1001.2014.3001.5501https://blog.csdn.net/s44Sc21/article/details/132746513?spm1001.2014.3001.5501 ②九宫幻方③七星填数④幻方填空&#xff1a;https:/…

Competitive Collaboration 论文阅读

论文信息 题目&#xff1a;Competitive Collaboration: Joint Unsupervised Learning of Depth, Camera Motion, Optical Flow and Motion Segmentation 作者&#xff1a;Anurag Ranjan&#xff0c; Varun Jampani&#xff0c; Lukas Balles 来源&#xff1a;CVPR 时间&#x…

【 OpenGauss源码学习 —— 列存储(analyze)(四)】

列存储&#xff08;analyze&#xff09; AcquireSampleCStoreRows 函数es_get_attnums_to_analyze 函数CStoreRelGetCUNumByNow 函数CStore::GetLivedRowNumbers 函数InitGetValFunc 函数CStoreGetfstColIdx 函数CStore::GetCUDesc 函数CStore::IsTheWholeCuDeleted 函数CStore…

vue开发-语法和基础流程规范

当一个请求发生的时候 后端的扭转流程对标前端的扭转过程 ***vue 中 整体url触发整体流程服务流程&#xff1a; node 中定义了默认加载 vue.config.js 文件 &#xff08;vue.config.js 文件名不可更改&#xff09;-> vue.config.js 中 devServer 绑定了个 ip端口 和资源 -》…

Java版本企业工程行业管理系统源码

Java版工程项目管理系统 Spring CloudSpring BootMybatisVueElementUI前后端分离 功能清单如下&#xff1a; 首页 工作台&#xff1a;待办工作、消息通知、预警信息&#xff0c;点击可进入相应的列表 项目进度图表&#xff1a;选择&#xff08;总体或单个&#xff09;项目显示1…

初学Python记

Python这个编程语言的大名当然听说过了呀&#xff0c;这几年特别火&#xff0c;火的一塌涂地。大家可以回忆一下&#xff1a;朋友圈推荐的广告里经常可以看见python的网课广告。 本学期&#xff0c;学校开设了python课程&#xff0c;这几天学习了一下入了一下门&#xff0c;感…

OpenHarmony:如何使用HDF驱动控制LED灯

一、程序简介 该程序是基于OpenHarmony标准系统编写的基础外设类&#xff1a;RGB LED。 目前已在凌蒙派-RK3568开发板跑通。详细资料请参考官网&#xff1a;https://gitee.com/Lockzhiner-Electronics/lockzhiner-rk3568-openharmony/tree/master/samples/b02_hdf_rgb_led。 …

C++ 判断

C 判断 判断结构要求程序员指定一个或多个要评估或测试的条件&#xff0c;以及条件为真时要执行的语句&#xff08;必需的&#xff09;和条件为假时要执行的语句&#xff08;可选的&#xff09;。 下面是大多数编程语言中典型的判断结构的一般形式&#xff1a; 判断语句 C 编…

SpringMVC之前端增删改查实现

SpringMVC是一个基于MVC架构的框架&#xff0c;它可以帮助我们实现前后端的分离&#xff0c;同时也能很好地支持前端的增删改查操作。 配置 Cloudjun <?xml version"1.0" encoding"UTF-8" ?><taglib xmlns"http://java.sun.com/xml/ns/j…

TSINGSEE青犀/视频分析/边缘计算/AI算法·人员/区域入侵功能——多场景高效运用

TSINGSEE青犀视频人员/区域入侵功能可对重要区域进行实时监测&#xff0c;对监控区域进行7*24全天候管控&#xff0c;当监测到有人员靠近、闯入时&#xff0c;AI算法后台就会立即发出告警及时通知管理人员&#xff0c;变被动“监督”为主动“监控”&#xff0c;真正做到事前预警…

2023计算机毕业设计题目 毕设选题大全

文章目录 0 前言1 java web 管理系统 毕设选题2 java web 平台/业务系统 毕设选题3 游戏设计、动画设计类 毕设选题 (适合数媒的同学)4 算法开发5 数据挖掘 毕设选题6 大数据处理、云计算、区块链 毕设选题7 网络安全 毕设选题8 通信类/网络工程 毕设选题9 嵌入式 毕设选题10 开…

Newman+Jenkins实现接口自动化测试

一、是什么Newman Newman就是纽曼手机这个经典牌子&#xff0c;哈哈&#xff0c;开玩笑啦。。。别当真&#xff0c;简单地说Newman就是命令行版的Postman&#xff0c;查看官网地址。 Newman可以使用Postman导出的collection文件直接在命令行运行&#xff0c;把Postman界面化运…

uniapp项目实践总结(十三)封装文件操作方法

导语&#xff1a;在日常 APP 开发过程中&#xff0c;经常要进行文件的保存、读取列表以及查看和删除文件等操作&#xff0c;接下来就看一下具体的方法。 目录 原理分析方法实现实战演练案例展示 原理分析 主要是以下 API。 uni.saveFile&#xff1a;保存文件到本地缓存列表…

小白备战大厂算法笔试(四)——哈希表

文章目录 哈希表常用操作简单实现冲突与扩容链式地址开放寻址线性探测多次哈希 哈希表 哈希表&#xff0c;又称散列表&#xff0c;其通过建立键 key 与值 value 之间的映射&#xff0c;实现高效的元素查询。具体而言&#xff0c;我们向哈希表输入一个键 key &#xff0c;则可以…

K210-CanMV IDE开发软件

K210-CanMV IDE开发软件 界面功能简介连接设备临时运行开机运行程序 界面功能简介 区域①菜单栏&#xff1a;操作文件&#xff0c;使用工具等。 区域②快捷按钮&#xff1a;区域①中的文件和编辑中部分功能的快捷方式。 区域③连接设备&#xff1a;连接设备和程序控制按钮。 …