深入理解 Flink(五)Flink Standalone 集群启动源码剖析

news2025/2/25 6:19:03

前言

Flink 集群的逻辑概念:
JobManager(StandaloneSessionClusterEntrypoint) + TaskManager(TaskManagerRunner)
Flink 集群的物理概念:
ResourceManager(管理集群所有资源,管理集群所有从节点) + TaskExecutor(管理从节点资源,接收 Task 部署执行)
在 Flink 不同的部署模式下(Standalone、YARN、K8S 等)只是最外层的封装略有区别,实际运行的内核并无差异。因此本文以 Standalone 集群为例,剖析 Flink 集群的启动源码。

Flink 集群启动脚本分析

Flink 集群的启动脚本位于 flink-dist 子项目中,flink-bin 下的 bin 目录:

start-cluster.sh

根据具体组件的不同,脚本会按照以下流程执行:
在这里插入图片描述

Flink 主节点 StandaloneSessionClusterEntrypoint 启动源码分析

JobManager 是 Flink 集群的主节点,它包含三大重要的组件:
1、ResourceManager
Flink 的集群资源管理器,只有一个,关于 slot 的管理和申请等工作,都由它负责
2、DispatcherRunner
负责接收用户提交的 JobGragh, 然后启动一个 JobMaster, JobMaster 类似于 YARN 集群中的 AppMaster 角色,类似于 Spark Job 中的 Driver 角色。内部有一个持久服务:JobGraghStore,用来存储提交到 JobManager 中的 Job 的信息,也可以用作主节点宕机之后做 job 恢复之用。
3、WebMonitorEndpoint
里面维护了很多很多的 Handler,也还会启动一个 Netty 服务端,用来接收外部的 rest 请求。如果客户端通过 flink run 的方式来提交一个 job 到 flink 集群,最终是由 WebMonitorEndpoint 来接收处理,经过路由解析处理之后决定使用哪一个 Handler 来执行处理。Router 路由器 绑定了一大堆 Handler,例如:submitJob ===> JobSubmitHandler。

这里简单说明一下 Flink 的资源管理架构,后续章节会展开详述:
ResourceManager: 全局资源管理者 => SlotManager
JobMaster: 资源使用者 => SlotPool
TaskExecutor:资源提供者 => TaskSlotTable
以上三者的内部,都有一个专门用来做 slot 管理的一个组件。对应的要启动这三个组件,都有一个对应的 Factory,也就说,如果需要创建这些组件实例,那么都是通过这些 Factory 来创建。而这三个 Facotry 最终都会被封装在一个 ComponentFactory 中。

StandaloneSessionClusterEntrypoint main 方法

// 入口,解析命令行参数 和 配置文件 flink-conf.yaml
StandaloneSessionClusterEntrypoint.main(){
    ClusterEntrypoint.runClusterEntrypoint(entrypoint){
        // 启动插件组件,配置文件系统实例等
        clusterEntrypoint.startCluster(){
            runCluster(configuration, pluginManager){
                // 第一步:初始化各种服务(8个基础服务)
                // 比较重要的:HAService,BlobServer, RpcServices, HeatbeatServices,....
                initializeServices(configuration, pluginManager);
                // 第二步:创建 DispatcherResourceManagerComponentFactory, 初始化各种组件的工厂实例
                // 其实内部包含了三个重要的成员变量:
                // 创建 ResourceManager 的工厂实例
                // 创建 DispatcherRunner 的工厂实例
                // 创建 WebMonitorEndpoint 的工厂实例
                createDispatcherResourceManagerComponentFactory(configuration);
                // 第三步:创建 集群运行需要的一些组件:WebMonitorEndpoint,DispatcherRunner, ResourceManager 等
                // 创建和启动 ResourceManager
                // 创建和启动 DispatcherRunner
                // 创建和启动 WebMonitorEndpoint
                clusterComponent = dispatcherResourceManagerComponentFactory.create(...);
            }
        }
    }
}

基础服务组件初始化

