tomcat架构设计分析,核心组件详解

提示:tomcat整体架构分析,tomcat核心组件详解、tomcat请求全流程、tomcat设计模式分析。责任链模式设计、tomcat设计详解、tomcat调优的前置文档

文章目录

  • 前言
  • 一、相关概念
    • 1、tomcat的概念
    • 2、web应用部署的3种方式
  • 二、tomcat的整体架构
    • 1、tomcat架构图
    • 2、tomcat核心组件详解
      • 2.1、server组件
      • 2.2、service组件
      • 2.3、连接器Connector组件
      • 2.4、容器Container组件
    • 3、请求定位Servlet的过程
  • 三、Tomcat架构设计精髓分析
    • 1、Connector高内聚低耦合设计
      • 1.1、ProtocolHandler
      • 1.2、EndPoint
      • 1.3、Processpr
      • 1.4、Adapter
    • 2、设计复杂系统的基本思路
    • 3、父子同期组合模式设计
    • 4、PipeLine-Valve责任链模式设计
      • 4.1、PipeLine-Vavle责任链模式
      • 4.2、为什么要使用管道机制?
      • 4.3、Vavle接口设计
      • 4.4、Valve和Filter的区别:
    • 5、Tomcat生命周期设计
      • 5.1、一键式启停: LifetCycle接口
      • 5.2、可扩展性:LifeCycle事件
      • 5.3、观察这模式
      • 5.4、组件的生命周期状态变化
      • 5.5、重用性
      • 5.6、模版设计模式
  • 总结


前言

本来想搞个tomcat的调优,毕竟是高频面试题。不过要想调优,得先知道tomcat的底层架构,才能更方便的调优。本人水平有限,如有误导,欢迎斧正,一起学习,共同进步!


一、相关概念

1、tomcat的概念

tomcat是一个servlet容器,同时也是一个web服务器。

2、web应用部署的3种方式

  1. 拷贝到webapps目录下
