概述

Tomcat、Jetty、Undertow这几个都是非常有名实现Servlet规范的应用服务器,Tomcat本身也是业界上非常优秀的中间件,简单可将Tomcat看成是一个Http服务器+Servlet容器,Servlet 容器是管理和运行 Servlet 的;相信大家对这只三脚猫Logo都是非常熟悉了,不管是在学校和还是工作都经常使用它,特别是Java程序员最初学习编程都经历过在Idea或Eclipse集成环境中配置Tomcat启动Web项目,当然还可以通过基于tomcat maven插件或者SpringBoot内嵌web容器方式调试运行。

源码编译

官方下载Tomcat源码,目前最新稳定版本为10.0.12

#由于tomcat源码依赖jakartaee-migration模块,而jakartaee-migration未发布到maven repository,我们需要git clone到本地,再mvn install来部署解决tomcat源码的编译问题,https://github.com/apache/tomcat-jakartaee-migration
#解压文件,进入到tomcat-jakartaee-migration-main目录,地址栏上输入cmd进入命令行窗口并且自动进入当前目录

#执行maven安装到本地仓库
mvn clean install

#解压apache-tomcat-10.0.12-src.zip的文件,Tomcat源码并非maven项目结构,但可以通过pom指定java代码目录(无需按照src/main结构来),项目目录下创建pom.xml文件,内容如下:
<?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">
<modelVersion>4.0.0</modelVersion> <groupId>com.github.sources</groupId>
<artifactId>source-tomcat</artifactId>
<version>10.0-SNAPSHOT</version>
<name>source-tomcat</name>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.easymock</groupId>
<artifactId>easymock</artifactId>
<version>3.5.1</version>
</dependency>
<dependency>
<groupId>org.apache.ant</groupId>
<artifactId>ant</artifactId>
<version>1.10.1</version>
</dependency>
<dependency>
<groupId>wsdl4j</groupId>
<artifactId>wsdl4j</artifactId>
<version>1.6.2</version>
</dependency>
<dependency>
<groupId>javax.xml</groupId>
<artifactId>jaxrpc</artifactId>
<version>1.1</version>
</dependency>
<dependency>
<groupId>org.eclipse.jdt</groupId>
<artifactId>org.eclipse.jdt.core</artifactId>
<version>3.25.0</version>
</dependency>
<dependency>
<groupId>org.eclipse.jdt.core.compiler</groupId>
<artifactId>ecj</artifactId>
<version>4.6.1</version>
</dependency>
<dependency>
<groupId>org.apache.tomcat</groupId>
<artifactId>jakartaee-migration</artifactId>
<version>0.2.1-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>biz.aQute.bnd</groupId>
<artifactId>biz.aQute.bndlib</artifactId>
<version>5.2.0</version>
<scope>provided</scope>
</dependency>
</dependencies> <build>
<finalName>Tomcat10.0</finalName>
<sourceDirectory>java</sourceDirectory>
<testSourceDirectory>test</testSourceDirectory>
<resources>
<resource>
<directory>java</directory>
</resource>
</resources>
<testResources>
<testResource>
<directory>test</directory>
</testResource>
</testResources>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.6.1</version>
<configuration>
<encoding>UTF-8</encoding>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
</plugins>
</build> </project>
#由于源码apache-tomcat-10.0.12的pom文件里的jakartaee-migration的版本为0.2.1-SNAPSHOT,我们改为上面tomcat-jakartaee-migration-main的项目的版本1.0.1-SNAPSHOT

#我们在JDTCompiler里注释下面三行源码, 不能会报没有 CompilerOptions.VERSION_16
// settings.put(CompilerOptions.OPTION_Source, CompilerOptions.VERSION_16);
// settings.put(CompilerOptions.OPTION_TargetPlatform, CompilerOptions.VERSION_16);
// settings.put(CompilerOptions.OPTION_Compliance, CompilerOptions.VERSION_16);

前面章节我们已提到tomcat main函数的入口在org.apache.catalina.startup.Bootstrap里,知道main函数

