浅尝Apache Mesos

文章目录

    • 1. Mesos是什么
    • 2. 共享集群
    • 3. Apache Mesos
      • 3.1 Mesos主节点
      • 3.2 Mesos代理
      • 3.3 Mesos框架
    • 4. 资源管理
      • 4.1 资源提供
      • 4.2 资源角色
      • 4.3 资源预留
      • 4.4 资源权重与配额
    • 5. 实现框架
      • 5.1 框架主类
      • 5.3 实现执行器
    • 6. 小结
    • 参考

1. Mesos是什么

Mesos是什么,Mesos是一个分布式的系统内核。

Mesos的构建原理与Linux内核相同,只是抽象级别不同。Mesos的内核在每台机器上运行并为应用程序(例如Hadoop、Spark、Kafka、ElasticSearch)提供API跨整个数据中心和云环境的资源管理和调度。

Mesos主要有以下特性:

  1. 线性可扩展
    1. 经过业界验证,可以轻松扩展到数万个节点
  2. 高可用性
    1. 使用Zookeeper实现主服务器和代理服务器容错副本,无中断升级
  3. 容器化
    1. 原生支持使用Docker和AppC镜像启动容器
  4. 可插拔隔离
    1. 对CPU、内存、磁盘、端口、GPU和自定义资源隔离模块的一流隔离支持
  5. 两级调度
    1. 支持使用可插入的调度策略在同一集群中运行云原生和遗留应用程序
  6. 接口
    1. 用于开发新的分布式应用程序、操作集群和监控的HTTP API
  7. Web UI
    1. 内置Web UI,用于查看集群状态和容器沙箱导航
  8. 跨平台
    1. 可在Linux、OSX和Windows上运行,与云服务商无关

我们通常会将各种应用程序部署在同一台机器集群上。而Apache Mesos是一个允许此类程序之间共享资源的平台。

2. 共享集群

许多应用程序需要共享集群,总的来说有两种常见的方法:

  1. 对集群静态分区并在每个分区上运行一个应用程序
  2. 为应用程序分配一组机器

上述方法虽然允许应用程序彼此独立运行,但并不能实现较高的资源利用率:比如某个应用程序只运行了一小段时间,之后处于非活动状态,由于我们为该应用程序分配了静态机器或分区,因此在非活动状态期间,存在未使用的资源。

我们可以通过将非活动状态期间的共享资源重新分配给其他应用程序来优化资源利用率,而Apache Mesos则可以帮助应用程序之间的资源动态分配。

3. Apache Mesos

基于我们上面讨论的两种共享集群的方法,应用程序只知道他们正在运行的特定分区或机器的资源,然而Apache Mesos为应用程序提供了集群中所有资源的抽象视图。

Mesos会充当机器与应用程序之间的接口,为应用程序提供集群中所有机器上的可使用资源,它会经常更新此信息从而获取完成状态的应用程序释放的资源。它允许应用程序做出关于哪台机器上执行哪个任务的最佳决定。

为了理解Mesos的工作原理,我们来看下它的架构:

Mesos架构

上图展示了Mesos的主要组件,Mesos由一个管理运行在每个集群节点上的代理守护进程和主守护进程以及在这些代理上运行任务的Mesos框架组成。

主节点通过提供资源实现跨框架的细粒度资源共享(CPU、RAM等)。每个提供的资源包含一个列表<agent ID, resource1: amount1, resource2: amount2, ...>,主节点根据给定的组织策略(例如公平共享和严格优先级)决定向每个框架提供多少资源。为了支持多样化的策略集,主节点才用模块化架构,可通过插件机制来轻松添加新的分配模块。

在Mesos上运行的框架由两个组件组成:

  1. 向主节点注册以获取资源的调度程序
  2. 在代理节点上启动以运行框架任务的执行程序进程

主节点确定向每个框架提供多少资源,而框架的调度程序则选择使用提供资源中的哪些资源。当框架接受提供的资源时,它会将要在这些资源上运行的任务的描述传递给Mesos,反过来,Mesos会在相应的代理上启动任务。

3.1 Mesos主节点

Master是Mesos中的核心组件,用于存储集群中资源的当前状态,此外,它还通过传递有关资源和任务等信息,从而充当代理和应用程序之间的协调器。