// 指定appBase
<Host name="localhost" appBase="webapps"unpackWARs="true" autoDeploy="true">/**
host: 虚拟主机的相关配置,
name: localhost是本身,你也可以改成具体的ip或者
appBase: 这个目录下的文件会帮你部署,可以是相对路径,也可以是绝对路径
unpackWARs: true代表自动把war包给解压。
autoDeploy: 热部署(自动刷新)
  1. server.xml 的Context标签下配置Context
<Context docBase="D:\mvc" path="/mvc" reloadable="true">/**
context: 一个context标签对应一个web应用。
path:指定访问该web应用的url入口。(context-path)
docBase:指定web应用的文件路径,可以给定绝对路径,也可以给定相对于<Host>中appBase属性的相对路径。例如:
docBase="F:\Resource\mvc\target\mvc-1.0-SNAPSHOT"
reloadable:如果这个属性设为true,tomcat服务器在运行状态下会监听在WEB-INF/classes和WEB-INF/lib目录下class文件的改动。
如果监听到class文件被更新的。服务器会自动重新加载web应用。

如果server.xml是下面这样的:
则代表的是 http://localhost:8080/mvc/user.do
其中你的controller写的是: @GetMapping(value = “user.do”)

<Host name="localhost"  appBase="webapps"
unpackWARs="true" autoDeploy="true"><Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs"prefix="localhost_access_log." suffix=".txt"pattern="%h %l %u %t &quot;%r&quot; %s %b" /><!--  http://localhost:8080/mvc/user.do   path:context-path   -->           
<Context docBase="F:\Resource\mvc\target\mvc-1.0-SNAPSHOT"path="/mvc" reloadable="true" />
  1. 在$CATALINA_BASE/conf/[enginename]/[hostname]/ 目录下,(默认是conf/Catalina/localhost)创建xml文件,文件名就是contextPath。比如创建mvc.xml,path就是/mvc
    注意:想要用根路径访问,文件名为ROOT.xml
<Context docBase="D:\mvc" reloadable="true">

二、tomcat的整体架构

tomcat要实现两个核心功能

  • 处理socket链接,负责网络字节流Request和Response对象转换
  • 加载和管理Servlet,以及具体处理Request请求
    因此Tomcat设计了两个核心组件连接器(Connector)和容器(Container)来分别做这两件事。连接器负责对外交流,容器负责内部处理。

1、tomcat架构图

在这里插入图片描述
从图中我们可以看到。

  1. Connector:Tomcat的连接器,用于接收请求并将其发送给容器。
  2. Container:Tomcat的容器,负责管理Servlet,Jsp和静态资源生命周期
  3. Engine:Tomcat的引擎,管理容器的生命周期和分配请求
  4. Host:Tomcat的主机,可以管理多个web应用程序的配置信息
  5. Context:Tomcat的上下文,用于管理多个web应用程序
  6. Servlet:Tomcat的Servlet,负责处理请求并生成响应
  7. JSP:Tomcat的JSP,用于动态生成web内容

一个tomcat中可以有多个service,每一个service可以分为容器Engine、链接Connector。
其中的容器Engine,每个容器有多个Host,每一个host都可以有多个context,然后每个context内又有多个servlet(tomcat将servlet封装成了Wrapper)
以 http://localhost:8080/mvc2 为例。
localhost,就会在多个host中找到是localhost的host,如果是指定的ip,则是具体的ip。8080,则会在这个host中找到8080的context。然后找到mvc2的servlet。然后去处理servlet中的逻辑,然后返回结果。

2、tomcat核心组件详解

2.1、server组件

指的就是整个Tomcat服务器,包含多组服务(services),负责管理和启动各个service,同时监听8005端口发送过来的shutdown命令,用于关闭整个容器

2.2、service组件

每个service组件都包含若干个用于接收客户端消息的Connector和处理请求的Engine组件。每个service组件还包含了若干个Executor组件,每个exevutor都是一个线程池,它可以为service内所有组件提供线程池执行任务。一个tomcat内可以有多个service,这样也是基于灵活性的考虑。通过一个tomcat内配置多个serivce,可以实现通过调用不同的端口去访问同一个机器上面的不同应用

2.3、连接器Connector组件

Tomcat与外部世界的连接器,监听固定端口接收外部请求,传递给Container,并将Container处理的结果返回给外部。连接器对servlet容器屏蔽了不同的应用层协议及I/O模型,无论是Http还是AJP,在容器中获取到的都是一个标准的ServletRequest对象

2.4、容器Container组件

容器,就是用来装载东西的器具,在tomcat中,容器就是用来装载Servlet的。Tomcat通过一种分层的架构,使得Servlet容器具有很好的灵活性。Tomcat设计了4种容器,分别是Engine、Host、Context和Wrapper。这4种容器不是平行关系,而是父子关系。

  • Engine : 引擎,Servlet顶层容器,用来管理多个虚拟站点,一个Service最多只有一个Engine;
  • Host : 虚拟主机,负责web应用的部署和Context的创建。可以给Tomcat配置多个虚拟主机地址,而一个虚拟主机下可以部署多个Web应用程序
  • Context: Web应用上下文,包含多个Wrapper,负责web配置的解析、管理所有的web资源,一个Context对应一个web应用程序
  • Wrapper:表示一个Servlet,最底层的容器,是对Servlet的封装,负责Servlet实例的创建、执行和销毁

在这里插入图片描述
可以对着Tomcat中的server.xml来理解:

<Server> //顶层组件,可以包括多个Service<Service> //顶层组件,可包含一个Engine,多个连接器<Connector/>//连接器组件,代表通信接口<Engine>//容器组件,一个Engine组件处理Service中的所有请求,包含多个Host<Host> //容器组件,处理特定的Host下客户请求,可包含多个Context<Context/> //容器组件,为特定的Web应用处理所有的客户请求</Host> </Engine></Service>
</Server>

tomcat启动期间会通过解析server.xml,利用反射创建相应的组件,所以xml中的标签和源码一一对应。

3、请求定位Servlet的过程

Tomcat是用Mapper组件来完成这个任务的。Mapper组件的功能就是将用户请求的url定位到一个Servlet,他的工作原理是:Mapper组件里保存了Web应用的配置信息,其实就是容器组件与访问路径的映射关系,比如Host容器里配置的域名、Context容器里的Web应用路径,以及Wrapper容器里Servlet映射的路径,你可以想象这些配置信息就是一个多层次的map,当一个请求到来时,Mapper组件通过解析请求url里面的域名和路径,再到自己保存的Map里面查找,就能定位到一个Servlet。一个请求url最只会定位到一个Wrapper容器,也就是一个Servlet

在这里插入图片描述

三、Tomcat架构设计精髓分析

1、Connector高内聚低耦合设计

优秀的设计应考虑高内聚、低耦合

  • 高内聚是指相关度比较高 功能要尽可能的集中,不要分散
  • 低耦合是指两个相关的模块要紧可鞥减少依赖的部分和降低依赖程度,不要让俩个模块产生强依赖

Tomcat连接器要实现的功能:

  • 监听网络端口
  • 接收网络连接请求
  • 读取请求网络字节流
  • 根据具体应用层协议(HTTP/AJP)解析字节流,生成统一的Tomcat Request对象
  • 将Tomcat Reuqest对象转为标准的ServletRequest
  • 调用Servlet容器,得到ServletResponse
  • 将ServletResponse转成TomcatResponse对象
  • 将Tomcat Response转为网络字节流
  • 将响应字节流写回给浏览器

分析连接器的详细功能,我们会发现连接器需要完成3个高内聚功能:

  • 网络通信
  • 应用层协议解析
  • Tomcat Request/Response与ServletRequest/ServletResponse的转换

因此,Tomcat的设计者设计了3个组件来分别实现这3个功能,分别是:EndPoint、Processor、Adapter

  • EndPoint 负责提供字节流给Processor
  • Processor负责提供Tomcat Request对象给Adapter
  • Adapter 负责提供ServletReqest对象给容器

除了这些变化点,系统也存在一些相对稳定的部分,因此 Tomcat 设计了一系列抽象基类来封装这些稳定的部分,抽象基类 AbstractProtocol 实现了 ProtocolHandler 接口。每一种应用层协议有自己的抽象基类,比如 AbstractAjpProtocol 和 AbstractHttp11Protocol,具体协议的实现类扩展了协议层抽象基类

在这里插入图片描述

1.1、ProtocolHandler

连接器用ProtocolHandler来处理网络连接和应用层协议,包含了2个重要组件:EndPoint和Processor

在这里插入图片描述
连接器用ProtocolHandler接口来封装通信协议和I/O模型的差异,ProtocolHandler内部又分为EndPoint和Processor模块,EndPoint负责底层Socket通讯,Processor负责应用层协议解析,连接器通过适配器Adapter调用容器。

1.2、EndPoint

endPoint是通讯端点。即通信监听的接口,是具体的Socket接收和发送处理器,是对传输层的抽象,因此,EndPoint是用来实现TCP/IP协议的。EndPoint是一个接口,对应的抽象实现类是AbstractEndpoint,而AbstractEndpoint的具体子类,比如在NoiEndpoint和Nio2Endpoint中,有两个重要的子组件:Acceptor和SocketProcessor、其中Acceptor用于监听Socket连接请求,SocketProcessor用于处理接收到的Sovket请求,他实现Runanble接口,在Run方法里调用协议处理组件Processor进行处理,为了提供处理能力,SovketProcessor被提交到线程池来执行,而这个线程池叫做执行器(Executor)。

1.3、Processpr

Processor用来实现HTTP/AJP协议,Processor接收来自EndPoint的Socket,读取字节流解析成Tomcat Request和Response对象,并通过Adapter将其提交到容器处理,Processor是对应用层协议的抽象。Processor是一个接口,定义了请求的处理等方法,他的抽象实现类AbstractProcessor对一些协议共有的属性进行封装,没有对方法进行实现,具体的视线有AJPProcessor、HTTP11Processor等,这些具体的实现类实现了特定协议的解析方法和请求处理方式。

在这里插入图片描述
EndPoint接收到Soket连接后,生成一个SocketProcessor任务提交到线程池去处理,SovketProcessor的Run方法会调用Processor组件去解析应用层协议,Processor通过解析生成的Request对象后,会调用Adapter的Service方法。

1.4、Adapter

由于协议不同,客户端发过来的请求信息也不尽相同,Tomcat定义了自己的Request类来“存放”这些请求信息。ProtocolHandler接口负责解析请求并生成Tomcat Reuquest类。但是这个Request对象不是标准的ServletRequest,也就一位这,不能用TomcatRequest作为参数来调用容器,Tomcat设计者的解决方案是引入CoyoteAdapter,这是适配器模型的经典运用,连接器调用CoyoteAdapter的Service方法,传入的是Tomcat Request对象,CoyoteAdapter负责将Tomcat Request转换为ServletRequest,在调用容器的Service方法。

2、设计复杂系统的基本思路

首先要分析需求,根据高内聚低耦合的原则确定子模块,然后找出子模块中的变化点和不变点,用接口和抽象基类去封装不变点,在抽象类中定义目标方法,让子类自行实现抽象方法,也就是具体子类去实现变化点

3、父子同期组合模式设计

(tomcat设计了4中容器:Engine、Host、Context、Wrapper,tomcat是怎么管理这些容器的?)
tomcat采用组合模式来管理这些容器,具体的实现方法是,所有容器组件都实现了Container接口,因此组合模式可以使用用户对单容器对象和组合同期对象的使用具有一致性。(我们从2.1的架构图可以知道每个services下都有一个engine,每个engine下都有多个Host,每个host下面都有多个context,每个context下都有多个wrapper,让这4个都继承一个公共的类Container,这样就能用一个Container来表示这4个容器了,不同的实现)
Container 接口定义如下:

public interface Container extends    Lifecycle{public void setName(String name);public Container getParent();public void setParent(Container container);public void addChild(Container child);public void removeChild(Container child);public Container findChild(String name);
}

在这里插入图片描述

4、PipeLine-Valve责任链模式设计

连接器中的Adapter会调用容器的Service方法来执行Servlet,最先拿到请求的是Engine容器,Engine容器对请求做一些处理后,会把请求传递给Host继续处理,以此类推,最后这个请求会传给Wrapper容器,Wrapper会调用最终的Servlet来处理。这个调用过程是怎么实现的呢?就是用Pipeline-Vavle管道。

4.1、PipeLine-Vavle责任链模式

PipeLine-Vavle是责任链模式,责任链模式是指一个请求处理的过程中,有很多的处理者依次对请求进行处理,每个处理者负责做自己对应的处理,处理完了疑惑,在调用下一个处理者继续处理

4.2、为什么要使用管道机制?

因为一个比较复杂的大型系统重,如果一个对象需要经过很复杂的逻辑处理,直接处理这些复杂的业务逻辑,扩展性,可重用性都是很差的。更好的解决方案是采用管道机制,用一条管道把多个对象(这些对象是指阀门部件,也就是这个很复杂的逻辑拆出来的一小块一小块的逻辑处理)连接起来,整体看起来就像若干个阀门嵌套在管道中一样,而处理逻辑放在阀门上。

PipeLine-Vavle责任链模式,从名字也能看出来,就俩组件,一个管道(pipeline)、一个阀门(vavle)。

4.3、Vavle接口设计

由于Pipeline是为容器设计的,所以他在设计时加入了一个Contained接口,就是为了制定当前Pipeline所属的容器
tomcat源码: org.apache.catalina.Pipeline

public interface Pipeline extends Contained {// 基础的处理阀public Valve getBasic();public void setBasic(Valve valve);// 对节点(阀门)增删查public void addValve(Valve valve);public Valve[] getValves();public void removeValve(Valve valve);// 获取第一个节点,遍历的起点,所以需要有这方法public Valve getFirst();// 是否对所有节点(阀门)都支持处理Servlet3异步处理public boolean isAsyncSupported();// 找到所有不支持Servlet3异步处理的阀门public void findNonAsyncValves(Set<String> result);
}

Pipeline维护了Valve链表,Valve可以插入到Pipeline中,对请求做某些处理,整个调用链的触发是Vavle来完成的,Valve完成自己的处理后,调用getNext.invoke()来触发下一个Vavle调用,每个容器都有一个Pipeline对象,只要触发这个Pipeline的第一个Valve,这个容器里Pipeline的Valve就会被调用到。Basic Valve处于Valve链表的末端,他是Pipeline中必不可少的一个Valve,负责调用下层容器的Pipeline里的第一个Valve。
在这里插入图片描述

  1. 当Adapter来请求了以后,先进入到Engine层,然后engine能够拿到(Pipeline的)getFirst,拿到第一个valve
  2. 然后从在engine中,就会一级一级的,从first拿到最后一个Basic
  3. 然后engine的basic就会getHost,拿到新的pipeline中的getFirst,然后再一级一级的拿到basic。。。直到拿到最下面的basic。(当然,每次都是先拿到管道pipeline,然后在用pipeline中的getFirst,然后invoke,然后nextInvoke。。。一级一级的往下走)

整个调用过程由连接器中的Adapter触发的,他会调用Engine的第一个Vavle:
真要通俗的讲,可以认为,先(每层都有pipeline)用管道pipiline中的getFirst拿到第一个,然后invoke,nextinvoke,然后直到basic,到basic了,在获取下一层(engine找host,host找context,context找wrapper)的pipeline,然后下一层的管道在getFirst,在invoke。。。

connector.getService().getContainer().getPipeline().getFirst().invoke(request, response);

Wrapper容器的最后一个Valve会创建一个Filter链,并调用doFilter(),最终会调到Servlet的service方法

filterChain.doFilter(request.getRequest(), response.getResponse());

4.4、Valve和Filter的区别:

  • Valve是Tomcat的私有机制,与Tomcat的基础架构/API是紧耦合的,Servlet API是共有的标准,所有的web容器包裹jetty都支持Filter机制。
  • Valve工作在web容器级别,拦截所有应用的请求;而Servlet Filter工作在应用级别,只能拦截某个web应用的所有请求
管道/阀门过滤器链/过滤器
管道(Pipeline)过滤器链(FilterChain)
阀门(Valve)过滤器(Filter)
底层实现为具有头、尾指针的单向链表底层实现为数组
Valve的核心方法invoke(request,response)Filter核心方法doFilter(request,response,chain)
pipeline.getFilter().invoke(request,respnse)filterchain.doFilter(request,response)

链表嘛,有单向链表、双向链表的,只是因为我们的请求是单向的,因此他是设计的单向的。
请求是单向的,因此我们链表单向的就行,就是值有next,没有pre。

5、Tomcat生命周期设计

tomcat里面有很多的组件,那这么大的系统,怎么管理这些组件呢?而且tomcat是怎么管理这些组件的生命周期呢,而且他还要支持热加载、热部署、后续可能还有针对于组件的扩展啊之类的,所以他的设计一定要扩展性比较高,要符合开闭原则(就是说你可以新增类,但是不能改旧类的逻辑)

5.1、一键式启停: LifetCycle接口

系统设计就是要找到系统的变化点和不变点。这里的不变点就是每个组件都要经历创建、初始化、启动这几个过程。而变化点是每个组件的具体的初始话方法是不同的。因此我们把不变点抽象出来为一个接口,这个接口和生命周期有关,叫做LifeCycle。这个接口有这些方法:init()、start()、stop()、destory(),每个具体的组件去实现这些方法。

public interface Lifecycle {/** 第1类:针对监听器 **/// 添加监听器public void addLifecycleListener(LifecycleListener listener);// 获取所有监听器public LifecycleListener[] findLifecycleListeners();// 移除某个监听器public void removeLifecycleListener(LifecycleListener listener);/** 第2类:针对控制流程 **/// 初始化方法public void init() throws LifecycleException;// 启动方法public void start() throws LifecycleException;// 停止方法,和start对应public void stop() throws LifecycleException;// 销毁方法,和init对应public void destroy() throws LifecycleException;/** 第3类:针对状态 **/// 获取生命周期状态public LifecycleState getState();// 获取字符串类型的生命周期状态public String getStateName();
}