会遇到测试类的报错如下,可以尝试运行忽略测试类,或者直接删除maven compile时产生的test文件夹,我们这里直接删除test文件夹

由于目前tomcat启动找不到配置文件,因此我们在源码根目录下创建source文件夹,并将conf和webapps这两个目录转移到source文件夹中

然后在运行设置里添加如下vm参数

-Dcatalina.home=F:\develop\apache-tomcat-10.0.12-src\source
-Dcatalina.base=F:\develop\apache-tomcat-10.0.12-src\source
-Djava.util.logging.manager=org.apache.juli.ClassLoaderLogManager
-Djava.util.logging.config.file=F:\develop\apache-tomcat-10.0.12-src\source\conf\logging.properties

配置好指定的catalina.home和catalina.base以及日志相关jvm参数后,启动Tomcat,到这里,我们是可以启动tomcat程序,但是访问http://localhost:8080/ 会报500错误,启动Tomcat BootStrap时未加载JSP编译器JasperInitializer

#在org.apache.catalina.startup.ContextConfig#configureStart中
webConfig(); // 这句下面添加如下初始化语句
context.addServletContainerInitializer(new JasperInitializer(), null);

重新启动Tomcat,查看http://localhost:8080,出现Tomcat的欢迎页面

架构源码剖析

整体架构

Tomcat源码中大量使用模板方法和适配器的设计模式,封装很多的组件,组件之间呈现出明显的层级关系,一层套着一层,这就是经典的套娃式架构设计;个人推荐从tomca配置文件开始理解其架构这个也是基于套娃式架构设计的优点得来。Tomcat 模块分层结构及相关模块的功能说明如下图:

Tomcat 核心组件

  • Listener 组件

    • 可以在 Tomcat 生命周期中完成某些容器相关的监听器。
  • JNDI
    • JNDI是 Java 命名与目录接口,是属于 J2EE 规范的,Tomcat 对其进行了实现。JNDI 在 J2EE 中的角色就是“交换机”,即 J2EE 组件在运行时间接地查找其他组件、资源或服务的通用机制(你可以简单理解为给资源取个名字,再根据名字来找资源)。
  • Cluster 组件
    • 提供了集群功能,可以将对应容器需要共享的数据同步到集群中的其他 Tomcat 实例中。
  • Realm 组件
    • 提供了容器级别的用户-密码-权限的数据对象,配合资源认证模块使用。
  • Loader 组件
    • Web 应用加载器,用于加载 Web 应用的资源,它要保证不同 Web 应用之间的资源隔离。
  • Manager 组件
    • Servlet 映射器,它属于 Context 内部的路由映射器,只负责该 Context 容器的路由导航。

Catalina 是 Tomcat 中的一个重要组件,它负责的是解析 Tomcat 的配置文件(server.xml),以此来创建服务器 Server 组件并进行管理。下面是Tomcat的conf目录下大名鼎鼎server.xml核心配置的内容结构,Server-Service-(Connector+Engine),在GlobalNamingResouce 域中可以定义全局资源 tomcat-user.xml,在web.xml文件中也有常见如session-config配置session超时时间默认是30分钟。