initializeServices(){
    // 初始化和启动 AkkaRpcService,内部其实包装了一个 ActorSystem
    commonRpcService = AkkaRpcServiceUtils.createRemoteRpcService(...);
    // 启动一个 JMXService,用于客户端链接 JobManager JVM 进行监控
    JMXService.startInstance(configuration.getString(JMXServerOptions.JMX_SERVER_PORT));
    // 初始化一个负责 IO 的线程池, Flink 大量使用了 异步编程。
    // 这个线程池的线程的数量,默认是:cpu core 个数 * 4
    ioExecutor = Executors.newFixedThreadPool(...);
    // 初始化 HA 服务组件,负责 HA 服务的是:ZooKeeperHaServices
    haServices = createHaServices(configuration, ioExecutor);
    // 初始化 BlobServer 服务端
    blobServer = new BlobServer(configuration, haServices.createBlobStore());
    blobServer.start();
    // 初始化心跳服务组件, heartbeatServices = HeartbeatServices
    heartbeatServices = createHeartbeatServices(configuration);
    // 启动 metrics(性能监控) 相关的服务,内部也是启动一个 ActorSystem
    MetricUtils.startRemoteMetricsRpcService(configuration, commonRpcService.getAddress());
    // 初始化一个用来存储 ExecutionGraph 的 Store, 实现是:FileArchivedExecutionGraphStore
    archivedExecutionGraphStore = createSerializableExecutionGraphStore(...);
}

重要组件工厂实例初始化

DispatcherRunnerFactory,默认实现:DefaultDispatcherRunnerFactory,生产 DefaultDispatcherRunner
ResourceManagerFactory,默认实现:StandaloneResourceManagerFactory,生产 StandaloneResourceManager
RestEndpointFactory,默认实现:SessionRestEndpointFactory,生产 DispatcherRestEndpoint

在这里插入图片描述

三大重要组件初始化

Flink 源码中,三大重要组件初始化按照一下流程进行:
在这里插入图片描述

三大重要组件初始化源码解析

WebMonitorEndpoint 启动和初始化源码剖析

核心入口:

DispatcherResourceManagerComponentFactory.create(...)

启动流程:

  1. 初始化一大堆 Handler 和 一个 Router,并且进行排序去重,之后,再把每个 Handler 注册 到 Router 当中。
  2. 启动一个 Netty 的服务端。
  3. 启动内部服务:执行竞选。WebMonitorEndpoint 本身就是一个 LeaderContender 角色。如果竞选成功,则回调 isLeader() 方法。
  4. 竞选成功,其实就只是把 WebMontiroEndpoint 的 address 以及跟 zookeeper 的 sessionID 写入到 znode 中。
  5. 启动一个关于 ExecutionGraph 的 Cache 的定时清理任务。

ResourceManager 启动和初始化源码剖析

核心入口:

DispatcherResourceManagerComponentFactory.create(...)

启动流程:

1、ResourceManager 是 RpcEndpoint 的子类,所以在构建 ResourceManager 对象完成之后,肯定会调用 start() 方法来启动这个 RpcEndpoint,然后就跳转到它的 onStart() 方法执行。
2、ResourceManager 是 LeaderContender 的子类,会通过 LeaderElectionService 参加竞选,如果竞选成功,则会回调 isLeader() 方法。
3、启动 ResourceManager 需要的一些服务:
    两个心跳服务
        ResourceManager 和 TaskExecutor 之间的心跳
        ResourceManager 和 JobMaster 之间的心跳
    两个定时服务
        checkTaskManagerTimeoutsAndRedundancy() 检查 TaskExecutor 的超时
        checkSlotRequestTimeouts() 检查 SlotRequest 超时

在这里插入图片描述

DispatcherRunner 启动和初始化源码剖析

核心入口:

DispatcherResourceManagerComponentFactory.create(...)

启动流程:

1、启动 JobGraphStore 服务
2、从 JobGraphStrore 恢复执行 Job, 要启动 Dispatcher

