Tomcat部署及其优化

news2024/9/27 21:08:35

目录

一、Tomcat概述

二、Tomcat的组成

三、Tomcat请求过程

 四、Tomcat服务部署

五、/usr/local/tomcat/目录下的主要目录说明

六、Tomcat虚拟主机配置

七、Tomcat优化

1.Tomcat配置文件参数优化

2.Tomcat JVM优化


一、Tomcat概述

Tomcat是基于java语言开发,是免费的开源代码的Web应用。Tomcat属于轻量级应用服务器,在中小型系统和并发访问用户不是很多的场合下被普遍使用,是开发和调试JSP程序的首选。一般来说,Tomcat虽然和Apache或者Nginx这些Web服务器一样,具有处理HTML页面的功能,然而由于其处理静态HTML的能力远不及Apache或者Nginx,所以Tomcat通常是作为一个Servlet和JSP容器,单独运行在后端。

二、Tomcat的组成

Tomcat由一系列的组件构成,其中核心的组件有三个:

  • Web容器:完成Web服务器的功能。
  • Servlet容器:名为catalina,用于处理Servle代码。
  • JSP容器:用于将JSP动态网页翻译成Servlet代码

因此Tomcat是Web应用服务器,也是一个Servlet\JSP容器。Tomcat作为Servlet容器,负责处理客户的.jsp动态页面的请求,把请求传递给Servlet,并将Servlet的响应传递给客户。

什么是Servlet?
Servlet是Java Servlet的简称,可以理解为是一个服务连接器,使用Java编写的服务端程序,具有独立于平台和协议的特性。简单理解为servlet就是一个中间件,包含了接口和方法,将客户端和数据库连接,从而实现动态网页的创建。

什么是JSP?
JSP全称Java Server Pages,是一种动态网页开发技术。它使用JSP标签在HTML网页中插入Java代码。通常以<%开头,以%>结束。
JSP是一种Java Servlet,主要用于实现Java web应用程序的用户界面部分。
JSP荣国网页表单获取用户输入数据、访问数据库及其他数据源,然后动态地创建网页。

Tomcat功能组件结构:

Connector:负责对外接收和响应请求。它是Tomcat与外界的交通枢纽,监听端口接收外界请求,并将请求处理后传递给容器做业务处理,最后将容器处理后的结果响应给外界。

Container:负责对内业务处理逻辑。其内部由Engine、Host、Context和Wrapper四个容器组成,用于管理和调用Servlet相关逻辑。

Service:对外提供Web服务。主要包含Connector和Container两个核心组件,以及其他功能组件。Tomcat服务器可以管理多个Service,且各个Service之间相互独立。

Contaniner结构分析

每个Service会包含一个Container容器。在Container内部包含了4个子容器:

  • Engine:引擎,用来管理多个虚拟主机,一个Service最多只能有一个Engine。
  • Host:代表一个虚拟主机,也可以叫站点,通过配置Host就可以添加站点。
  • Context:代表一个Web应用,包含多个Service封装器。
  • Wrapper:封装器,容器的最底层。每一个Wrapper封装着一个Servlet,负责对象实例的创建、执行和销毁功能。

三、Tomcat请求过程

  1. 用户在浏览器中输入网址,请求被发送到本机端口8080,被在那里监听的Connector获取
  2. Connector把该请求交给它所在的Service和Engine(Container)来处理,并等待Engine的回应
  3. 请求在Engine、Host、Context和Wrapper这四个容器之间层层调用,最后在Servlet中执行对应的业务逻辑、数据存储等。
  4. 执行完之后的请求响应在Context、Host、Engine容器之间层层返回,最后返回给Connector,并通过Connector返回给客户端

 四、Tomcat服务部署

1.在部署Tomcat前必须安装好jdk,因为jdk是Tomcat运行的必要环境。

rpm -qpl jdk-8u201-linux-x64.rpm 
rpm -ivh jdk-8u201-linux-x64.rpm 
java -version

2.设置jdk环境变量

vim /etc/profile.d/java.sh
......
export JAVA_HOME=/usr/java/jdk1.8.0_201-amd64
export JRE_HOME=$JAVA_HOME/jre
export CLASSPATH=.:$JAVA_HOME/lib:$JRE_HOME/lib
export PATH=$JAVA_HOME/bin:$JRE_HOME/bin:$PATH