由于主服务器发生任何故障都会导致资源和任务状态丢失,因此我们将其部署在高可用性配置中。如上图所示,Mesos部署了主服务器守护进程和一个Master,这些守护进程依靠Zookeeper在发生故障时恢复状态。

3.2 Mesos代理

Mesos集群必须在每台机器上运行代理,这些代理定期向主节点报告其资源,并依次接收应用程序已经安排运行的任务。在计划任务完成或者丢失后,次循环将重复。

3.3 Mesos框架

Mesos允许应用程序实现一个抽象组件,该组件与Master交互以接收集群中的可用资源,并根据这些资源做出调度决策,这些组件成为框架。

Mesos的框架由两个子组件组成:

  • 调度程序:使应用程序能够根据所有代理上的可用资源来调度任务
  • 执行器:在所有代理上运行,并包含该代理上执行任务计划任务所需的所有信息

框架安排任务运行的示例图:

Mesos框架安排任务运行示意

  1. 代理1向主服务器报告其有4个CPU和4GB的可用内存,主服务器随后调度分配策略模块,告知其应该为框架1提供所有可用资源。
  2. 主服务器向框架1发送资源邀约,描述代理1有哪些可用资源
  3. 框架的调度程序向主服务器回复在代理上运行的两个任务的信息,第一个任务使用<2CPU,1GB RAM>,第二个任务使用<1CPU,2GB RAM>
  4. 最后主服务器将任务发送给代理,代理将适当的额资源分配给框架的执行器,执行器则启动这两个任务(图中虚线所示),由于仍然存在1CPU和1GB RAM未分配,因此分配模块现在可以将它提供给框架2.

此外,当任务完成且新资源变为空闲时,此资源提供过程会重复。

Mesos允许应用程序使用各种编程语言实现自定义调度程序和执行程序。按照Mesos-go中的Scheduler接口的定义如下,具体可参考:Mesos Scheduler interface

// Scheduler a type with callback attributes to be provided by frameworks
// schedulers.
//
// Each callback includes a reference to the scheduler driver that was
// used to run this scheduler. The pointer will not change for the
// duration of a scheduler (i.e., from the point you do
// SchedulerDriver.Start() to the point that SchedulerDriver.Stop()
// returns). This is intended for convenience so that a scheduler
// doesn't need to store a reference to the driver itself.
type Scheduler interface {Registered(SchedulerDriver, *mesos.FrameworkID, *mesos.MasterInfo)Reregistered(SchedulerDriver, *mesos.MasterInfo)Disconnected(SchedulerDriver)ResourceOffers(SchedulerDriver, []*mesos.Offer)OfferRescinded(SchedulerDriver, *mesos.OfferID)StatusUpdate(SchedulerDriver, *mesos.TaskStatus)FrameworkMessage(SchedulerDriver, *mesos.ExecutorID, *mesos.SlaveID, string)SlaveLost(SchedulerDriver, *mesos.SlaveID)ExecutorLost(SchedulerDriver, *mesos.ExecutorID, *mesos.SlaveID, int)Error(SchedulerDriver, string)
}

从上述接口的定义可以看出,它主要由各种回调方法组成,主要用于与主机通信。

同样,执行器的实现也需要实现Executor接口,具体可参考:Mesos Executor interface

/*** Executor callback interface to be implemented by frameworks' executors. Note* that only one callback will be invoked at a time, so it is not* recommended that you block within a callback because it may cause a* deadlock.** Each callback includes an instance to the executor driver that was* used to run this executor. The driver will not change for the* duration of an executor (i.e., from the point you do* ExecutorDriver.Start() to the point that ExecutorDriver.Join()* returns). This is intended for convenience so that an executor* doesn't need to store a pointer to the driver itself.*/
type Executor interface {Registered(ExecutorDriver, *mesosproto.ExecutorInfo, *mesosproto.FrameworkInfo, *mesosproto.SlaveInfo)Reregistered(ExecutorDriver, *mesosproto.SlaveInfo)Disconnected(ExecutorDriver)LaunchTask(ExecutorDriver, *mesosproto.TaskInfo)KillTask(ExecutorDriver, *mesosproto.TaskID)FrameworkMessage(ExecutorDriver, string)Shutdown(ExecutorDriver)Error(ExecutorDriver, string)
}