<?xml version="1.0" encoding="UTF-8"?>
<Server port="8005" shutdown="SHUTDOWN">
<Listener className="org.apache.catalina.startup.VersionLoggerListener" />
<GlobalNamingResources>
<Resource name="UserDatabase" auth="Container"
type="org.apache.catalina.UserDatabase"
description="User database that can be updated and saved"
factory="org.apache.catalina.users.MemoryUserDatabaseFactory"
pathname="conf/tomcat-users.xml" />
</GlobalNamingResources>
<Service name="Catalina">
<Executor name="tomcatThreadPool" namePrefix="catalina-exec-"
maxThreads="150" minSpareThreads="4"/>
<Connector executor="tomcatThreadPool"
port="8080" protocol="HTTP/1.1"
connectionTimeout="20000"
redirectPort="8443" />
</Connector>
<Engine name="Catalina" defaultHost="localhost">
<Realm className="org.apache.catalina.realm.LockOutRealm">
<Realm className="org.apache.catalina.realm.UserDatabaseRealm"
resourceName="UserDatabase"/>
</Realm>
<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" />
</Host>
</Engine>
</Service>
</Server>
  • Server :代表了一个 Tomcat 实例,包含了 Servlet 容器以及其他组件,负责组装并启动 Servlet 引擎、Tomcat 连接器。每个Tomcat都只有一个Server,表示整个服务环境。一个Server中可以有多个Service。Server就管理多个Service。tomcat源码中服务继承自LifeCycle,tomcat服务组件顶层接口,有唯一实现StandardServer。
  • Service:服务是 Server 内部的组件,一个Server中可以有多个Service,它将若干个 Connector 组件绑定到一个 Container,每一个Service中可以有多个Connector和一个Container(Engine)。
    • Connector主要用来接收请求,解析请求内容,封装request和response,然后将准备好的数据交给Container处理。
    • Container就是我们常说的容器,里面可以有多个Host,一个host表示一个虚拟主机,就是一个对应一个WebApps. 最后Container处理完请求之后会将响应内容返回给Connecter,再由Connecter返回给客户端。
    • Executor 执行器:tomcat线程池的配置,提供给Connector。
  • Connecter:负责处理用户的 servlet 请求,并返回对象给 web 用户的模块

我们知道tomcat是处理http的请求,意味着tomcat使用的是 HTTP 协议进行数据传输,而HTTP 协议是一种应用层协议,其本质就是一种浏览器与服务器之间约定好的通信格式,因此tomcat作为一个Http服务器需要包含接受连接、解析请求数据、处理请求和发送响应这几大块功能。在这里我们再分析tomcat最核心的两个功能:

  • 处理 Socket 连接,负责网络字节流与 Request 和 Response 对象的转化。
  • 加载和管理 Servlet,由 Servlet 具体负责处理 Request 请求。

Tomcat 最底层使用的是Socket进行连接的,所以这里也涉及网络IO模型,通过socket监听本机端口,接收和处理监听端口的网络请求,Request和Response是按照Http协议来封装的,所以Connector同时需要实现TCP/IP协议和Http协议;基于核心功能Tomcat 设计了两个核心组件连接器(Connector)和容器(Container)来分别实现,连接器复杂对外接收和解析请求,封装request和response,最后把数据交给Containner,容器负责内部处理。

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

连接器

连接器主要需要完成以下三个核心功能:

  • socket 通信,也就是网络通信。
  • 应用层协议解析,解析处理应用层协议,封装成一个 Request 对象。
  • 将 Request 转换为 ServletRequest,将 Response 转换为 ServletResponse。

Tomcat 通过 EndPoint、Processor 和 Adapter这 3 个组件来实现连接器,这三个组件之间通过抽象接口进行交互。从一个请求的正向流程来看, Endpoint 负责提供请求字节流给 Processor,Processor 负责提供 Tomcat 定义的 Request 对象给 Adapter,Adapter 负责提供标准的 ServletRequest 对象给 Servlet 容器:

  • Endpoint 和 Processor 可以自由组合,放在一起抽象成了 ProtocolHandler 组件,连接器用 ProtocolHandler 来处理网络连接和应用层协议。Connector中具体用事件处理器来处理请求【ProtocoHandler】;不同的ProtocoHandler代表不同的连接类型【所以一个Service中可以有多个Connector】 例如:Http11Protocol使用普通的Socket来连接的,Http11NioProtocol使用NioSocket连接。

  • EndPoint:对接 I/O 模型,提供字节流给Processor,监听通信端口,是对传输层的抽象,处理底层Socket的网络连接,用来实现 TCP/IP 协议的。 是一个接口,对应的抽象类为AbstractEndPoint,有很多实现类,比如NioEndPoint,JIoEndPoint等。在其中有两个组件,一个 是Acceptor,另外一个是SocketProcessor。 Acceptor用于监听Socket连接请求,SocketProcessor用于处理接收到的Socket请求。EndPoint 接收到 Socket 连接后,生成一个 SocketProcessor 任务提交到线程池去处理,SocketProcessor 的 Run 方法会调用 Processor 组件去解析应用层协议,Processor 通过解析生成 Request 对象后,会调用 Adapter 的 Service 方法。
  • Processor:对接应用层协议,提供Tomcat Request对象给Adapter,Processor是用于实现HTTP协议的,也就是说Processor是针对应用层协议的抽象。 Processor接受来自EndPoint的Socket,然后解析成Tomcat Request和Tomcat Response对象,最后通过Adapter 提交给容器。 对应的抽象类为AbstractProcessor,有很多实现类,比如AjpProcessor、Http11Processor等。
  • Adapter:遵循 Servlet 规范,提供ServletRequest给容器,ProtocolHandler接口负责解析请求并生成 Tomcat Request 类。 需要把这个 Request 对象转换成 ServletRequest。 Tomcat 引入CoyoteAdapter,这是适配器模式的经典运用,连接器调用 CoyoteAdapter 的 sevice 方法,传入的是 Tomcat Request 对象,CoyoteAdapter 负责将 Tomcat Request 转成 ServletRequest,再调用容器的 service 方 法,将请求适配到Servlet容器 Container 架构。