在父组件的init()方法里需要创建子组件并调用子组件的init()方法,同样,在父组件的start()方法中也需要调用子组件的start()方法,因此调用者可以无差别调用各组件的init()方法和start()方法,这就是组合模式的使用。只需要调用最顶层组件,也就是server组件的init()和start()方法,整个tomcat就被启动起来了。

5.2、可扩展性:LifeCycle事件

各个组件的实现都是复杂多变的,如果将来需要增加新的逻辑,直接修改start()方法吗?这样会违法开闭原则。组件的init()和start()调用是由他的父组件的状态变化触发的,上层组件的初始化会触发子组件的初始化,上层组件的启动会触发子组件的启动,因此我们把组件的生命周期定义为一个个的状态,把状态的转变看做是一个事件,而事件是有监听器的,在监听器里可以实现一些逻辑,并且监听器也可以方便的添加和删除,这就是典型的观察者模式
其实就是在LifeCycle接口里加入俩方法:添加监听器和删除监听器。

在这里插入图片描述

5.3、观察这模式

监听器,观察者模式,每个组件都可以实现自己的监听,只要让组件去实现这个生命周期的接口就行(他里面有监听器的接口,只需要去实现就行)
我们还需要定义一个Enum来表示组件有哪些状态,以及处在什么状态会触发什么样的事件
在这里插入图片描述

