Linux运维 第三阶段 (二十) tomcat
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Linux运维 第三阶段 (二十) tomcat相关的知识,希望对你有一定的参考价值。
Linux运维 第三阶段 (二十) tomcat
一、相关概念(1、编程语言;2、servlet、jsp;3、tomcat):
tomcat(app-server server)
为提高tomcat工作性能,前端要引入很多组件(如cache server(varnish)同样对它生效)
1、编程语言:
php相关框架、网站程序设计涉及到的基本内容:
php:
开发语言,脚本语言,动态语言;
安装的php是个运行环境;
用php开发语言开发网站程序,这个程序在运行环境中解释执行,若每条指令都解释执行、每个用户请求的动态内容都解释执行这将非常慢;在php4时引入zend engine,使得php分两段式,先编译解释为opcode然后再执行,因此在同一个php进程内,若同一段代码要执行两次,第一次要编译,第二次则不需编译直接执行;类似httpd中的prefork模型,一个请求用一个进程响应,同一个进程有自己的内存空间,在php中一个进程所编译的结果第二个进程是无法使用的;因此引入Xcache(或APC或eAccelerator),可以在多个进程间共享opcode
MVC(模型model,视图view,控制器controller;一种软件设计模式,设计创建web应用程序的模式,目的使M和V的代码分离,从而使同一个程序可以使用不同的表现形式,如一批统计数据可分别用柱状图、饼图显示,C确保M和V同步,一旦M改变,V应该同步更新)
在大规模应用程序开发方面需要将开发分为三层(data layer数据层,business layer业务层,presentation layer展示层),将数据拿来做业务处理(计算)后展示给用户,同tcp/ip的分层(若分层后,每一层单独改进则不用全部修改,每一层将自己的功能通过服务向上层提供,上层通过调用接口调用下层的功能),在程序设计时分层的好处,可用三个团队各自负责自己最擅长的方面
php虽是开发语言,但它并不能轻易实现让data,business,presentation分开,要借助额外工具才能实现(php有很多框架用来实现分层功能,如smarty模板引擎),结合这种框架的语言尤其适合开发web应用,应用程序站点(用php做动态站点,php将数据读过来作出处理,处理结果通过网页html格式展示给用户,通过浏览器看到的是展示层)
若通过C语言开发网页(给C引入模板将其分层,这对C来说是非常困难的,C没有很好的跨平台性,C、C++与硬件架构(OS、CPU架构)结合非常紧密,所以在32bitOS编写的程序到64bitOS上是不能运行的,同样在intel架构的主机上编写的程序到amd架构的主机也不能运行,要重新编写,最大缺陷移植困难,维护也困难(发现某个代码错误,修改后又要重新编译),虽与汇编语言比方便移植,但对互联网应用来说移植很难;C、C++最大好处高效,运行速度快,C在性能上虽与汇编语言没法比,但与php、java等这些动态语言至少快30%,经过良好优化可快50%,所以C、C++通常用于开发驱动程序及底层应用(如OS、DB);现在很多大型软件都是结合多种语言共同开发,在C中可以嵌入其它语言,在java中可以嵌入C,python中嵌入java等,彼此间互相调用(互操作);C出现时高级语言并不多,到现今C仍很流行,仍然是除java外被程序员使用最多的一种,C比C++要简单很多,C面向过程,C++面向对象,面向对象比面向过程更容易构建大型程序,尽管如此linux内核是用C写的,没有任何一种语言适应开发任何应用,每一种语言都有其适合场景
API(application programming interface)将系统的系统调用system call二次封装成函数,以方便程序员使用,这样程序员就不用写驱动直接拿来用,OS本身就是一个虚拟机,把底层硬件做了一层抽象,并将抽象的结果使用比较容易调用的方式输出给程序员,程序员借助system call就能编程了,但system call过于底层,而且功能简陋(设计哲学所限定,功能简陋数目越少性能就越高),并非每个API都包含system call,有些API是纯粹的API仅完成一些功能没有system call,而有些API中的库可能一个就调用了N个system call,如果OS不同那API就不一样,所以在win下开发的程序是不能在linux上编译的(win下的API和linux下的API是不同的),编译时要检查所依赖的库是否存在、调用的库所遵循的语法是否相同;为便于程序的移植性,于是就有POSIX(portable operating system,为兼容UNIX的风格加了IX,有了规范即可互操作),只要双方都遵循编程接口规范,那win和linux就可实现跨平台编译(可跨OS编译,但不能跨OS运行,除非再引进一种抹合ABI的机制,而java正是这么一种语言)
例:linux上使用API开发了一个程序在linux上完成了编译,编译完后是不能在win上运行的(尽管都遵循同一种API,但ABI(application binary interface)是不一样的,二进制格式的程序不同(#file/bin/ls,是ELF格式),且动态库也不同(win上的动态库是dll,linux上的动态库是so)
JAVA
早期叫green-->oak-->java,SUN公司90年代初为当时实现智能电视计划专门研发的语言,用同一种语言开发的程序要能实现在不同的硬件平台上都能运行,基本着眼点在于跨平台性,java在智能电视计划上并没发挥出来,但随着http的出现,互联网爆炸式发展,web应用迫切需要一种跨平台语言(html开发工具所开发的是静态网页,要开发动态站点意味着开发出的程序要能在任何一个client的主机上(不同的硬件及不同的OS)都能运行,必须跨平台),java语言被用到开发小程序上
java是程序设计语言中最流行的语言,没有之一
java最大特点:跨平台,靠JVM实现一次编译到处运行(write once run anywhere)
java组件,包含四个独立却又相关的技术:
java程序设计语言;
java API(C语言为加速C的开发有C库,java也一样为使java开发更高效提供java库,java官方提供了很多java库即java API;有SUN定义的规范,开放组织也定义了一堆的受欢迎的API,还有第三方的类库);
java class(byte code字节码所遵循的规范是由java class文件格式定义的,虽不精确有助于理解);
JVM(java virtual machine;SUN的hotspot JVM(JRE、JDK)更成熟;open JDK(包含的不仅仅是JVM,bug多,淘宝在用但在不断更新)
注:JRE(java runtime environment,JRE=JVM+API仅用于运行,终端用户用,此处API仅一部分库,开发时的库没有)
JDK(java development kit,开发+运行,实现程序开发的最小环境,包含JRE,JDK=java语言+API+JVM,开发编译并运行,程序员用)
为完成程序开发,除JRE和JDK外,还提供调试工具、监控工具、打包工具等(均命令行工具)
在linux上开发了个C程序,这个程序要能完全运行还依赖库*.so,虽然已编译好程序是独立的,若没有相应共享库仍不能运行(类似自定义的micro linux OS,每一次运行一个命令都要将相应的库文件复制过来),java也类似,源程序是针对某个java API编写的,用java complier编译时它针对某个API编译,所以编译完后要依赖那个API对应二进制格式才能运行,由此还要装载额外的库(API本身提供的库)
注:API在开发时表现为头文件(库的名称、函数、参数的格式、参数类型、参数调用方法、返回值的类型、返回多少值等规范)和库文件,运行时表现为库文件(库里有一堆的函数),程序到底用哪个函数(如shell脚本中的函数,脚本中要将函数导入,没有定义函数或未导入将无法调用),库在程序运行时就要装载到内存,否则无法运行
JVM是一专用的运行环境,提供一类程序所运行的平台,但它不能保证每个程序都能运行起来,通过class loader要把公共类和私有类都要自己装载进来才行(例如linuxOS,它提供平台让某些应用程序在这个平台上运行,至于你有没有相应的库文件,OS是不管的,得自己负责加载,linux中是通过linux-gate.so这个库自动调用其它库,不用程序本身负责加载所依赖的库文件,而java提供了一个工具(class loader)来自动加载库文件)
最后这些代码都要放到CPU上运行,要通过内核调度和理解(OS必须能理解这种程序才能运行),JVM要抹合底层OS的不同,JVM本身有两段,与OS平台无关的代码转为与OS平台有关的代码才能在某OS上运行(JVM提供与某OS底层有关的段来让java代码在该平台上运行,针对不同平台有不同的代码)
class loader类加载器(自动装载所依赖的库文件,公共类(java API)和私有类(自己开发的函数))
使用java程序设计语言 + java API = 开发出的源程序(*.java格式),源程序编译后是*.class格式,最后通过class loader加载公共类(java API)和私有类(自己开发的函数)将*.class运行在JVM上
JVM的实现方式:
一次性的解释器(解释byte code并执行,解释的过程是将byte code转为真正的二进制格式,第一次解释完,下次还要解释,每次执行都要编译,将与平台无关的byte code转为与平台有关的代码)
即时编译器(just-in-time complier,将byte code解释完后缓存下来,第二次执行时直接从缓存中取,要依赖更多内存用于缓存编译结果,这样每个程序解释完都要占用内存来缓存,甚至某些程序解释完还包括有数据,这将导致占用大量内存)
自适应编译器(能监控所有代码中,哪些代码执行效率最高,哪些代码执行频率低,能仅缓存那些代码执行频率高的,根据二八法则(80%的任务由20%的程序运行的,只缓存这20%的代码))
java应用领域不同,java分为三类:
java SE(standard edition,早期叫J2SE)
java EE(enterprise edition,J2EE)
java ME(mobile edition,J2ME,专用于开发手持设备的app,企业中用的很少,移动开发用的是android或object-c;android本身也是JAVA程序,后端是经优化后的用于移动平台的JDK,属于google公司;object-c继承C语言的特性,扩充C的面向对象的编程语言,在Mac Os下用苹果提供的SDK等开发工具包,可用来做ios开发)
2、servlet、jsp
applet
特殊的类,可理解为小程序,java在其类库中引入applet,这种类所研发的小程序让html的开发者在开发的网页页面中直接提供一个applet小程序,只要client在浏览器上安装了JRE插件就能实现让applet小程序在client的JVM上运行起来,根据client本地的运行环境能将执行结果通过网页显示给用户,由此动态网站成为现实
applet小程序要先编译好嵌入到html中,client在浏览网页时要将html和小程序都要下载到本地,这个程序在本地执行,是client动态网站(类似win中的ActiveX机制)
缺陷:client要配置JRE比较麻烦;有安全隐患,若开发者不怀好意导致源程序有问题则client就要遭殃;要先下载到本地再运行,早期带宽小影响速度等
CGI技术(common gateway interface)
能让用户访问某种特定资源的时候触发web server调用额外的程序来执行,web server通常只能服务静态页面,http借助于MIME实现传输更多文件格式(client的browser支持着各类MIME插件),这样用户请求的资源可以是任意格式,通过http可将所有资源都转换为文本格式,传输完成后再还原回原来的格式类型,若client请求了a.cgi的文件,cgi本身又作为处理器,这个处理器能基于cgi协议调用某个应用程序,web server发现请求的是cgi,额外再启动一个进程,a.cgi在新启动的这个进程上执行,执行完将结果返回给web server,web server再响应给client,实现让web server额外启动其它进程用来执行用户请求的动态类的资源在本地执行完后格式化为html再返回给client的技术
在CGI框架下,任何内容都由CGI生成(包括静态内容),通过这种方式开发的站点,网页内容一点点的改变都要重新编译,若是php处理动态内容,这样缓存下的opcode都要失效
在CGI模式下,用户请求的动态内容通过CGI协议交互的程序执行,执行完后是纯文本信息通过browser显示的杂乱无章的,在CGI技术上没法说html标签是由web server提供,而处理结果由CGI程序提供,所有内容都由后端server生成,由此html文档都要通过php给print出来(php能处理显示)
servlet
用java语言实现了CGI技术,用java语言开发网站遵循servlet规范,增加了对http协议的处理能力,servlet能让用户接受http请求,理解http协议,并且将程序执行的结果以http报文格式封装再通过web server响应给client
用servlet开发动态站点,servlet是CGI技术,java语言开发出的程序是.java编译完后是.class(byte code)并依赖公共类和私有类才能运行,对应的若源文件中内容有变化要再次编译,这种机制维护起来不便,servlet逃脱不了CGI的限制,任何一个静态内容都要在java程序中生成,这使得java程序员必须懂html
jsp(java server page)
jsp是servlet的升级,在html中嵌入了java代码,主要用于实现网页开发,通过jsp的编译器(jasper)把jsp转为servlet(.java)再用java complier编译为*.class才能运行
SSH(structs spring hebernate,开发jsp程序的框架)
jsp技术就是在servlet之外能以嵌入式方式写动态的java页面,并且能监控这个页面什么时候改变,能随时转换成servlet处理的技术
jasper负责监控源文件(*.jsp)的改变并随时按需将源文件转为.java再交至servlet执行
jsp要比php运行性能好(java语言比php程序要成熟规范的多),一般大型应用或对性能有要求的都使用jsp(淘宝早期用php,现大多数功能都转到jsp上;facebook将php代码转为C++代码再编译执行)
applet(基于client)、servlet(基于CGI)、jsp(嵌入HTML中),这些都是java类库,用于在某种场景下加速java开发速度
servlet container(servlet容器)
servlet container通过CGI与web server交互,把用户请求的内容接收进来在本地的JVM上运行,它负责接收CGI传递来的请求,负责监控本地的*.java程序是否发生了改变或修改,如果修改了负责转换为*.class
servlet container = servlet + JDK
web container(web容器)
站在tomcat的角度说,把包含进来的web server称作连接器,有连接器可与用户直接交互,也可以把连接器用于与前端web server交互
web container中的连接器是将前端的web server包含进来,就算前端没有web server解码http请求、封装http报文,这里的连接器代替它直接与client交互
连接器直接面向client和前端使用代理的区别(如LAMP架构用一台主机压力会很大性能要差很多,于是将其分层,在前端装nginx,所有连接请求与nginx建立,所有连接维持会话都在nginx上,若请求的是动态内容可转至后端处理;有了连接器后虽可直接面向用户,若这个连接器性能不强,那压力会很大,所以在前端放一web server解决会话连接建立和释放的压力,若前端web server再有缓存则web container压力会小很多)
web container = servlet container + jasper + connector
以下举例有助于理解:
web service网络通讯需要遵循http协议,所有请求都必须使用http协议来封装,web server在前端接受用户请求,并用后端处理好的数据再次封装报文并响应给用户。当用户请求到达,web server理解报文拆开报文并取出里面的东西交给jsp和servlet去处理,jsp和servlet负责对接收到的请求(web server解包后的数据)进行处理,然后返回结果给web server,由web server再封装成http报文发送给用户。打个比方说,web server就是好像是一个工厂的采购员和销售员,接收货物和发送货物都要经过他统一处理,收到原材料的时候,卸货、分类入库、交给使用的部门,他不负责采购来的原材料的加工处理, 原材料由jsp和servlet统一加工处理成产品,怎么生产怎么处理,生产什么样的产品,都是它们(jsp和servlet)的事,但是它们不管原材料和包装销售,你可以在这里面自由编程生产你想要的产品。 要对外返回销售的时候,得有统一的包装部门统一包装好,发送货物处理,这个也是web server负责的。 也就是: web server统一管理收原料,打包装发货物,jsp和servlet管加工管处理。 tomcat干的事就是把这整个工厂干的活都包了,从采购到加工到销售,他全会。
无论servlet container、web container、JDK最终它们都要运行在JVM内的环境中
servlet container、web container在OS上启动起来之后都表现为一个JVM进程,在这个JVM内部,若是web container它就有web container的功能,如果是servlet container它就有servlet container的功能,这个叫JVM instance(JVM实例),如果多个用户同时发起请求,而且前端web server要并发连接多个请求,这么多请求要并行执行(连接器只有一个),JVM instance的进程创建和删除比CGI进程的代价要大得多,所以一般不会为每一个请求创建一个JVM instance,而要在同一个JVM instance中启动多个线程(java本身支持多线程编程模型),这么多线程要在同一个JVM进程上运行,这使得JVM的内存空间变得非常复杂
JVM运行时数据区域(线程私有内存区、线程共享内存区):
线程私有内存区(程序计数器、java虚拟机栈、本地方法栈):
程序计数器(program counter register,java把自己模拟成一个虚拟机,在CPU内部就有一个程序指针,每一个进程(指令+数据)在CPU上运行时,某条指令运行时就要载入下一条指令执行,当前运行到哪一条指令若要切换出去就要将状态保存下来,程序计数器是用来保存java代码中的程序执行到第几条指令,而java代码不是运行在CPU而是运行在java虚拟机中,所以java虚拟机必须要模拟出CPU效果,每一个线程都要一个计数器,所以程序计数器是每线程私有的,每一个JVM instance在某时刻会调度某个线程运行在一颗或多颗CPU上,JVM instance为每个线程在某个独立的CPU上维持一个程序计数器)
java虚拟机栈(JVM stack,每线程私有的内存空间,描述的是java方法执行的内存模型,每个方法执行会创建一个栈帧stack frame,栈帧中通常存放局部变量、方法出口、动态链接指针、操作栈等;每次方法调用都会有一个栈帧放入虚拟机栈,OS给JVM分配的内存是有限的,JVM分配给虚拟栈的内存是有限的,如果方法调用过多,导致虚拟栈满了就会溢出,栈帧的数量叫栈深度;会有StackOverFlowError(线程请求的栈深度大于虚拟机所允许的深度)和OutOfMemoryError(扩展时无法申请到足够的内存))
本地方法栈(native method stack,java虚拟机方法有两类(java方法和本地方法),java方法(java程序自身实现的方法,java虚拟机与硬件交互完成某些特定功能要用system call,方法区中的方法要转为system call这样会很慢);本地方法(将java中的某些功能通过本地方法来实现,java编程+本地OS编程,使得与硬件交互时直接使用CAPI,通过system call直接与硬件打交道,执行性能要比java方法要快);java某些方法既能通过java自身代码来实现,也能通过调用外部的其它编程语言(本地方法)来实现;本地方法栈与java虚拟机栈实现的功能是一样的,是本地方法的某些进程用到时实现的;会有StackOverFlowError和OutOfMemoryError)
线程共享内存区(方法区、堆):
方法区(method area,某一类特定对象执行的方法的集合;如shell脚本是面向过程的,要完成某种功能,理解实现的过程一条条的往下写指令即可;java面向对象则是从抽象-->具体,类(操作)、对象(方法),实例化的过程,实例化操作方法(例如车-->小轿车,车-->挖掘机,功能将会确定下来),每一类执行的操作可理解为方法;会有OutOfMemoryError)
堆(heap,最大的内存空间,用于存放对象,对于面向对象编程,所有文件都是类文件,只要一执行就要创建很多对象,对象要执行操作(方法),执行方法的过程就是执行程序的过程,程序要计数器(每一个实例化的程序要保存自己的私有变量),堆就是实例出的对象,对于面向对象编程来讲,对象是很多的,而且有些还很大,堆是GC管理的主要区域;在JVM启动时,堆就直接被启动,堆大小是可扩展的(通过-Xmx最大值和-Xms最小值),如果无法扩展将有OutOfMemoryError)
方法区中还有个组件:运行时常量池runtime constant pool,是在方法区中保持常量的一段子区域
运行时的数据区域通常会用到直接内存direct memory,类似mmap,在JDK1.4中引入了NIO(new input output)机制,基于通道与缓冲区的IO方式与OS共享内存互相操作,避免数据复制,这样可提高java性能
以上这些内存区域都有可能溢出,要随时监控着JVM的运行状态(是否有溢出),主要关注:heap、method area、JVM stack,只要涉及到内存分配和回收都有可能产生溢出,重点是堆区,对象已运行结束,认为对象已死,意味着可以回收,但并非每个时刻GC(garbage collector垃圾回收器)都会来回收,对象死后给每个对象做好标记,而后一批一批的回收,GC完成垃圾回收时有很多算法:
标记清除算法(最简单最常用,将已死的对象清除,会造成大量碎片)
复制算法(对标记清除算法的改进,每一个对象在存储时分两份,分两个内存区域,一个内存区域存储对象另一个空闲,用的时候再存数据,这样内存区域只有一半可用,每个对象通过复制来进行回收继而再创建,回收后不会产生碎片,但浪费空间)
注:类所创建出的对象中98%的对象都是朝生夕死,这样通过监控2%的长久使用的对象使用复制算法,而其它的对象使用标记清除算法效果最好
标记整理算法(对复制算法的改进,仍使用标记清除,每个对象创建完都做下标记,但后续不是清理,不用时将它移到另一端(内存分两段,一段用于创建不用的都移到另一端,回收后避免内存碎片)
回收针对两类对象(young新生代对象、old老龄化对象)
垃圾回收器GC(将算法得以实现):
serial(单线程完成新生代对象收集,工作在新生代空间,对那些朝生夕死的对象回收,一次只能回收一个对象)
ParNew(parallel new,实现多线程回收,对serial算法的进一步调优)
parallel scavenge(尽可能避免回收时对正常进程的影响,吞吐量高,主要降低垃圾回收的时间)
serial old(针对old对象)
parallel old(针对old对象)
CMS(concurrent mark sweep,并行标记清除,很优秀,实现并发收集,低停顿;缺陷:无法收集浮动垃圾(运行中的线程产生的垃圾),由于基于标记清除算法可能会产生碎片)
G1(garbage first,对CMS的改进,不会产生碎片,停顿时间可自定义(可操作性强))
启动虚拟机时以上这些GC可自定义,根据实际需要选择并调整GC
任何一个类都要在JVM运行时数据区域中运行,运行时依赖公共类或私有类由class loader加载至运行时数据区域中(运行时数据区域之外有class loader,可使用JVM自带的类加载器完成类加载,也可调用自己开发的类加载器),分配好内存空间、创建一个线程,轮到它时就可运行,最后靠执行引擎来运行
类的生命周期(一个类从加载到卸载出去):
loading加载(在new生成一个对象,有各种方法,通常有个引用)
verification验证(安全相关)
preparation准备(设置初始值、分配内存空间、准备初始化内存变量、准备创建线程)
resolution解析(直接引用、间接引用)
initialization初始化(classloader的最后一步)
using使用(执行)
unloading卸载
servlet是CGI技术,能够使java开发动态页面并通过CGI方式与前端webserver通信,servlet对所有代码的编码是硬编码,静态页面的html格式也要由java输出,这非常麻烦任何静态内容的修改也导致需重新编译,使java程序员必须要懂html解决前端展示的定义;后引入jsp,可实现让java程序以标签方式嵌入到html文档中,用jasper将.jsp(jsp程序风格的编码)转为.java(servlet风格的编码)
JAVA EE包含多个独立的API,servlet和jsp就是其中两个,还包括:
EJB(enterprise java beans,java相关的诸多高级功能的实现,如RMI,remote method invocation,对象/关系映射,跨越多个数据源的分布式事务等)
JMS(java message service,高性能异步消息服务,实现JAVAEE应用程序与非java程序的透明通信)
JMX(java management extensions,在程序运行时对其进行交互式监控和管理的机制)
JTA(java transaction API,允许应用程序在自身的一个或多个组件中平滑地处理错误的机制)
JavaMail(通过工业标准的POP|SMTP|IMAP协议发送和接收邮件的机制)
JAVA SE 包含的API:
JNDI(java naming and directory interface,用于与LDAP服务交互)
JAXP(java API for XML processing,用于分析及转换XML,基于XSLT实现)
JAVA SE = JAVA SE的API + JDK
JAVA EE = JAVA EE它自身的API +JAVA SE的API
Web container(遵循JAVA EE规范能兼容JAVA EE中所有的API,只要开发出的程序基于JAVA EE的API就能够在这样的应用程序上(web container)部署并运行起来)
JAVA EE application servers(JAVA EE应用程序服务器有):
Websphere(IBM,商业)
Weblogic(oracle并购BEA得来,商业)
oc4j(oracle,商业)
tomcat(ASF,开源)
JBoss(redhat,核心是tomcat,在tomcat上作了二次开发,引入了很多功能,尤其企业级的应用接口,开源)
JOnAS(ObjectWeb协会开发,整合了tomcat和jetty成为它的容器,开源)
Geronimo(ASF,杰罗尼莫,开源)
GlassFish(有商业版和社区版)
resin(CAUCHO,开源,比tomcat更轻量级)
3、tomcat
先是sun研发了演示性的application server(TWS,RI,reference implementation),并不关心是否稳定,只大致说明所谓的应用程序服务器是这个样子;后ASF(apache software foundation)研发了Jserv,同sun的TWS一样丑陋;后sun将它的 以上是关于Linux运维 第三阶段 (二十) tomcat的主要内容,如果未能解决你的问题,请参考以下文章 Linux企业运维——Kubernetes(二十)Prometheus监控