从节点 TaskManagerRunner 启动源码分析

TaskManager 是 Flink 的 worker 节点,负责 Flink 中本机 slot 资源的管理以及具体 task 的执行。
TaskManager 上的基本资源单位是 slot,一个作业的 task 最终会部署在一个 TaskManager 的 slot 上运行,TaskManager 会负责维护本地的 slot 资源列表,并与 Flink Master 和 JobMaster 通信。

// 核心启动入口
TaskManagerRunner.main(args){
    runTaskManagerSecurely(args, ResourceID.generate()){
    // 加载配置:解析 args 和 flink-conf.yaml 得到配置信息
    Configuration configuration = loadConfiguration(args);
    // 启动 TaskManager
    // 在Flink 当中,所有的组件(跟资源有关)都有一个 ResourceID
    // 后续还会见到很多的类似的ID的概念:AllocationID
    runTaskManagerSecurely(configuration, resourceID){
        // 启动 TaskManager
        // 这个具体实现是:首先初始化 TaskManagerRunner, TaskManager 启动中,要初始化的一些服务,都是在这个构造方法里面!
        // 最后,再调用 TaskManagerRunner.start() 来启动,然后跳转到 TaskExecutor 的 onStart() 开启注册。
        runTaskManager(configuration, resourceID, pluginManager){
        // 第一步:构建 TaskManagerRunner 实例
        // 具体实现中也做了两件事:
        // 第一件事: 初始化了一个 TaskManagerServices 对象! 其实这个动作就类似于 JobManager 启动的时候的第一件大事(启动8个服务)
        // 第二件是: 初始化 TaskExecutor(Standalone 集群中提供资源的角色,ResourceManager 其实就是管理集群中的从节点的管理角色)
        // TaskExecutor 它是一个 RpcEndpoint,意味着,当 TaskExecutor 实例构造完毕之后,启动 RPC 服务就会跳转到 onStart() 方法
        taskManagerRunner = new TaskManagerRunner(...){
            // 初始化一个线程池 ScheduledThreadPoolExecutor 用于处理回调
            this.executor = Executors.newScheduledThreadPool(....)
            // 获取高可用模式:ZooKeeperHaServices
            highAvailabilityServices = HighAvailabilityServicesUtils.createHighAvailabilityServices(...)
            // 初始化 JMXServer 服务
            JMXService.startInstance(configuration.getString(JMXServerOptions.JMX_SERVER_PORT));
            // 创建 RPC 服务
            rpcService = createRpcService(configuration, highAvailabilityServices);
            // 创建心跳服务
            heartbeatServices = HeartbeatServices.fromConfiguration(conf);
            // 创建 BlobCacheService,内部会启动两个定时任务:PermanentBlobCleanupTask 和 TransientBlobCleanupTask
            blobCacheService = new BlobCacheService(....);
            // 创建 TaskExecutorService,内部其实就是创建 TaskExecutor 并且启动,详细内容如下一部分阐述。
            taskExecutorService = taskExecutorServiceFactory.createTaskExecutor(....){
                // 创建 TaskExecutorToServiceAdapter,内部封装 TaskExecutor,它是 TaskManagerRunner 的成员变量
                TaskManagerRunner::createTaskExecutorService;
                }
            }
                // 第二步:启动 TaskManagerRunner,然后跳转到 TaskExecutor 中的 onStart() 方法
                taskManagerRunner.start(){
                    taskExecutor.start();
                }
            }
        }
    }
}

TaskManager/TaskExecutor 注册

TaskManager 是一个逻辑抽象,代表一台服务器,这台服务器的启动,必然会包含一些服务,另外再包含一个 TaskExecutor,存在于 TaskManager 的内部,真实的帮助 TaskManager 完成各种核心操作,比如:

1、部署和执行 StreamTask
2、管理和分配 slot

监听和获取 ResourceManager 的地址

核心入口为:resourceManagerLeaderRetriever 的 start() 方法,具体实现方式见前面章节:
https://blog.csdn.net/weixin_44512041/article/details/135493920
在注册监听之后,如果发生了对应的事件,则会收到一个响应,然后回调:

ResourceManagerLeaderListener.notifyLeaderAddress();

内部详细实现:

// 关闭原有的 ResouceManager 的链接
closeResourceManagerConnection(cause);
// 开启注册超时的延时调度任务
startRegistrationTimeout();
// 当前 TaskExecutor 完成和 ResourceManager 的链接
tryConnectToResourceManager();

最重要的是第三步,TaskExecutor 和 ResourceManager 建立连接,会进行注册,心跳,Slot 汇报 三件大事。

TaskExecutor 开始注册

核心入口:

TaskExecutorToResourceManagerConnection.start();

TaskExecutor 注册失败

核心入口:

TaskExecutorToResourceManagerConnection.onRegistrationFailure(failure);

TaskExecutor 注册成功

核心入口:

TaskExecutorToResourceManagerConnection.onRegistrationSuccess(result.f1);

TaskExecutor 进行 Slot 汇报

当注册成功,ResourceManager 会返回 TaskExecutorRegistrationSuccess 对象。然后回调下面的方法,进入到 slot 汇报的过程。

TaskExecutorToResourceManagerConnection.onRegistrationSuccess(TaskExecutorRegistrationSuccess success);
    // 继续回调
    ResourceManagerRegistrationListener.onRegistrationSuccess(this, success);
        // 封装链接对象
        establishResourceManagerConnection(resourceManagerGateway, resourceManagerId, taskExecutorRegistrationId, ....);
            // 内部实现
            resourceManagerGateway.sendSlotReport(
            getResourceID(),
            taskExecutorRegistrationId,
            taskSlotTable.createSlotReport(getResourceID()), taskManagerConfiguration.getTimeout()
            );

TaskExecutor 和 ResourceManager 心跳

Flink 中 ResourceManager、JobMaster、TaskExecutor 三者之间存在相互检测的心跳机制,ResourceManager 会主动发送请求探测 JobMaster、TaskExecutor 是否存活,JobMaster 也会主动发送请求探测 TaskExecutor 是否存活,以便进行任务重启或者失败处理。
假定心跳系统中有两种节点:sender 和 receiver。心跳机制是 sender 和 receivers 彼此相互检测。但是检测动作是 Sender 主动发起,即 Sender 主动发送请求探测 receiver 是否存活,因为 Sender 已经发送过来了探测心跳请求,所以这样 receiver 同时也知道 Sender 是存活的,然后 Reciver 给 Sender 回应一个心跳表示自己也是活着的。具体表现:

  • Flink Sender 主动发送 Request 请求给 Receiver,要求 Receiver 回应一个心跳;
  • Flink Receiver 收到 Request 之后,通过 Receive 函数回应一个心跳请求给 Sender;
    在这里插入图片描述

ResourceManager 端心跳服务启动

ResourceManager 在初始化的最后,执行了:

ResourceManager.startHeartbeatServices();

启动了两个心跳服务:

// 维持 TaskExecutor 和 ResourceManager 之间的心跳
taskManagerHeartbeatManager = heartbeatServices.createHeartbeatManagerSender(resourceId, new TaskManagerHeartbeatListener(),
getMainThreadExecutor(), log);
// 维持 JobMaster 和 ResourceManager 之间的心跳
jobManagerHeartbeatManager = heartbeatServices.createHeartbeatManagerSender(resourceId, new JobManagerHeartbeatListener(),
getMainThreadExecutor(), log);

具体是构造了一个 HeartbeatManagerSenderImpl 实例对象,并且调用了:

mainThreadExecutor.schedule(this, 0L, TimeUnit.MILLISECONDS);

heartbeatMonitor 内部封装了一个 heartbeatTarget,对于 ResourceManager 来说,每个注册成功的 TaskExecutor 都会被构建成一个 HeartbeatTarget ,然后构建成一个 heartbeatMonitor。这个可以在 ResourceManager 端完成 TaskExecutor 注册的时候进行验证。
当 ResourceManager 端完成一个 TaskExecutor 的注册的时候,马上调用:

// 维持心跳
taskManagerHeartbeatManager.monitorTarget(taskExecutorResourceId, new HeartbeatTarget<Void>() {
    @Override
    public void receiveHeartbeat(ResourceID resourceID, Void payload) {
    }
    @Override
    public void requestHeartbeat(ResourceID resourceID, Void payload) {
        // 给 TaskExecutor 发送心跳请求
        taskExecutorGateway.heartbeatFromResourceManager(resourceID);
    }
});

这样子,刚才注册的 TaskExecutor 就先被封装成一个 HeartbeatTarget, 然后被加入到 taskManagerHeartbeatManager 进行管理的时候,变成了 HeartbeatMonitor。当这句代码完成执行的时候,当前 ResourceManager 的心跳目标对象,就多了一个 TaskExecutor,然后当执行:

taskExecutorGateway.heartbeatFromResourceManager(resourceID);

就给 TaskExecutor 发送了一个心跳请求。

TaskExecutor 端心跳处理

当 TaskExecutor 接收到 ResourceManager 的心跳请求之后,进入内部实现:

TaskExecutor.heartbeatFromResourceManager(ResourceID resourceID);
    // 内部实现
    resourceManagerHeartbeatManager.requestHeartbeat(resourceID, null);
        // 内部实现
        reportHeartbeat(requestOrigin);
            // 第一件事:进行心跳报告
            heartbeatMonitor.reportHeartbeat();
            // 记录最后一次的心跳时间
            lastHeartbeat = System.currentTimeMillis();
            // 重设心跳超时相关的 时间 和 延迟调度任务
            resetHeartbeatTimeout(heartbeatTimeoutIntervalMs);
            // 先取消
            cancelTimeout();
            // 再重新调度
            futureTimeout = scheduledExecutor.schedule(this, heartbeatTimeout, TimeUnit.MILLISECONDS);
        // TaskExecutor 进行负载汇报
        heartbeatTarget.receiveHeartbeat(.....);
            // 给 ResourceManager 回复 TaskExecutor 的负载。
            resourceManagerGateway.heartbeatFromTaskManager(resourceID, heartbeatPayload);

如果连续 5 次心跳请求没有收到,也就是说,如果 50s 内都没有收到心跳请求,则执行心跳超时处理。

heartbeatListener.notifyHeartbeatTimeout(resourceID);

超时处理也非常的暴力有效,Flink 认为: 如果 TaskExecutor 收不到 ResourceManager 的心跳请求了,则认为当前 ResourceManager 死掉了。但是 Flink 集群肯定会有一个 active 的 ResourceManager 节点的。而且之前也注册过监听,如果 Flink HA 集群的 Active 节点发生迁移,则 TaskExecutor 也一定已经收到过通知了,然后现在需要做的,只是重新链接到新的 active ResourceManager 即可。

reconnectToResourceManager(
    new TaskManagerException(String.format("The heartbeat of ResourceManager with id %s timed out.", resourceId))
);

TaskExecutor 向 ResourceManager 汇报负载

核心入口:HeartBeatManagerImpl 的 requestHeartbeat() 方法的最后一句代码:

heartbeatTarget.receiveHeartbeat(getOwnResourceID(), heartbeatListener.retrievePayload(requestOrigin));

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

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

相关文章

Oracle 19c OCP 1z0 082考场真题解析第27题

考试科目&#xff1a;1Z0-082 考试题量&#xff1a;90 通过分数&#xff1a;60% 考试时间&#xff1a;150min本文为云贝教育郭一军&#xff08;微信&#xff1a;guoyJoe&#xff09;原创&#xff0c;请尊重知识产权&#xff0c;转发请注明出处&#xff0c;不接受任何抄袭、演绎…

道路拆除的题解