5.4、组件的生命周期状态变化

组件的生命周期状态变化如下:

在这里插入图片描述

5.5、重用性

LifeCycleBase抽象基类
一般来说,实现类不止一个,不同的类在实现接口时往往会有一些相同的逻辑,如果让各个子类都去实现一遍,就会有重复的代码。那子类如果重用这部分逻辑呢?就是定义一个基类来实现共同的逻辑,然后让各个子类去继承他,就达到了重用的目的。而基类中往往定义一些抽象方法,所谓的抽象方法就是说基类不会去实现这些方法,而是调用这些方法来实现骨架逻辑。

Tomcat定义一个基类LifeCycleBase来实现LifeCycle接口,把一些公共的逻辑放到基类中去,比如生命状态的转变与维护、生命周期时间的触发以及监听器的添加和删除等,而子类就负责实现自己的初始化、启动和停止等方法。为了避免跟基类中的方法同名,我们把具体子类的实现方法改个名字,在后面加上Internal,叫initlenternal()、startInternal()等。

5.6、模版设计模式

模版设计模式(骨架抽象类和模版方法):LifeCycleBase实现了LifeCycle接口中的所有方法,还定义了相应的抽象方法交给具体子类去实现。


总结

tomcat的整体架构就分析完毕了,我们下一步就是,tomcat调优应该怎么搞,哈哈,是不是还蛮期待的。毕竟各种调优,各种原理,是面试官最喜欢问的问题。当然,了解底层原理,也有助于我们后续解决问题。

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

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