4. 资源管理

4.1 资源提供

上面说到代理节点会将其资源信息发布给主服务器,反过来主服务器将这些资源提供给集群中运行的框架,此过程成为资源提供。

资源信息一般由两部分组成:

  1. 资源
    1. 资源用于发布代理机器的硬件信息,例如CPU、内存、磁盘等
  2. 属性

每个代理有五种预定义资源:

  • 中央处理器
  • 图形处理器
  • 内存
  • 磁盘
  • 端口

这些资源的值可以用以下三种类型之一来定义:

  • 标量:用浮点数来表示数值信息,以允许小数值,例如1.5G内存
  • 范围:用于表示标量值的范围,例如端口号范围
  • 集合:用于表示多个文本值

默认情况下,Mesos代理会尝试从机器检测这些资源。但在某些情况下我们可以在代理上配置自定义资源,此类自定义资源的值,也应为上述任何一种类型。

例如,我们可以使用以下这些资源启动我们的代理:

--resources='cpus:24;gpus:2;mem:2048;disk:40960;ports:[21000-24000];bugs(debug_role):{a,b,c}'

上面我们为代理配置了一些预定义资源和一个名为bugs集合类型的自定义资源。

除了资源之外,代理还可以向主服务器发布键值属性,这些属性充当代理的附加元数据,并帮助框架进行调度决策。

一个有用的例子是将带来添加到不同的机架或者区域,然后在同一个机架或者区域上安排各种任务以实现数据本地化

--attributes='rack:abc;zone:sg;os:centos5;level:10;keys:[1000-1500]'

与资源类似,属性的值可以是标量、范围或者文本类型。

4.2 资源角色

现代操作系统许多都支持多用户,同样,Mesos也支持同一个集群中的多个用户,这些用户被称为角色。我们可以将每个决策视为集群中的资源消费者。

由此,Mesos代理可以基于不同的分配策略对不同角色下的资源进行划分,而框架可以在集群内订阅这些角色,从而实现对不同角色下的资源进行细粒度的控制。

例如,假设有一个集群托管应用程序,为组织中不同用户提供服务,因此,通过将资源划分成角色,每个应用程序都可以彼此独立地工作。

此外,框架可以使用这些角色来实现数据局部性。

例如,假设集群中有两个应用程序,分别为生产者和消费者,其中生产者将数据写入持久卷,消费者随后可以读取该卷,我们可以通过生产者共享该卷来优化消费者应用程序。

由于Mesos允许多个应用程序订阅同一角色,我们可以将持久卷与资源角色关联起来,此外,生产者和消费者的框架都将订阅相同的资源角色,因此消费者应用程序现在可以在与生产者应用程序相同的卷上启动数据读取任务。

4.3 资源预留

Mesos如何将集群资源分配给不同的角色,Mesos通过预留来实现资源分配。

预留类型有两种:

  • 静态预留
  • 动态预留

静态预留则是在代理启动时的资源分配:

–resources=“cpus:4;mem:2048;cpus(test_abc):8;mem(test_abc):4096”

与静态预留不同,动态预留允许我们重新调整角色内的资源,Mesos允许框架和集群操作员通过框架消息(作为对资源提供的响应)或者通过HTTP API调用动态更改资源分配。

Mesos将所有不具有任何角色的资源分配给名为(*)的默认角色,Master主服务器向所有框架提供此类资源,无论它们是否订阅了该资源。

4.4 资源权重与配额

Mesos主节点一般使用公平策略来提供资源,它使用加权主导资源公平性来识别缺少资源的角色,然后主服务器向已订阅这些角色的框架提供更多资源。

尽管在应用程序之间公平共享资源是Mesos的一个重要特性,但这并非是必要的。假设一个集群托管着资源占用低的应用程序和资源需求高的应用程序。在样的部署中,我们希望根据应用程序的性质分配资源。

Mesos允许框架通过订阅角色并为该角色添加更高的权重来请求更多资源。因此,如果有两个角色,一个权重为1,另一个为2,则Mesos会为第二个角色分配两倍的公平份额资源。

另外,我们还可以通过HTTP接口调用来配置权重,可参考:Mesos Weights Set by HTTP API

除了确保为具有权重的角色公平分配资源之外,Mesos还确保为角色分配最少的资源。