目录 原题描述&#xff1a; 题目描述 输入格式 输出格式 样例 #1 样例输入 #1 样例输出 #1 样例 #2 样例输入 #2 样例输出 #2 提示 题目大意&#xff1a; 主要思路&#xff1a; 至于dis怎么求&#xff1f; 代码code&#xff1a; 原题描述&#xff1a; 题目描述 …

我为什么注销了知乎的账号

在谈论这个话题前&#xff0c;大家先看看这篇知乎的讨论&#xff01;回答感觉比较经典&#xff01; 我大概有几年没上知乎了&#xff0c;因为一直感觉知乎上是一群爱讨论假大空理论的群体或者一群无聊的杠精&#xff01;与咱这撸袖子埋头干的人不合拍&#xff01;昨天没事上了一…

出版实务 | 出版物的成本及其构成

文章目录 出版物成本的总体构成直接成本开发成本制作成本 间接成本期间费用 本量利分析原则特点和作用变动成本项目固定成本项目本量利分析的基本公式及其应用定价发行折扣率销售数量单位销售收入销售收入总额单位销售税金销售税金总额变动成本总额单位变动成本固定成本总额单位…

如何对制作好的查询进行编辑和导出?

发布者已经创建好了查询&#xff0c;如发现数据有误&#xff0c;想要进行修改&#xff0c;或者想要将收集好的表格进行导出&#xff0c;应该如何操作&#xff1f;本次就来介绍如何使用此功能。 &#x1f4d6;案例&#xff1a;教师荣誉核对系统 在开启可修改列功能的教师荣誉核对…

IO进程线程day5

1.实现互斥机制 #include <head.h>char buf[128]; //全局数组&#xff0c;临界资源//1、创建一个互斥锁 pthread_mutex_t mutex;//定义分支线程 void *task(void *arg) {while(1){//3、获取锁资源pthread_mutex_lock(&mutex);printf("分支线程中&…

Oracle-探究统计信息收集自动采样AUTO_SAMPLE_SIZE

前言&#xff1a; Oracle数据库进行统计信息收集时&#xff0c;可以通过ESTIMATE_PERCENT参数指定采样方式或者比例&#xff0c;有以下4种指定的方式 1 统计信息收集时不指定值&#xff0c;这时候ESTIMATE_PERCENT值为默认值DBMS_STATS.AUTO_SAMPLE_SIZE&#xff0c;自动采样 …

ApolloCarla联合仿真基本操作

Apollo 系统架构 CANBus&#xff1a;对接车辆的底盘&#xff0c;做一些数据的收发&#xff0c;如油门&#xff0c;方向盘转角 HDMap&#xff1a; 给localization提供定位图层的信息给perception一些车道线、道路拓扑、红绿灯的信息&#xff08;超时空感知&#xff09;&#x…

一台Linux服务jdk1.6 与 jdk1.8 并存,tomcat6+tomcat8 并存

Linux jdk1.6,1.8 tomcat6 tomcat8 并存 需求场景&#xff1a; 有一个项目 原来是 jdk1.6tomcat6 部署的&#xff0c;现在需要进行项目架构升级 项目需要适配jdk1.8 然后用 jdk.8 tomcat 8进行部署&#xff0c;然后下架 jdk1.6 的linux服务 现在有一台 jdk.8 tomcat 8的linu…

【办公软件】手机当电脑摄像头Iriun Webcam软件安装与试用

家里电脑是台式的没有摄像头&#xff0c;但老安卓手机有一台。本来想用小爱摄像头做电脑摄像头&#xff0c;但是发现像素有点差&#xff0c;捣鼓了半天没成功。看网上别人都用旧手机来当电脑摄像头&#xff0c;并且也能使用音频&#xff0c;所以还是用旧手机做摄像头比较香。 …

Java 常见缓存详解以及解决方案

一. 演示Mybatis 一级缓存 首先我们准备一个接口 两个实现的方法&#xff0c; 当我们调用这个queryAll&#xff08;&#xff09;方法时我们需要调用selectAll&#xff08;&#xff09;方法来查询数据 调用此接口实现效果 这个时候我们就可以发现了问题&#xff0c;我们调用方法…