source /etc/profile.d/java.sh
java -version

3.安装启动Tomcat

tar zxvf apache-tomcat-9.0.16.tar.gz
mv apache-tomcat-9.0.16 /usr/local/tomcat

#后台启动
/usr/local/tomcat/bin/startup.sh 
或
/usr/local/tomcat/bin/catalina.sh start

#前台启动
/usr/local/tomcat/bin/catalina.sh run


netstat -natp | grep 8080

配置systemd管理控制启动

vim /usr/lib/systemd/system/tomcat.service
[Unit]
Description=tomcat server
Wants=network-online.target
After=network.target

[Service]
Type=forking
Environment="JAVA_HOME=/usr/java/jdk1.8.0_201-amd64"
ExecStart=/usr/local/tomcat/bin/startup.sh
ExecStop=/usr/local/tomcat/bin/shutdown.sh
Restart=on-failure

[Install]
WantedBy=multi-user.target


systemctl restart tomcat    #启动
systemctl enable tomcat    #配置自启
systemctl stop tomcat    #停止服务
systemctl status tomcat    #检测状态

4.优化启动速度

第一次启动tomcat可能会发现 Tomcat 启动很慢,默认情况下可能会需要几十秒,可以修改jdk参数进行改。

vim /usr/java/jdk1.8.0_201-amd64/jre/lib/security/java.security
......
securerandom.source=file:/dev/urandom


systemctl restart tomcat

tomcat 启动慢的原因是随机数产生遭到阻塞,遭到阻塞的原因是熵池大小 。

  • /dev/random:阻塞型,读取它就会产生随机数据,但该数据取决于熵池噪声,当熵池空了,对/dev/random 的读操作也将会被阻塞。
  • /dev/urandom:非阻塞的随机数产生器,它会重复使用熵池中的数据以产生伪随机数据。这表示对/dev/urandom的读取操作不会产生阻塞,但其输出的熵可能小于/dev/random的。它可以作为生成较低强度密码的伪随机数生成器,不建议用于生成高强度长期密码。

五、/usr/local/tomcat/目录下的主要目录说明

  • bin:存放启动和关闭 Tomcat 的脚本文件,如 catalina.sh、startup.sh、shutdown.sh 
  • conf:存放 Tomcat 服务器的各种配置文件,如主配置文件 server.xml 和 应用默认的部署描述文件 web.xml 
  • lib:存放 Tomcat 运行需要的库文件的 jar 包,一般不作任何改动
  • logs:存放 Tomcat 执行时的日志
  • temp:存放 Tomcat 运行时产生的文件
  • webapps:存放 Tomcat 默认的 Web 应用项目资源的目录
  • work:Tomcat 的工作目录,存放 jsp 编译后产生的 class 文件

六、Tomcat虚拟主机配置

很多时候公司会有多个项目需要运行,一般不会是在一台服务器上运行多个 Tomcat 服务,这样会消耗太多的系统资源。此时, 就需要使用到 Tomcat 虚拟主机。

例如现在新增两个域名 www.bi01.com 和 www.bi02.com, 希望通过这两个域名访问到不同的项目内容。

1.创建 bi01 和 bi02 项目目录和文件

mkdir /usr/local/tomcat/webapps/bi01
mkdir /usr/local/tomcat/webapps/bi02
echo "This is bi01 page\!" > /usr/local/tomcat/webapps/bi01/index.jsp
echo "This is bi02 page\!" > /usr/local/tomcat/webapps/bi02/index.jsp

2.修改 Tomcat 主配置文件 server.xml

vim /usr/local/tomcat/conf/server.xml
--165行前--插入
<Host name="www.bi01.com" appBase="webapps" unpackWARs="true" autoDeploy="true" xmlValidation="false" xmlNamespaceAware="false">
	<Context docBase="/usr/local/tomcat/webapps/bi01" path="" reloadable="true" />
</Host>

<Host name="www.bi02.com" appBase="webapps" unpackWARs="true" autoDeploy="true" xmlValidation="false" xmlNamespaceAware="false">
	<Context docBase="/usr/local/tomcat/webapps/bi02" path="" reloadable="true" />
</Host>


systemctl restart tomcat