Mesos允许我们为资源角色添加配额,配额可以指定角色保证接收到的最小资源量。

5. 实现框架

Mesos允许应用程序选择任意语言提供框架实现,即实现Scheduler和Executor的接口从而实现框架。

5.1 框架主类

在实现调度程序和执行程序之前,需要先实现框架的入口点:

  • 向master注册
  • 向代理执行器提供运行时信息
  • 启动调度程序

下面程序来自Mesos官方提供的示例,具体可参考:Mesos Framework Python test examples

import os
import sys
import timeimport mesos.interface
from mesos.interface import mesos_pb2
from mesos.scheduler import MesosSchedulerDriverTOTAL_TASKS = 5TASK_CPUS = 1
TASK_MEM = 128class TestScheduler(mesos.interface.Scheduler):def __init__(self, implicitAcknowledgements, executor, framework):self.implicitAcknowledgements = implicitAcknowledgementsself.executor = executorself.framework = frameworkself.taskData = {}self.tasksLaunched = 0self.tasksFinished = 0self.messagesSent = 0self.messagesReceived = 0# 注册def registered(self, driver, frameworkId, masterInfo):print "Registered with framework ID %s" % frameworkId.valueself.framework.id.CopyFrom(frameworkId)driver.updateFramework(framework, [])def resourceOffers(self, driver, offers):# 资源申请for offer in offers:tasks = []offerCpus = 0offerMem = 0for resource in offer.resources:if resource.name == "cpus":offerCpus += resource.scalar.valueelif resource.name == "mem":offerMem += resource.scalar.valueprint "Received offer %s with cpus: %s and mem: %s" \% (offer.id.value, offerCpus, offerMem)remainingCpus = offerCpusremainingMem = offerMemwhile self.tasksLaunched < TOTAL_TASKS and \remainingCpus >= TASK_CPUS and \remainingMem >= TASK_MEM:tid = self.tasksLaunchedself.tasksLaunched += 1print "Launching task %d using offer %s" \% (tid, offer.id.value)task = mesos_pb2.TaskInfo()task.task_id.value = str(tid)task.slave_id.value = offer.slave_id.valuetask.name = "task %d" % tidtask.executor.MergeFrom(self.executor)cpus = task.resources.add()cpus.name = "cpus"cpus.type = mesos_pb2.Value.SCALARcpus.scalar.value = TASK_CPUS # CPU资源mem = task.resources.add()mem.name = "mem"mem.type = mesos_pb2.Value.SCALARmem.scalar.value = TASK_MEM # 内存资源tasks.append(task)self.taskData[task.task_id.value] = (offer.slave_id, task.executor.executor_id)remainingCpus -= TASK_CPUSremainingMem -= TASK_MEMoperation = mesos_pb2.Offer.Operation()operation.type = mesos_pb2.Offer.Operation.LAUNCHoperation.launch.task_infos.extend(tasks)driver.acceptOffers([offer.id], [operation])def statusUpdate(self, driver, update):# 状态更新print "Task %s is in state %s" % \(update.task_id.value, mesos_pb2.TaskState.Name(update.state))# Ensure the binary data came through.if update.data != "data with a \0 byte":print "The update data did not match!"print "  Expected: 'data with a \\x00 byte'"print "  Actual:  ", repr(str(update.data))sys.exit(1)if update.state == mesos_pb2.TASK_FINISHED:self.tasksFinished += 1if self.tasksFinished == TOTAL_TASKS:print "All tasks done, waiting for final framework message"slave_id, executor_id = self.taskData[update.task_id.value]self.messagesSent += 1driver.sendFrameworkMessage(executor_id,slave_id,'data with a \0 byte')if update.state == mesos_pb2.TASK_LOST or \update.state == mesos_pb2.TASK_KILLED or \update.state == mesos_pb2.TASK_FAILED:print "Aborting because task %s is in unexpected state %s with message '%s'" \% (update.task_id.value, mesos_pb2.TaskState.Name(update.state), update.message)driver.abort()# Explicitly acknowledge the update if implicit acknowledgements# are not being used.if not self.implicitAcknowledgements:driver.acknowledgeStatusUpdate(update)# 框架消息处理def frameworkMessage(self, driver, executorId, slaveId, message):self.messagesReceived += 1# The message bounced back as expected.if message != "data with a \0 byte":print "The returned message data did not match!"print "  Expected: 'data with a \\x00 byte'"print "  Actual:  ", repr(str(message))sys.exit(1)print "Received message:", repr(str(message))if self.messagesReceived == TOTAL_TASKS:if self.messagesReceived != self.messagesSent:print "Sent", self.messagesSent,print "but received", self.messagesReceivedsys.exit(1)print "All tasks done, and all messages received, exiting"driver.stop()if __name__ == "__main__":if len(sys.argv) != 2:print "Usage: %s master" % sys.argv[0]sys.exit(1)executor = mesos_pb2.ExecutorInfo()executor.executor_id.value = "default"executor.command.value = os.path.abspath("./test-executor")executor.name = "Test Executor (Python)"executor.source = "python_test"framework = mesos_pb2.FrameworkInfo()framework.user = "" # Have Mesos fill in the current user.framework.name = "Test Framework (Python)"framework.checkpoint = Trueframework.role = "*"implicitAcknowledgements = 1if os.getenv("MESOS_EXPLICIT_ACKNOWLEDGEMENTS"):print "Enabling explicit status update acknowledgements"implicitAcknowledgements = 0if os.getenv("MESOS_EXAMPLE_AUTHENTICATE"):print "Enabling authentication for the framework"if not os.getenv("MESOS_EXAMPLE_PRINCIPAL"):print "Expecting authentication principal in the environment"sys.exit(1);credential = mesos_pb2.Credential()credential.principal = os.getenv("MESOS_EXAMPLE_PRINCIPAL")if os.getenv("MESOS_EXAMPLE_SECRET"):credential.secret = os.getenv("MESOS_EXAMPLE_SECRET")framework.principal = os.getenv("MESOS_EXAMPLE_PRINCIPAL")else:framework.principal = "test-framework-python"credential = None# Subscribe with all roles suppressed to test updateFramework() methoddriver = MesosSchedulerDriver(TestScheduler(implicitAcknowledgements, executor, framework),framework,sys.argv[1],implicitAcknowledgements,credential,[framework.role])status = 0 if driver.run() == mesos_pb2.DRIVER_STOPPED else 1# Ensure that the driver process terminates.driver.stop();sys.exit(status)