相关文章

家教管理系统设计与实现

摘 要 传统办法管理信息首先需要花费的时间比较多&#xff0c;其次数据出错率比较高&#xff0c;而且对错误的数据进行更改也比较困难&#xff0c;最后&#xff0c;检索数据费事费力。因此&#xff0c;在计算机上安装家教管理系统软件来发挥其高效地信息处理的作用&#xff0c…

[论文笔记]RAFT: Adapting Language Model to Domain Specific RAG

引言 今天带来一篇结合RAG和微调的论文&#xff1a;RAFT: Adapting Language Model to Domain Specific RAG。 为了简单&#xff0c;下文中以翻译的口吻记录&#xff0c;比如替换"作者"为"我们"。 本文介绍了检索增强微调(Retrieval Augmented Fine Tunin…

Opencv中的直方图(4)局部直方图均衡技术函数createCLAHE()的使用

操作系统&#xff1a;ubuntu22.04 OpenCV版本&#xff1a;OpenCV4.9 IDE:Visual Studio Code 编程语言&#xff1a;C11 算法描述 创建一个指向 cv::CLAHE 类的智能指针并初始化它。 函数原型 Ptr<CLAHE> cv::createCLAHE (double clipLimit 40.0,Size tileGridSize…

house of cat

文章目录 house of cat概述&#xff1a;_IO_wfile_jumps进入_IO_wfile_seekoffFSOP__malloc_assert 例题&#xff1a;思路&#xff1a;分析&#xff1a;利用&#xff1a; house of cat 概述&#xff1a; house of cat主要的摸底还是覆盖vtable指针&#xff0c;因为在glibc-2.2…