Host 

  • name:主机名
  • appBase:Tomcat程序工作目录,即存放web应用程序的目录;相对路径为webapps,绝对路径为 /usr/local/tomcat/webapps
  • unpackWARs:在启用此webapps时是否对WAR格式的归档文件先进行展开;默认为true
  • autoDeploy:在Tomcat处于运行状态时放置于appBase目录中的应用程序文件是否自动进行deploy;默认为true
  • xmlValidation:是否验证xml文件执行有效性检验的标志
  • xmlNamespaceAware:是否启用xml命名空间,设置该值与xmlValidation为true,表示对web.xml文件执行有效性检验

Context

  • docBase:相应的Web应用程序的存放位置;也可以使用相对路径,起始路径为此Context所属Host中appBase定义的路径;
  • path:相对于Web服务器根路径而言的URI;如果为空"",则表示为此webapp的根路径 / ;
  • reloadable:是否允许重新加载此context相关的Web应用程序的类;默认为false

七、Tomcat优化

Tomcat默认安装下的缺省配置并不适合生产环境,它可能会频繁出现假死现象需要重启,只有通过不断压测优化才能让它最高效率稳定的运行。优化主要包括三方面,分别为操作系统优化(内核参数优化),Tomcat配置文件参数优化,Java虚拟机(JVM)调优。

1.Tomcat配置文件参数优化

常用的优化相关参数如下:

  • redirectPort:
    如果某连接器支持的协议是HTTP,当接收客户端发来的HTTPS请求时,则转发至此属性定义的 8443 端口。
  • maxThreadsTomcat:
  • 使用线程来处理接收的每个请求,这个值表示Tomcat可创建的最大的线程数,即支持的最大并发连接数,默认值是 200。
  • minSpareThreads:
    最小空闲线程数,Tomcat 启动时的初始化的线程数,表示即使没有人使用也开这么多空线程等待,默认值是 10。
  • maxSpareThreads:
    最大备用线程数,一旦创建的线程超过这个值,Tomcat就会关闭不再需要的socket线程。默认值是-1(无限制)。一般不需要指定。
  • processorCache:
    进程缓冲器,可以提升并发请求。默认值是200,如果不做限制的话可以设置为-1,一般采用maxThreads的值或者-1。
  • URIEncoding:
    指定 Tomcat 容器的 URL 编码格式,网站一般采用UTF-8作为默认编码。
  • connnectionTimeout:
    网络连接超时,单位:毫秒,设置为 0 表示永不超时,这样设置有隐患的。通常默认 20000 毫秒就可以。
  • enableLookups:
    是否反查域名,以返回远程主机的主机名,取值为:true 或 false,如果设置为 false,则直接返回 IP 地址,为了提高处理能力,应设置为 false。
  • disableUploadTimeout:
    上传时是否使用超时机制。应设置为 true。
  • connectionUploadTimeout:
    上传超时时间,毕竟文件上传可能需要消耗更多的时间,这个根据你自己的业务需要自己调,以使Servlet有较长的时间来完成它的执行,需要与上一个参数一起配合使用才会生效。
  • acceptCount:
    指定当所有可以使用的处理请求的线程数都被使用时,可传入连接请求的最大队列长度,超过这个数的请求将不予处理,默认为 100 个。
  • maxKeepAliveRequests:
    指定一个长连接的最大请求数。默认长连接是打开的,设置为1时,代表关闭长连接;为-1时,代表请求数无限制
  • compression:
    是否对响应的数据进行GZIP压缩,off:表示禁止压缩;on:表示允许压缩(文本将被压缩)、force:表示所有情况下都进行压缩,默认值为 off,压缩数据后可以有效的减少页面的大小,一般可以减小 1/3 左右,节省带宽。
  • compressionMinSize:
    表示压缩响应的最小值,只有当响应报文大小大于这个值的时候才会对报文进行压缩,如果开启了压缩功能,默认值就是 2048。
  • compressableMimeType:
    压缩类型,指定对哪些类型的文件进行数据压缩。
  • noCompressionUserAgents="gozilla, traviata":
    对于以下的浏览器,不启用压缩

如果已经进行了动静分离处理,静态页面和图片等数据就不需做 Tomcat 处理,也就不要在 Tomcat 中配置压缩了。