容器

容器主要用于封装和管理Servlet以及具体处理Request请求。Tomcat 中设计四大Servlet容器组件,分别是Engine、Host、Context、Wrapper--->Servlet,这 4 种容器不是平行关系,而是父子关系。在tomcat源码org.apache.catalina.core.StandardEngine四个都有其标准实现,每一个容器都有一个 Pipeline 对象。

  • Engine:整个 Catalina 的 Servlet 容器引擎,用来管理多个虚拟站点,一个Service最多只能有一个Engine,但是一个引擎可包含多个 Host。
  • Host:代表的是一个虚拟主机,或者说一个站点,可以给 Tomcat 配置多个虚拟主机地址,而一个虚拟主机下可包含多个 Context。
  • Context:表示一个 Web 应用程序,相当于我们在webapp下的应用,一个 Web 应用通常有多个 Servlet,一个Web应用可包含多个 Wrapper子容器。
  • Wrapper:servlet包装类,包装Servlet负责管理整个 Servlet 的生命周期,包括装载、初始化、资源回收等,每一个Wraper都包装着Servlet。

PipeLine和Valve(管道和阀门)

Engine、Host、Context、Wrapper容器都继承ContainerBase,而ContainerBase有StandardPipeLine对象,每个容器构造方法setBasic设置默认Valve(StandardEngineValve),PipeLine和Valve组件的init、start。

Connector使用容器Container处理请求时,connector.getService().getContainer().getPipeLine().getFirst().invoke(request,response);

启动流程

整个 Tomcat 其实就是一个 Catalina 实例,Tomcat 启动的时候会初始化这个实例,Catalina 实例通过加载server.xml 完成其他实例的创建,创建并管理一个 Server,Server 创建并管理多个服务, 每个服务又可以有多个Connector 和一个 Container。

我们知道启动tomcat是执行startup.sh脚本文件,这个脚本文件里又执行catalina.sh最终是执行org.apache.catalina.startup.Bootstrap类。

因此得知Tomcat从Bootstrap类main方法开始,以链的方式逐级调用各模块的init方法进行初始化, 待各个模块都初始化后, 又会逐级调用各个模块的start()方法启动各个模块;commonLoader、catalinaLoader、sharedLoader这三个类加载器打破双亲委派。

内嵌示例

接下来我们演示内嵌tomcat运行示例,创建maven项目,pom文件

<?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>spring-extend</artifactId>
<groupId>com.itxs</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion> <artifactId>tomcat-test</artifactId> <properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-core</artifactId>
<version>8.5.28</version>
</dependency>
<dependency>
<groupId>org.apache.tomcat</groupId>
<artifactId>tomcat-jasper</artifactId>
<version>8.5.28</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>5.3.9</version>
<scope>compile</scope>
</dependency>
</dependencies>
</project>

创建一个可以测试Servlet,MyFirstServlet.java

package com.itxs;