DrissionPage设置启动浏览器为edge

1.查看浏览器启动路径 在浏览器地址栏输入下面地址&#xff0c;拿到可执行文件的路径 。 edge://version/ 2.替换路径 打开DrissionPage._configs. chromium_options.py文件&#xff0c;找到def browser_path(self)这个函数&#xff0c;将返回内容替换为edge的启动路径&#x…

轿厢电梯-电动车检测数据集(真实电梯监控)

轿厢电动车检测数据集&#xff0c; 可做电梯乘客、电动车检测任务。 数据集由真实电梯监控图片&#xff08;大约四千&#xff09;、电动车网图、手机拍摄图片构成&#xff0c;总计14000张左右&#xff0c;其中近8000样本已标注。 注&#xff1a;文件夹后面数字为对应数据集样本…

论斜率优化dp

论斜率优化dp 1问题2暴力算法-线性dp3斜率优化线性dp4后记 1问题 如下图 看到这题&#xff0c;题面很复杂 其实可以转化为如下问题 有 n n n个任务&#xff0c;排成一个有序序列&#xff0c;我们要解决这些任务 总费用是每一个任务的完成时间乘以费用系数求和 每个任务之前…

紫金大数据平台架构之路(一)----大数据任务开发和调度平台架构设计

一、总体设计 初来公司时&#xff0c;公司还没有大数据&#xff0c;我是作为大数据架构师招入的&#xff0c;结合公司的线上和线下业务&#xff0c;制定了如下的大数据架构路线图。 二、大数据任务开发和调度平台架构设计 在设计完总体架构后&#xff0c;并且搭建完hadoop/ya…