以上是一些常用的配置参数,还有好多其它的参数设置,还可以继续深入的优化,HTTP Connector 与 AJP Connector 的参数属性值,可以参考官方文档的详细说明进行学习。
 

vim /usr/local/tomcat/conf/server.xml
......
<Connector port="8080" protocol="HTTP/1.1" 
connectionTimeout="20000" 
redirectPort="8443" 
--71行--插入
minSpareThreads="50" 
enableLookups="false" 
disableUploadTimeout="true" 
acceptCount="300" 
maxThreads="500" 
processorCache="500"
URIEncoding="UTF-8" 
maxKeepAliveRequests="100"
compression="on" 
compressionMinSize="2048" 
compressableMimeType="text/html,text/xml,text/javascript,text/css,text/plain,image/gif,image/jpg,image/png"/>

2.Tomcat JVM优化

  • -server:一定要作为第一个参数,在多个CPU时性能佳
  • -Xms:堆内存的初始大小,是分配JVM的初始内存,默认为物理内存的1/64。一般来讲,此值设的大点,程序会启动的快一点。
  • -Xmx:堆内存的最大大小,是分配JVM的最大内存,默认为物理内存的1/4。如果程序运行需要占用更多的内存,超出了这个设置值,就会抛出OutOfMemory异常。

默认空余堆内存小于40%时,JVM就会增大堆直到-Xmx的最大限制;空余堆内存大于70%时,JVM会减少堆直到 -Xms的最小限制。
因此建议-Xms与-Xmx设成一样的值,均设为物理内存的一半。其目的是为了能够在java垃圾回收机制清理完堆区内存后不需要重新计算堆区内存的大小而浪费资源。
----------------------------------------------------

-Xmn:堆内新生代的大小,通过这个值也可以得到老生代的大小:-Xmx减去-Xmn。官方推荐配置为整个堆的 3/8。
----------------------------------------------------

  • 堆区进一步细化分为:新生代、中生代、老生代。
  • java中每新new一个对象所占用的内存空间就是新生代的空间,当java垃圾回收机制对堆区进行资源回收后,那些新生代中没有被回收的资源将被转移到中生代,中生代的被转移到老生代。
  • 整个JVM堆大小 = 新生代大小 + 老生代大小 + 永久代大小

----------------------------------------------------

  • -Xss:设置每个线程可使用的内存大小,即栈的大小。一般情况下,设置256k就足够了,此配置将会影响此进程中并发线程数的大小。
  • -XX:ParallelGCThreads:配置并行收集器的线程数,即:同时有多少个线程一起进行垃圾回收。当 CPU 数量小于8,此值建议配置等于 CPU 数量。
  • -XX:PermSize:设置非堆内存初始值,即持久代内存大小,默认是物理内存的1/4
  • -XX:MaxPermSize:最大非堆内存的大小,即最大持久代内存大小,默认是物理内存的1/4

----------------------------------------------------

  • 非堆区内存是不会被java垃圾回收机制进行处理的,且最大堆内存与最大非堆内存的和不能超出操作系统的可用内存。
  • XMX和XMS设置一样大,MaxPermSize和PermSize设置一样大,这样可以减轻伸缩堆大小带来的压力。

----------------------------------------------------

  • -XX:+HeapDumpOnOutOfMemoryError:表示当JVM发生OOM时,自动生成DUMP文件
  • -XX:HeapDumpPath:表示生成DUMP文件的路径
  • -XX:+UseParNewGC:对新生代采用多线程并行回收,缩短垃圾收集的时间
  • -XX:+UseConcMarkSweepGC:并发标记清除收集器,它是老年代的收集算法,缩短垃圾收集的时间
  • -XX:+DisableExplicitGC:禁止调用System.gc(),防止误调用gc方法导致系统的 JVM 大起大落而使系统响应时间严重降低。
  • -Djava.awt.headless=true:避免在 Linux/Unix 环境下 Web 网页不能正常显示图片
  • -XX:+CMSParallelRemarkEnabled:启用并行标记,降低标记停顿
  • -XX:+UseCMSCompactAtFullCollection -XX:CMSFullGCsBeforeCompaction=0:这两个参数默认值就是这样的,表示触发FullGC时压缩堆,优化内存碎片
  • -XX:CMSInitiatingOccupancyFraction=70:在应用程序使用70%完内存后开始CMS垃圾收集

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

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

相关文章