调度程序可能会由于缺乏资源而无法再代理上启动任务,则会被拒绝,就是上面的DRIVER_STOPPED。

5.3 实现执行器

框架的执行器组件负责在Mesos代理上执行应用程序服务。

import sys
import threading
import timeimport mesos.interface
from mesos.interface import mesos_pb2
from mesos.executor import MesosExecutorDriverclass MyExecutor(mesos.interface.Executor):def launchTask(self, driver, task):# Create a thread to run the task. Tasks should always be run in new# threads or processes, rather than inside launchTask itself.def run_task():print "Running task %s" % task.task_id.valueupdate = mesos_pb2.TaskStatus()update.task_id.value = task.task_id.valueupdate.state = mesos_pb2.TASK_RUNNINGupdate.data = 'data with a \0 byte'driver.sendStatusUpdate(update)# This is where one would perform the requested task.print "Sending status update..."update = mesos_pb2.TaskStatus()update.task_id.value = task.task_id.valueupdate.state = mesos_pb2.TASK_FINISHEDupdate.data = 'data with a \0 byte'driver.sendStatusUpdate(update)print "Sent status update"thread = threading.Thread(target=run_task)thread.start()def frameworkMessage(self, driver, message):# Send it back to the scheduler.driver.sendFrameworkMessage(message)if __name__ == "__main__":print "Starting executor"driver = MesosExecutorDriver(MyExecutor())sys.exit(0 if driver.run() == mesos_pb2.DRIVER_STOPPED else 1)

执行器的代码比较简单,主要是定义了task执行的逻辑以及对应将数据发回调度器的逻辑。

6. 小结

因为在工作中看到有对应的Mesos字眼,虽然自己不会去直接接触这个东西(大多都是通过包装好的组件间接使用),但了解一下运行原理对自己理解一些组件会有一些帮助。

主要是学习了一下Mesos的基本原理和统一集群中运行的应用程序之间的资源共享,以及Mesos是如何通过集群资源(CPU和内存等)帮助应用程序实现资源最大利用。