Python基础语法(多进程开发进程建数据共享进程锁进程池)

Python基础语法文章导航&#xff1a; Python基础&#xff08;01初识数据类型&变量&#xff09;Python基础&#xff08;02条件&循环语句&#xff09;Python基础&#xff08;03字符串格式化&运算符&进制&编码&#xff09;Python基础&#xff08;04 基础练习…

Marin说PCB之闲谈设计经验之沟通

今天这期小编我不讲解技术&#xff0c;主要是分享一些个人的工作中的一些经验吧&#xff0c;首先给诸位分享的就是小编我的学的降龙十八掌第一式&#xff1a;沟通&#xff0c;为啥要说沟通是第一个我要说的话题呢&#xff0c;这个说来话长了&#xff0c;小编我就长话短说了。 因…

Rust: Web框架Axum和Rest Client协同测试

Axum作为Rust当红Web框架&#xff0c;值得了解一下。下面实例包括几个典型的常场场景。 具体如下&#xff1a; 一、Axum 1、toml中依赖 [dependencies] tokio {version"1.39.3",features ["full"]} axum {version "0.7.5",features ["to…

OceanBase 关于 place_group_by HINT的使用

PLACE_GROUP_BY Hint 表示在多表关联时&#xff0c;如果满足单表查询后直接进行group by 的情形下&#xff0c;在跟其它表进行关联统计&#xff0c;减少表内部联接。 NO_PLACE_GROUP_BY Hint 表示在多表关联时&#xff0c;在关联后才对结果进行group by。 使用place_group_by …