import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException; public class MyFirstServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {
doPost(req, resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws IOException {
resp.getWriter().print("hello tomcat servlet,access success!!!");
}
}

TomcatApplication.java文件

package com.itxs;

import org.apache.catalina.LifecycleException;
import org.apache.catalina.core.AprLifecycleListener;
import org.apache.catalina.core.StandardContext;
import org.apache.catalina.startup.Tomcat;
import javax.servlet.ServletException;
import java.io.File; public class TomcatApplication { public static int TOMCAT_PORT = 8080;
public static String TOMCAT_HOSTNAME = "127.0.0.1";
public static String WEBAPP_PATH = "src/main"; public static void main(String[] args) throws LifecycleException {
TomcatApplication.run();
} public static void run() throws LifecycleException {
Tomcat tomcat = new Tomcat();
tomcat.setPort(TomcatApplication.TOMCAT_PORT);
tomcat.setHostname(TomcatApplication.TOMCAT_HOSTNAME);
tomcat.setBaseDir("."); // tomcat 信息保存在项目下
StandardContext myContext = null;
try {
myContext = (StandardContext) tomcat.addWebapp("/itxs", System.getProperty("user.dir") + File.separator + TomcatApplication.WEBAPP_PATH);
myContext.setReloadable(false);
// 上下文监听器
myContext.addLifecycleListener(new AprLifecycleListener());
// 注册servlet
tomcat.addServlet("/itxs", "myFirstServlet",new MyFirstServlet());
// servlet mapping
myContext.addServletMappingDecoded("/first.do", "myFirstServlet");
tomcat.start();
tomcat.getServer().await();
} catch (ServletException e) {
e.printStackTrace();
}
}
}

运行main方法

并访问http://127.0.0.1:8080/itxs/first.do,收到结果页面

性能调优

关于性能调优方面可以考虑一下几个:

  • 内存:jvm参数,这个是实际生产中用的较多的。
  • 并发优化:connector开启线程池及线程池优化;
  • 缓存优化:connector 压缩和客户端连接超时;
  • IO优化:protocol 选择BIO NIO NIO2(AIO);
  • 组件优化:APR sendfile epoll openssl Tomcat native;

我们本篇只是简单理解tomcat架构和搭建tomcat源码调试环境,后续有时间我们再对tomcat源码和设计模式做进一步剖析和分享

解决Tomcat10.0.12源码编译问题进而剖析其优秀分层设计架构的更多相关文章

  1. 非寻常方式学习ApacheTomcat架构及10.0.12源码编译

    概述 开启博客分享已近三个月,感谢所有花时间精力和小编一路学习和成长的伙伴们,有你们的支持,我们继续再接再厉 **本人博客网站 **IT小神 www.itxiaoshen.com 定义 Tomcat官 ...

  2. spark2.1.0的源码编译

    本文介绍spark2.1.0的源码编译 1.编译环境: Jdk1.8或以上 Hadoop2.7.3 Scala2.10.4 必要条件: Maven 3.3.9或以上(重要) 点这里下载 http:// ...

  3. 英蓓特Mars board的android4.0.3源码编译过程

    英蓓特Mars board的android4.0.3源码编译过程 作者:StephenZhu(大桥++) 2013年8月22日 若要转载,请注明出处 一.编译环境搭建及要点: 1. 虚拟机软件virt ...

  4. [odroid-pc] ubuntu12.04 64bit Android4.0.3 源码编译报错及解决的方法

    第一个错误:         host Executable: cmu2nuance (out/host/linux-x86/obj/EXECUTABLES/cmu2nuance_intermedia ...

  5. android 5.0 (lollipop)源码编译环境搭建(Mac OS X)

    硬件环境:MacBook Pro Retina, 13-inch, Late 2013 处理器  2.4 GHz Intel Core i5 内存  8 GB 1600 MHz DDR3 硬盘60G以 ...

  6. hadoop2.0 eclipse 源码编译

    在eclipse下编译hadoop2.0源码 http://www.cnblogs.com/meibenjin/archive/2013/07/05/3172889.html hadoop cdh4编 ...

  7. Unix NetWork Programming(unix环境编程)——环境搭建(解决unp.h等源码编译问题)

    此配置实例亲测成功,共勉,有问题大家留言. 环境:VMware 10 + unbuntu 14.04 为了unix进行网络编程,编程第一个unix程序时遇到的问题,不能包含unp.h文件,这个感觉和a ...

  8. 【转】Unix NetWork Programming——环境搭建(解决unp.h等源码编译问题)

    下面开始用简单但典型的客户端和服务器端程序说明如何进行网络编程.这一小节讲的是客户端,一个用来连接并读取服务器发送来的时间的客户端. 这里涉及到了编写代码,因此要 搭建unix网络编程环境 unix系 ...

  9. Spark-2.0.2源码编译

    注:图片如果损坏,点击文章链接:https://www.toutiao.com/i6813925210731840013/ Spark官网下载地址: http://spark.apache.org/d ...

随机推荐

  1. 深度揭秘Netty中的FastThreadLocal为什么比ThreadLocal效率更高?

    阅读这篇文章之前,建议先阅读和这篇文章关联的内容. 1. 详细剖析分布式微服务架构下网络通信的底层实现原理(图解) 2. (年薪60W的技巧)工作了5年,你真的理解Netty以及为什么要用吗?(深度干 ...

  2. IDEA生成doc文档-生成chm文档

    首先,打开IDEA,并找到Tools -> Generate JavaDoc- 可供查询的chm比那些HTML页面好看多了. 如果您用过JDK API的chm文档,那么您一定不会拒绝接受其它第三 ...

  3. 网络协议之:一定要大写的SOCKS

    目录 简介 SOCKS的故事 SOCKS的历史 SOCKS协议的具体内容 SOCKS4 SOCKS4a SOCKS5 总结 简介 很久很久以前,人们还穿的是草鞋,草鞋虽然穿着舒服,但是不够美观.然后人 ...

  4. 【GS应用】基因组选择在杂交玉米上的应用示例

    目录 GS两步走 示例 缩短周期和成本 分类 杂交类型 试验研究 选择响应 选择的强度 选择的周期 预测能力 数据分析的注意事项 GS实施 优缺点 GS的成功 展望 GS两步走 示例 缩短周期和成本 ...

  5. Yii自定义全局异常,接管系统异常

    Yii自定义全局异常,接管系统异常 一般自己的框架都会使用一些自己封装的全局异常,那么在系统发生异常突发情况时候,即可自主的做一些异常机制处理,例如发送短信.发送邮件通知系统维护人员或者以更加友好的方 ...

  6. Mysql的delimiter

    告诉MySQL解释器,该段命令是否已经结束了,mysql是否可以执行了.默认情况下,delimiter是分号;.在命令行客户端中,如果有一行命令以分号结束,那么回车后,mysql将会执行该命令. 有时 ...

  7. Scrapy-Splash的安装和使用

    Scrapy-Splash是一个Scrapy中支持JavaScript渲染的工具. Scrapy-Splash的安装分为两部分.一个是Splash服务的安装,具体是通过Docker,安装之后,会启动一 ...

  8. Kubernetes:应用自动扩容、收缩与稳定更新

    在前面我们已经学习到了 Pod 的扩容.滚动更新等知识,我们可以手动为 Deployment 等设置 Pod 副本的数量,而这里会继续学习 关于 Pod 扩容.收缩 的规则,让 Pod 根据节点服务器 ...

  9. day14 linux三剑客之sed命令

    day14 linux三剑客之sed命令 sed命令 Sed 主要用来自动编辑一个或多个文件.简化对文件的反复操作.编写转换程序等. sed(流式编辑器) : sed主要用来修改文件. 1.sed命令 ...

  10. 容器之分类与各种测试(三)——queue

    queue是单端队列,但是在其实现上是使用的双端队列,所以在queue的实现上多用的是deque的方法.(只要用双端队列的一端只出数据,另一端只进数据即可从功能上实现单端队列)如下图 例程 #incl ...