里面还有涉及到关于Mesos的资源的公平分配策略以及订阅角色的应用程序之间的资源动态分配,Mesos允许应用程序根据集群中的Mesos代理提供的资源做出调度决策,同时Mesos提供了大量HTTP API可用于接口调用进行资源的权重和配额修改。

另外关于Mesos的具体实践还需要继续学习,这次只是在虚拟机大致跑了一下测试用例,并没有采用手动编写程序的方式来进行具体实践。

参考

  • Mesos Architecture
  • mesos-go
  • Apache Mesos

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

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

相关文章

【国产AI绘图】快手把“可图”大模型开源了,这是一款支持中文的SDXL模型

Kolors 是由 Kuaishou Kolors 团队&#xff08;快手可图&#xff09;开发的基于潜在扩散的大规模文本到图像生成模型。经过数十亿对文本图像的训练&#xff0c;Kolors 在视觉质量、复杂语义的准确性以及中英文字符的文本渲染方面&#xff0c;与开源和专有模型相比都具有显著优势…

Hi6276 无Y应用电源方案IC

Hi6276 combines a dedicated current mode PWM controller with integrated high voltage power MOSFET.Vcc low startup current and low operating current contribute to a reliable power on startup design with Hi6276. the IC operates in Extended ‘burst mode’ to …

【初中数学选讲】绝对值的几何意义例题(20240503-01)

初中数学选讲&#xff1a;绝对值的几何意义例题&#xff08;20240503-01&#xff09; 1. 练习题目1.1 题目描述1.2 分析 2 答题2.1 定义2.2 分段讨论2.2.1 情况1&#xff1a; x x x点在 a a a点左侧&#xff08; x < a , m ∣ x − a ∣ x<a,\ \ m\left|x-a\right| x<…

六、数据可视化—首页、列表页制作(爬虫及数据可视化)

六、数据可视化—首页、列表页制作&#xff08;爬虫及数据可视化&#xff09; 1&#xff0c;首页制作&#xff08;1&#xff09;创建新项目选择flask框架&#xff08;2&#xff09;下载模板&#xff08;3&#xff09;导入flask框架中进行改写&#xff08;4&#xff09;访问服务…

回溯算法-以景点门票销售管理系统为例

1.回溯算法介绍 1.来源 回溯算法也叫试探法&#xff0c;它是一种系统地搜索问题的解的方法。 用回溯算法解决问题的一般步骤&#xff1a; 1、 针对所给问题&#xff0c;定义问题的解空间&#xff0c;它至少包含问题的一个&#xff08;最优&#xff09;解。 2 、确定易于搜…

【论文阅读】-- Visual Analytics for Model Selection in Time Series Analysis

时间序列分析中模型选择的可视化分析 摘要1 引言2 相关工作3 问题表征3.1 Box-Jenkins 方法论3.2 ARIMA 和季节性 ARIMA 模型3.3 模型规范3.4 模型拟合3.5 模型诊断 4 需求分析5 VA 用于时间序列分析中的模型选择5.1 VA选型流程说明5.2 TiMoVA 原型5.2.1 实施选择5.2.2 图形用户…

【在Linux世界中追寻伟大的One Piece】HTTPS协议原理

目录 1 -> HTTPS是什么&#xff1f; 2 -> 相关概念 2.1 -> 什么是"加密" 2.2 -> 为什么要加密 2.3 -> 常见的加密方式 2.4 -> 数据摘要 && 数据指纹 2.5 -> 数字签名 3 -> HTTPS的工作过程 3.1 -> 只使用对称加密 3.2 …

《linux系统内核设计与实现》-实现最简单的字符设备驱动

开发linux内核驱动需要以下4个步骤&#xff1a; 1 编写hello驱动代码 驱动代码如下 helloDev.c&#xff0c;这是一个最小、最简单的驱动&#xff0c;去掉了其他的不相干代码&#xff0c;尽量让大家能了解驱动本身。 #include <linux/module.h> #include <linux/mod…

导航栏样式,盒子模型