html+css+js网页设计 故宫10个页面 ui还原度100%

htmlcssjs网页设计 故宫10个页面 ui还原度100% 网页作品代码简单&#xff0c;可使用任意HTML编辑软件&#xff08;如&#xff1a;Dreamweaver、HBuilder、Vscode 、Sublime 、Webstorm、Text 、Notepad 等任意html编辑软件进行运行及修改编辑等操作&#xff09;。 获取源码 …

Spring 学习笔记

概述 Spring 是一个企业级 J2EE 应用开发一站式解决方案&#xff0c;其提供的功能贯穿了项目开发的表现层、业务层和持久化层&#xff0c;同时&#xff0c;Spring 可以和其他应用框架无缝整合 Spring 的特性包括以下几个方面&#xff1a; 轻量&#xff1a;Spring 是一个轻量…

三级_网络技术_58_应用题

一、 请根据下图所示网络结构回答下列问题。 1.填写RG的路由表项。 目的网络/掩码长度输出端口__________S0&#xff08;直接连接&#xff09;__________S1&#xff08;直接连接&#xff09;__________S0__________S1__________S0__________S1 (2)如果在不改变路由表项的前提…

如何录制黑神话悟空的游戏BGM导入iPhone手机制作铃声?

在游戏的世界里&#xff0c;总有那么一些旋律&#xff0c;能够触动玩家的心弦&#xff0c;让人难以忘怀。《黑神话悟空》以其精美的画面和动人的背景音乐&#xff0c;赢得了无数玩家的喜爱。如果你也想将游戏中的背景音录制下来&#xff0c;制作成个性化的m4r格式铃声&#xff…

C++ 设计模式——备忘录模式

C 设计模式——备忘录模式 C 设计模式——备忘录模式1. 主要组成成分2. 逐步构建备忘录模式步骤1: 创建备忘录步骤2: 实现原发器步骤3: 创建管理者&#xff08;负责人&#xff09;类步骤4: 客户端使用 3. 备忘录模式 UML 图UML 图解析 4. 备忘录模式的优点5. 备忘录模式的缺点6…

Camtasia 2024 v2024.0.6 for Mac 中文版 屏幕录像视频录制编辑软件

TechSmith Camtasia for Mac 中文版 是一款专业的屏幕录像及视频录制编辑软件。以业界领先的清晰度重新定义了屏幕录制&#xff0c;将屏幕、摄像头、麦克风和系统音频捕获为独立音轨&#xff0c;实现终极控制和灵活性。通过拖放过渡、标注等功能&#xff0c;以及改进的工作流程…

AI-Talk开发板之helloword

一、说明 创建第一个应用在AI-Talk开发板上运行&#xff0c;编写一个“Hello World”应用&#xff0c;启动之后在主函数里通过UART0&#xff08;debug&#xff09;打印"Hello World"。 官方指导&#xff1a;第一个应用 | 聆思文档中心 (listenai.com) 二、创建工程…

标准库标头 <optional> (C++17)学习之optional

类模板 std::optional 管理一个可选 &#xfeff;的所含值&#xff0c;即既可以存在也可以不存在的值。 一种常见的 optional 使用情况是作为可能失败的函数的返回值。与如 std::pair<T, bool> 等其他手段相比&#xff0c;optional 可以很好地处理构造开销高昂的对象&a…