传知代码-轻量注意力网络实现苹果叶片识别

代码以及视频讲解 本文所涉及所有资源均在传知代码平台可获取 引言 该系统基于EfficientNet与多头自注意力机制&#xff0c;构建了一个高效、精准的苹果叶片识别模型&#xff0c;能够对不同种类的苹果叶片进行准确分类。通过结合EfficientNet的强大特征提取能力和多头注意力…

Ks渲染做汽车动画吗?汽车本地渲染与云渲染成本分析

Keyshot是一款强大的实时光线追踪和全域光渲染软件&#xff0c;它确实可以用于制作汽车动画&#xff0c;包括汽车模型的渲染和动画展示。Keyshot的动画功能允许用户创建相机移动、物体变化等动态效果&#xff0c;非常适合用于汽车动画的制作。 至于汽车动画的渲染成本&#xff…

Power Platform开发小技巧,一天一个APP, 如何快速搭建二维码识别器

之前&#xff0c;给大家分享了微软Power Platform开发课程——手把手教你搭建二维码生成器&#xff0c;很多小伙伴反馈真好用。这期我们继续为大家分享Power Platform的开发能力与技巧。 今天介绍如何开发⼀个⼆维码识别器。 该应用包含如下功能&#xff1a; 1.⼆维码图片的…

尾矿库安全监测系统:守护矿山安全的关键技术

尾矿库是矿山企业用于存放尾矿的重要设施&#xff0c;其安全状况直接关系到周边环境和人民生命财产安全。近年来&#xff0c;随着技术的不断进步&#xff0c;尾矿库安全监测系统应运而生&#xff0c;为尾矿库的安全管理提供了强有力的技术支持。本文将详细介绍尾矿库安全监测系…

基于spi机制构造的webshell

前言 最近在翻阅yzddmr6师傅博客的时候&#xff0c;发现师傅还有个github的地址 https://github.com/yzddmr6/MyPresentations 里面发现师傅去补天白帽子大会上讲解了一些webshell的攻防&#xff0c;特此进行了学习&#xff0c;然后发现了一个很有意思的webshell&#xff0c…

YOLOv9改进,YOLOv9主干网络替换为PP-LCNetV2(百度飞浆视觉团队自研,轻量化架构),全网独发

摘要 PP-LCNetV2 是在图像分类任务中提出的一种轻量级卷积神经网络,用于在边缘设备上实现高效的推理。PP-LCNet 系列模型的设计旨在提高移动和边缘设备上的推理性能,同时保持较高的准确率。PP-LCNetV2 是在 PP-LCNetV1 基础上改进的。 理论介绍 PP-LCNetV2模型结构如下: …

数据库存储加密技术有哪些 TDE透明加密和列表级加密

透明数据加密&#xff08;TDE&#xff09;和列级加密是数据库加密中两种常见的加密方式&#xff0c;它们在加密范围、实现方式以及对应用程序的影响等方面存在明显的区别。 透明数据加密&#xff08;TDE&#xff09; 定义&#xff1a; 透明数据加密&#xff08;Transparent …

稀土阻燃协效剂-氢氧化镁(氢氧化铝)的应用

稀土阻燃协效剂凭借独特的稀土4f电子层结构,在聚合物材料燃烧时可催化酯化成炭,迅速在高分子表面形成致密连续的碳层,隔绝聚合物材料内部的可燃性气体与氧气的接触,从而达到阻燃抑烟的效果,且燃烧时不产生有毒有害气体。 金士镧系列稀土阻燃剂是一种基于稀土协效阻燃的复合阻燃…

CTF竞赛介绍以及刷题网址(超详细)零基础入门到精通,收藏这一篇就够了

CTF&#xff08;Capture The Flag&#xff09;中文一般译作夺旗赛&#xff0c;在网络安全领域中指的是网络安全技术人员之间进行技术竞技的一种比赛形式。CTF起源于1996年DEFCON全球黑客大会&#xff0c;以代替之前黑客们通过互相发起真实攻击进行技术比拼的方式。发展至今&…

安全防护装备检测系统源码分享

安全防护装备检测检测系统源码分享 [一条龙教学YOLOV8标注好的数据集一键训练_70全套改进创新点发刊_Web前端展示] 1.研究背景与意义 项目参考AAAI Association for the Advancement of Artificial Intelligence 项目来源AACV Association for the Advancement of Computer…