车速预测 | Matlab基于RBF径向基神经网络的车速预测模型(多步预测,尾巴图)

目录 效果一览基本介绍程序设计参考资料 效果一览 基本介绍 车速预测 | Matlab基于RBF径向基神经网络的车速预测模型&#xff08;多步预测&#xff0c;尾巴图&#xff09; 程序设计 完整程序和数据获取方式&#xff1a;私信博主回复Matlab基于RBF径向基神经网络的车速预测模型…

电脑重装系统有什么好处和坏处?利弊分析

在数字时代&#xff0c;电脑已成为我们日常生活和工作中不可或缺的工具。随着时间的推移&#xff0c;电脑可能会出现各种问题&#xff0c;如运行缓慢、软件冲突、病毒侵入等。此时&#xff0c;许多人会选择重装系统作为解决问题的方法。重装系统既有好处也有坏处。本文将深入探…

GC6109——双通道5V低电压步进电机驱动芯片,低噪声、低振动,应用摄像机,机器人等产品中

GC6109是双通道5V低电压步进电机驱动器&#xff0c;具有低噪声、低振动的特点&#xff0c;特别适用于相机的变焦和对焦系统&#xff0c;万向节和其他精密、低噪声的STM控制系统。该芯片为每个通道集成了256微步驱动器。带SPl接口&#xff0c;用户可以方便地调整驱动器的参数。内…

Java版直播商城:电商源码、小程序、三级分销及 免 费 搭 建 方案

一、技术选型 java开发语言&#xff1a;java是一种跨平台的编程语言&#xff0c;适用于大型企业级应用开发。使用java开发直播商城可以保证系统的稳定性和可扩展性。 spring boot框架&#xff1a;spring boot是一个快速构建spring应用的框架&#xff0c;简化了开发过程&#xf…

Elasticsearch windows开箱即用【记录】

一、准备工作 安装ES之前要在本机安装好JDK&#xff0c;对应的兼容性见官网链接&#xff1a;https://www.elastic.co/cn/support/matrix ES官网链接&#xff1a;https://www.elastic.co/cn/, 我本机安装的是JDK8&#xff0c;测试使用的是7.3.0版本的ES和Kibana。 1、首先去…

【2024最新-python3小白零基础入门】No3.python六大数据类型学习

文章目录 前言一、 Number&#xff08;数字&#xff09;1.1 数字类型分类1.2 数字类型转换1.3 数字运算 二、 String&#xff08;字符串&#xff09;2.1 字符串的创建&#xff1a; 2.2 字符串的基本操作&#xff1a;2.3 字符串常用方法&#xff1a; 三、 List&#xff08;列表&…

Vulnhub靶机:Corrosion 2

一、介绍 运行环境&#xff1a;Virtualbox 攻击机&#xff1a;kali&#xff08;10.0.2.15&#xff09; 靶机&#xff1a;corrosion:2&#xff08;10.0.2.13&#xff09; 目标&#xff1a;获取靶机root权限和flag 靶机下载地址&#xff1a;https://www.vulnhub.com/entry/c…

Python教程39:使用turtle画美国队长盾牌

---------------turtle源码集合--------------- Python教程36&#xff1a;海龟画图turtle写春联 Python源码35&#xff1a;海龟画图turtle画中国结 Python源码31&#xff1a;海龟画图turtle画七道彩虹 Python源码30&#xff1a;海龟画图turtle画紫色的小熊 Python源码29&a…

大创项目推荐 深度学习手势检测与识别算法 - opencv python

文章目录 0 前言1 实现效果2 技术原理2.1 手部检测2.1.1 基于肤色空间的手势检测方法2.1.2 基于运动的手势检测方法2.1.3 基于边缘的手势检测方法2.1.4 基于模板的手势检测方法2.1.5 基于机器学习的手势检测方法 3 手部识别3.1 SSD网络3.2 数据集3.3 最终改进的网络结构 4 最后…