1.代码 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>Document</title><style>li{he…

MySQL5.7下载及安装详细教程

我下载的是MySQL 5.7.43 &#xff0c;以下是详细下载安装过程 一、下载过程步骤 1、进入官方网站&#xff1a;https://www.mysql.com/ 2、首页滑到最下面&#xff0c;找到MySQL Community server 3、选择你想要的版本和电脑对应配置进行下载 4、下载完后&#xff0c;保存解…

Google Earth Engine(GEE)——ui.Panel添加到地图上

结果 函数 ui.root.add(widget) 将一个widget添加到根面板上。 返回根面板。 参数。 widget&#xff08;ui.Widget&#xff09;。 要添加的widget。 返回&#xff1a; ui.Panel 代码 //label var label ui.Label({ value: "text label", style: {fontSi…

vscode使用Git的常用操作

主打一个实用 查看此篇之前请先保证电脑安装了Git&#xff0c;安装教程很多&#xff0c;可自行搜索 一.初始化本地仓库&#x1f534; 使用vscode打开项目文件夹如图所使初始化仓库&#xff0c;相当于命令行的git init 二.提交到暂存区&#x1f534; 二.提交到新版本&#x1f…

代码随想录算法训练营第25天|LeetCode 491.递增子序列、46.全排列、47.全排列 II

1.LeetCode 491.递增子序列 题目链接&#xff1a;https://leetcode.cn/problems/non-decreasing-subsequences/description/ 文章链接&#xff1a;https://programmercarl.com/0491.递增子序列.html 视频链接&#xff1a;https://www.bilibili.com/video/BV1EG4y1h78v/ 思路&am…

归并排序详解(递归与非递归)

归并排序是建立在归并操作上的一种有效算法。该算法是采用分治法的一个非常典型的应用。将已有序的子序列合并&#xff0c;得到完全有序的序列&#xff1b;即先使每个子序列有序&#xff0c;再使子序列间断有序。若将两个有序表合并成一个有序表&#xff0c;成为二路归并。 一…

【6】图像分类部署

【6】图像分类部署 文章目录 前言一、将pytorch模型转为ONNX二、本地终端部署2.1. ONNX Runtime部署2.2. pytorch模型部署&#xff08;补充&#xff09; 三、使用flask的web网页部署四、微信小程序部署五、使用pyqt界面化部署总结 前言 包括将训练好的模型部署在本地终端、web…

【Android】自定义换肤框架01之皮肤包制作

前言 目前为止&#xff0c;市面上主流的安卓换肤方案&#xff0c;其实原理都是差不多的 虽然大多都号称一行代码集成&#xff0c;但其实想要做到完全适配&#xff0c;并不简单 这个系列&#xff0c;就是让大家从零开始&#xff0c;完全掌握这方面知识&#xff0c;这样才能对…

RabbitMq - Java客户端基础【简单案例 +Work模型】

目录 1、前置知识 1.1、AMQP怎么理解 1.2、Spring AMQP是什么 1.3、为什么要了解Spring-AMQP&#xff1f; 2、使用Spring-AMQP实现一个发消息案例 3、Work模型 问题&#xff1a; 优化&#xff1a; 小结&#xff1a;Work模型的使用&#xff1a; 1、前置知识 1.1、AMQP怎…

【WPF】桌面程序开发之xaml页面基础布局方式详解

使用Visual Studio开发工具&#xff0c;我们可以编写在Windows系统上运行的桌面应用程序。其中&#xff0c;WPF&#xff08;Windows Presentation Foundation&#xff09;项目是一种常见的选择。然而&#xff0c;对于初学者来说&#xff0c;WPF项目中xaml页面的布局设计可能是一…

EtherCAT转Profinet网关配置说明第三讲:博图配置

EtherCAT协议转Profinet协议网关模块&#xff08;XD-ECPNS20&#xff09;是实现EtherCAT协议和Profinet协议之间无缝通讯的重要设备。使EtherCAT协议和Profinet协议能够相互转换&#xff0c;进行工控自动化里的互连和传送数据。 EtherCAT作为一种高性能实时以太网通信协议&…

【讲解下iOS语言基础】

&#x1f308;个人主页: 程序员不想敲代码啊 &#x1f3c6;CSDN优质创作者&#xff0c;CSDN实力新星&#xff0c;CSDN博客专家 &#x1f44d;点赞⭐评论⭐收藏 &#x1f91d;希望本文对您有所裨益&#xff0c;如有不足之处&#xff0c;欢迎在评论区提出指正&#xff0c;让我们共…