D18【python接口自动化学习】-python基础之内置数据类型

day18 综合练习&#xff1a;实现手机通讯录&#xff08;下&#xff09; 学习日期&#xff1a;20240925 学习目标&#xff1a;内置数据类型--27 小试牛刀&#xff1a;如何使用类型转换实现手机通讯录&#xff08;下&#xff09; 学习笔记&#xff1a; 实现手机通讯录 案例文…

以题为例浅谈反序列化漏洞

什么是反序列化漏洞 反序列化漏洞是基于序列化和反序列化的操作&#xff0c;在反序列化——unserialize()时存在用户可控参数&#xff0c;而反序列化会自动调用一些魔术方法&#xff0c;如果魔术方法内存在一些敏感操作例如eval()函数&#xff0c;而且参数是通过反序列化产生的…

占领矩阵-第15届蓝桥省赛Scratch中级组真题第5题

[导读]&#xff1a;超平老师的《Scratch蓝桥杯真题解析100讲》已经全部完成&#xff0c;后续会不定期解读蓝桥杯真题&#xff0c;这是Scratch蓝桥杯真题解析第190讲。 如果想持续关注Scratch蓝桥真题解读&#xff0c;可以点击《Scratch蓝桥杯历年真题》并订阅合集&#xff0c;…

【图像处理】多幅不同焦距的同一个物体的平面图象,合成一幅具有立体效果的单幅图像原理(一)

合成一幅具有立体效果的单幅图像&#xff0c;通常是利用多个不同焦距的同一物体的平面图像&#xff0c;通过图像处理技术实现的。以下是该过程的基本原理&#xff1a; 1. 立体视觉原理 人眼的立体视觉是通过双眼观察物体的不同视角而获得的。两只眼睛的位置不同&#xff0c;使…

【学习笔记】MIPI

MIPI介绍 MIPI是由ARM、Nokia、ST、IT等公司成立的一个联盟&#xff0c;旨在把手机内部的接口如存储接口&#xff0c;显示接口&#xff0c;射频/基带接口等标准化&#xff0c;减少兼容性问题并简化设计。 MIPI联盟通过不同的工作组&#xff0c;分别定义一系列手机内部的接口标…

猜拳数据集-石头-剪刀-布数据集

“石头-剪刀-布”计算机视觉项目是一个利用摄像头捕捉手势并识别出手势是石头、剪刀还是布的项目。这类项目通常用于学习和展示计算机视觉技术&#xff0c;如图像处理、特征提取以及机器学习或深度学习模型的应用。 数据介绍 rock-paper-scissors Computer Vision Project数…

基于状态机的流程编排架构设计

背景 xx产品侧规划了全新的能力升级&#xff0c; 主要思路为&#xff1a;改变之前通过xx等手工生成xx的方式&#xff0c;通过标准化流程尽可能的减少人工介入&#xff0c;提升产出效率。xx入库、xx生成链路存在链路长、链路不稳定问题&#xff0c;由于目前缺乏比较好的监控、检…

一文多图,彻底弄懂LSM-Tree

一文弄懂LSM-Tree LSM-Tree是什么&#xff1f; LSM-Tree&#xff08;Log Structured Merge Tree&#xff09;是一种数据结构&#xff0c;它被设计用于处理大量写入操作的场景&#xff0c;常见于许多NoSQL数据库中&#xff0c;如BigTable、Cassandra、RocksDB和LevelDB等。 L…

废品回收小程序:回收更加便捷!

在日常生活中&#xff0c;废品回收已经成为了一种常见事&#xff0c;随着电商的快速发展&#xff0c;居民难免会产生大量的废纸盒等可回收物&#xff0c;以及在日常生活中产生的其他回收物&#xff0c; 目前&#xff0c;废品回收市场也发生了改革&#xff0c;传统的“叫卖”方…

MySQL高阶1990-统计实验的数量

目录 题目 准备数据 分析数据 总结 题目 写一个 SQL 查询语句&#xff0c;以报告在给定三个实验平台中每种实验完成的次数。请注意&#xff0c;每一对&#xff08;实验平台、实验名称&#xff09;都应包含在输出中&#xff0c;包括平台上实验次数是零的。 结果可以以任意…