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将它的TWS捐献给了ASF,ASF将TWS和Jserv进行揉合重新开发并封装,项目名称catalina;计算机出版公司O’Reilly让ASF的程序员写书介绍catalina这个项目,并给这个项目提供的图片是公猫,之后项目名称也更名为tomcat,但代码名称依然叫catalina,程序名是apache tomcat
tomcat在4.0时就揉合了Jserv和TWS的所有技术并重写,4.0版本就稳定了,而5.0版本彻底让tomcat站稳脚跟,6.0足以在生产环境中使用,如今很多公司都在用,它是个开放性框架,可根据需要做二次开发
严格意义讲tomcat并不是真正意义的application server(JAVA EE定义的很多API,tomcat仅实现了最基本的两个servlet和jsp,另外几个都没提供),若要使用JAVA EE提供的所有的API的话,商业化的application server才是更合适的选择,但绝大多数的电子商务公司仅需servlet和jsp即可,tomcat虽不完整但很流行,只要是用jsp开发的网站绝大多数用的都是tomcat,由于遵守apache开源协议,tomcat可以将其嵌入到众多的其它应用程序服务器产品中(如JBoss、JonAS),很多企业也渐渐抛弃使用传统的EJB转而采用开源组件(如SSH(structs spring hibernate)来构建复杂应用,而tomcat可对这些组件实现完美的支持
tomcat的体系结构(具备web container的架构和组件):
tomcat在自身内部实现了用java开发的web server,这个web server运行起来可完完全全工作在前台
在server内部可有多个engine,但一般只启动一个engine,若有多个engine怎么能够将请求转至哪一个engine,靠connector实现将server接收的请求转发至某个engine
一个server可实现将发往多个不同connector的请求,通过不同的connector转发至同一个engine上;若一个server内部有多个engine,意味着一个server内部有多个service,那怎么实现将发往多个不同connector的请求转发至不同的engine,靠service实现将一个或多个连接器关联至某个engine
一个service内部只能有一个engine,通常一个tomcat内部仅一个service一个engine可有多个connector
server(每一个server是一个tomcat实例,启动一个tomcat就启动了一个server,之所以称为server它能解析http请求,若用户请求的是.jsp页面它可调用engine中的jasper实现转换)
service(将一个或多个connector关联至某个engine)
connector(将server与engine联系起来,将server接收的请求转发给engine)
engine(可理解为是servlet container的实现,具备servlet container功能的java虚拟机;这这里的engine又可将.jsp解码为.java,这就具备了web container的功能,但主要目的是servlet container的功能;engine的位置是java虚拟机所在的位置)
host(在engine内部,实现类似虚拟主机的功能,但tomcat只提供基于FQDN的虚拟主机;例如httpd中一个物理主机可有多个,这里的engine就相当于一个JVM instance(或可理解为相当于web server的进程),有可能接收进来多个请求,不同的请求是在不同的主机上,host就是用来实现虚拟主机的;一个engine内部可有多个虚拟主机,而某个用户请求进来他没说请求哪个主机,这里要给engine指定一个默认主机,当用户访问了一个不存在或没有明确定义的主机时使用默认主机)
context(上下文,主要功能用来定义类似httpd的路径别名,一个host内部可有多个context,在java内部每一个context可单独部署一个应用程序,例如:网页根目录(/web/htdocs/discuz),定义别名alias /phpwind(/web/bbs/phpwind),则访问http://www.magedu.com/phpwind这个路径下就是个独立的应用程序)
注:php开发完即可运行不需要额外的资源;java不是这样的,开发完还要依赖公共类和私有类,类要通过class loader完成装载,程序才能运行;php每个独立的应用程序量级小,tomcat每个应用程序都要专门去部署,java比php要重量级的多;这也是要专门用组件context描述一个应用程序怎么去部署
部署(将一个应用程序放在host或context下,并将这个应用程序依赖的库装载完成的过程),将一个web应用程序所依赖的库装载进JVM,每一个应用程序都应该有自己的部署描述符,如果没有继承默认的(/usr/local/tomcat/conf/web.xml)
容器类组件:engine,host,context
顶级组件:server,service
还有一堆其它组件(在JVM内部使用java类实现不同的子功能,如在httpd中实现基于用户认证、基于IP认证等,类似这些功能engine不具备,要在这些基本组件上提供相关的类来完成):
Realm组件(用户认证,对tomcat来讲Realm就是个用户账号数据库)
Valve组件(这是一类组件,不同的实现方式能完成不同的功能,相当于是个过滤器,如记录访问日志(Valve说明哪些日志信息可记录,如仅记录info级别的日志等)、基于IP认证)
注:Realm组件、Valve组件均可放在engine、host、context上,可对不同位置做不同的处理
Logger(日志记录器,负责记录日志,用于定义日志文件位置、名称、大小、滚动等,Logger不能用在context上)
GlobalNamingResources(全局命名资源,用于整个服务器的JNDI映射,与LDAP关联)
WatchedResource(被监控的资源,若监控到这个资源改变将通知需要监控资源的资源,可用于context中监视指定webapp文件的改变,一旦改变重新装载,一般任何应用程序都要用此项)
Listener(用于创建和配置LifecycleListener对象,生命周期监听器,开发人员用于创建和删除容器)
Loader(重要,运行时动态装载所依赖的类)
Manager(重要,用于实现http会话管理的功能,tomcat6中有5种实现(StandardManager,PersistentManager,DeltaManager,BackupManager,SimpleTcpReplicationManager)
Stores(PersistentManager必须包含一个Stores元素指定会话的数据存储到何处,有FileStore和JDBCStore两种实现方式)
Resources(经常用于实现在Context中指定需要装载的但不在tomcat本地磁盘上的应用资源,如java类、HTML页面、jsp文件等
Cluster(专用于配置集群的元素,channel、membership、sender、transport、receiver)
注:StandardManager(tomcat6默认的会话管理器,用于非集群环境单个运行状态的tomcat实例的会话进行管理,会话数据会保存在FS上,文件SESSION.ser,tomcat重启时会读取此文件)
PersistentManager(当一个会话长时间处于空闲状态时会被写入到swap会话对象,对于内存资源比较吃紧的应用环境比较有用)
DeltaManager(用于tomcat集群的会话管理器,用于将改变了的会话数据同步给其它node实现会话复制,能在内存中完成会话复制;缺陷,若node过多,会通知给集群中的所有node,过于占用内存资源及网络带宽)
BackupManager(用于tomcat集群的会话管理器,DeltaManager的简化版,一个node会话的改变只复制给另外一个node,而不是集群中的所有node)
SimpleTcpReplicationManager(已废弃obsolete)
以上组件中最关键的是:server,service,connector,host,context,Realm,Logger,Valve,DeltaManager
tomcat本身基于java开发,内部的所有组件也都是java程序,包括server上提供的webserver,它的部署方式:
独立运行方式stand alone(tomcat自己提供服务和容器,对于内部的web server,java本身支持多线程,性能虽不错,但它既要处理web请求又要处理动态请求很繁忙)
代理方式(代理可隐藏后端主机,提高安全性,使用httpd或nginx作为前端的web server用于接收和响应用户请求,仅对动态请求.jsp转至tomcat上,这样可降低tomcat任务,它就不用处理静态内容了,前端的代理和后端的tomcat可放在同一主机也可分层工作在不同主机)
注:若采用代理方式,前端的httpd或nignx要工作在反向代理模式下;最好是整合httpd与tomcat,同一组织研发,技术及功能之间能衔接紧密,好处还体现在httpd可将用户请求以二进制格式方式AJP(apache Jserv protocol)向后转发,所以效率要高;若是nginx只能使用http协议方式(定义upstream组,并基于某种算法,配置动静分离,静态内容本地处理,动态内容.jsp或.do的转发到后端tomcat server上)
httpd通过mod_proxy、mod_jk(mod_jk2已废弃)这几组模块都能实现以集群LB方式连到tomcat上,httpd在向后转发时可根据标准来判定(如byrequests,bytraffic,bybusyness)到底选择哪个tomcat来处理,也能监测到后端tomcat server的健康状况,并通过web接口输出管理界面,在这个页面上可对后端tomcat server进行管理
mod_proxy或mod_jk都能实现对后端tomcat的负载均衡LB、调度算法的指定、健康状况检查、通过web接口进行管理、session affinity
mod_proxy(apache对mod_proxy作了巨大改进,官方建议使用,在新技术的设计上已超出mod_jk,并能提供更精细的控制,其下还有子模块:mod_proxy_http,mod_proxy_ajp,mod_proxy_balancer)
mod_jk(Jserv专用的,专门设计用来与后端tomcat通信)
mod_jk2(虽采用新架构新技术,新版本设计方面有不如人意的地方,官方已不提供维护,obsolete)
通常提到的tomcat cluster就是指httpd以反向代理方式基于mod_proxy或mod_jk将用户请求代理至后端tomcat server上的集群
若此架构(httpd+tomcat)用在电商站点,对于购物车session的保存,mod_proxy和mod_jk都能实现session affinity,但mod_proxy还能实现是php的会话还是jsp的会话,如果php的话httpd就可代理至LAMP架构中的php-fpm上;这两个模块都能实现代理至后端时支持使用两种协议,http和AJP,但nginx只能使用http协议;而且可将后端tomcat上的http连接器禁用,则用户只能通过向前端代理发起请求,以免用户不通过前端的代理直接与tomcat进行交互;官方建议:若使用tomcat集群,使用httpd作为前端,并用AJP协议的连接器并禁用http的连接器
tomcat核心配置文件(通过层次间的结构关系描述各组件间的关系):
<Server>
<Service>
<Connector/>
<Engine>
<Host>
<Context></Context>
</Host>
</Engine>
</Service>
</Server>
在同一个物理机上运行的每一个tomcat实例,就是一个服务器,就是个JVM进程,通过Bootstrap这样的外部组件(可理解为是类加载器)完成启动一个JVM,并且将tomcat自身所需要的类加载完成,tomcat自身都是在JVM进程中运行
每一个tomcat要接受外部请求要有connector,connector可理解为是web server的实现,connector类型有:http|AJP|ssl|proxy,每个connector类型都需要一个java类实现,可将connector类型理解为协议,协议要靠软件来工作起来,软件就是实现implementation,tomcat自身就是java程序,在java中,connector就是一个个编好程序的java类实现的,不同的程序员为实现相同的功能(connector)会开发出不同的java类,这些开发出的java类就是实现同一种类型的connector,如AJP类型(SUN程序开发出了一个实现类,IBM程序员也开发了实现类,这两个类都实现了AJP的connector,但内部细节(基准不同或技术不同)是不一样的,则会有不同的connector)
APR(apache portable runtime,高性能,让httpd的各组件之间进行通信且能让httpd跨平台移植),若httpd或tomcat支持APR的话,那AJP实现的connector会有至少两种以上(支持APR的和不支持APR的)
两个不同的tomcat instance,彼此间是独立的JVM进程,每一个JVM就是一个完整的组件,tomcat instance1接收了用户请求,tomcat instance2就不能接收了,坚决不允许两个实例监听在同一个socket上;若在同一个物理机上,定义了两个监听在不同socket上的tomcat,这两个tomcat可同时启动起来,这就是多实例tomcat,要使用不同的环境变量定义(CATALINA_HOME=/RUN_PATH1,CATALINA_BASE=/RUN_PATH2)
注:在同一物理机上,也可运行多个mysql,监听在不同的port,3306|3307,而且这两个mysql还可做成主从,主可以对外提供服务,从可用来做冷备
二、操作(1、JDK、tomcat的安装;2、整合httpd+tomcat;3、httpd负载均衡、sticky session、状态查看;4、tomcat的session复制):
环境:
[root@node1 ~]# uname -a
Linux node1.magedu.com2.6.32-358.el6.x86_64 #1 SMP Tue Jan 29 11:47:41 EST 2013 x86_64 x86_64 x86_64GNU/Linux
准备:
node1(192.168.41.133,源码安装httpd,生产环境中代理主机都是两块网卡,一个公网地址,一个内网,记得要开通转发)
node2(192.168.41.134,部署JDK、tomcat)
node3(192.168.41.135,部署JDK、tomcat)
jdk-8u51-linux-x64.rpm(JDK安装包有rpm、通用二进制格式(bin格式)、源码包)
apache-tomcat-7.0.67.tar.gz
apr-1.5.2.tar.gz
apr-util-1.5.4.tar.gz
httpd-2.4.18.tar.gz
tomcat-connectors-1.2.41-src.tar.gz
1、JDK、tomcat的安装:
node{2,3}-side:
[root@node2 ~]# rpm -ivh jdk-8u51-linux-x64.rpm
Preparing... ########################################### [100%]
1:jdk1.8.0_51 ########################################### [100%]
Unpacking JAR files...
rt.jar...
jsse.jar...
charsets.jar...
tools.jar...
localedata.jar...
jfxrt.jar...
plugin.jar...
javaws.jar...
deploy.jar...
注:.jar(类似tar,是java的压缩归档文件,将java类压缩归档存储)
[root@node2 ~]# ll /usr/java(/usr/java是默认安装位置,若同时有多个版本latest一般指向版本高的)
总用量 4
lrwxrwxrwx 1 root root 16 11月 22 21:22 default-> /usr/java/latest
drwxr-xr-x 9 root root 4096 11月 22 21:22jdk1.8.0_51
lrwxrwxrwx 1 root root 21 11月 22 21:22 latest-> /usr/java/jdk1.8.0_51
[root@node2 ~]# ls /usr/java/jdk1.8.0_51/bin
java(启动虚拟机)
javac(编译器)
javadoc(java文档生成工具)
jar(打包工具,打包常用的还有apt)
jdb(调试工具)
以下是java JDK免费提供的监控和故障处理工具:
jps(JVM process status tool,显示指定系统内所有的hostspot虚拟机进程的列表信息,使用hadoop时常用)
jstat(JVM statistics monitoring tool,收集并显示hostspot虚拟机各方面的运行数据)
jinfo <pid>(显示正在运行的hostpot虚拟机配置信息)
jmap(生成某hostspot虚拟机的内存转储快照,调试时用)
jconsole(可视化工具,图形化的控制台)
jvisualvm(可视化工具)
[root@node2 ~]# vim /etc/profile.d/java.sh
JAVA_HOME=/usr/java/jdk1.8.0_51
export PATH=$PATH:$JAVA_HOME/bin
[root@node1 ~]# . !$
. /etc/profile.d/java.sh
[root@node2 ~]# yum list all | grep java(redhat6自带了openjdk,若有安装将其卸载)
java-1.6.0-openjdk.x86_64 1:1.6.0.0-1.50.1.11.5.el6_3 rhel-source
java-1.6.0-openjdk-devel.x86_64 1:1.6.0.0-1.50.1.11.5.el6_3 rhel-source
java-1.6.0-openjdk-javadoc.x86_64 1:1.6.0.0-1.50.1.11.5.el6_3 rhel-source
java-1.7.0-openjdk.x86_64 1:1.7.0.9-2.3.4.1.el6_3 rhel-source
java-1.7.0-openjdk-devel.x86_64 1:1.7.0.9-2.3.4.1.el6_3 rhel-source
[root@node2 ~]# java -version
java version "1.8.0_51"
Java(TM) SE Runtime Environment (build 1.8.0_51-b16)
Java HotSpot(TM) 64-Bit Server VM (build25.51-b03, mixed mode)
[root@node2 ~]# java -help
#java [-options] class [args……](执行类)
#java [-options] -jar jarfile [args……](执行jar文件)
#java -D<name>=<value>(设置系统属性值)
#java -classpath <class_path>(指定类加载器)
[root@node2 ~]# java -XX:+PrintFlagsFinal(列出所有参数及默认值,可更改的配置参数有内存分配、算法、GC相关参数,对于布尔型使用“+”开启“-”关闭,其它的要赋值)
#java -XX:+<option>(开启某参数指定的功能)
#java -XX:-<option>(关闭某参数指定的功能)
#java -XX:<option>=<value>(给指定参数赋值)
node{2,3}-side:
[root@node2 ~]# tar xf apache-tomcat-7.0.67.tar.gz -C /usr/local/
[root@node2 ~]# cd /usr/local
[root@node2 local]# ln -sv apache-tomcat-7.0.67/ tomcat
"tomcat" ->"apache-tomcat-7.0.67/"
[root@node2 local]# cd tomcat
[root@node2 tomcat]# ls
bin conf lib LICENSE logs NOTICE RELEASE-NOTES RUNNING.txt temp webapps work
[root@node2 tomcat]# ls bin/(里面是一堆脚本和jar文件(jar,java的归档压缩包,java库,tomcat启动自己就要调用库),.bat(win的批处理脚本),catalina.sh核心脚本,其它脚本可单独执行也可用核心脚本调用它们,如#catalina.sh version)
[root@node2 tomcat]# ls conf/(此目录下的文件权限均为600;catalina.policy,tomcat的安全策略,与资源授权相关,哪些资源可否被用户访问,哪些资源可否能被装载;catalina.properties,tomcat自身属性,所使用的内存大小,所使用的字符串的缓冲池大小等;context.xml,默认上下文的配置文件;logging.properties,server.xml核心配置文件;tomcat-users.xml认证有关,提供账号密码才能登陆,这是一种基于文件或内存的认证机制,tomcat启动后会将这个文件读进内存,并在内存中完成账号认证密码检测;web.xml,非常关键,默认的应用程序描述符)
catalina.policy catalina.properties context.xml logging.properties server.xml tomcat-users.xml web.xml
[root@node2 tomcat]# ls lib/(一大堆.jar,java类库,很重要,使得tomcat具备很多功能,如jasper.jar,jsp-api.jar)
[root@node2 tomcat]# ls logs/(日志按天滚动,tomcat启动后此目录下才有内容)
[root@node2 tomcat]# ls webapps/(ROOT为默认应用程序部署的路径,这些目录下都有WEB-INF目录(该目录下放有应用程序私有资源,用户通过网页是看不到的,也不允许用户被访问))
docs examples host-manager manager ROOT
[root@node2 tomcat]# ls work/(工作目录,在这个目录下每一个engine都有自己独立的目录,engine下的host也有自己独立的目录,host下的应用程序也有自己独立的目录;work/下就是应用程序的工作车间,.java和.class都在其下对应的目录中)
注:部署(将一个应用程序放在host或context下,并将这个应用程序依赖的库装载完成的过程),将一个web应用程序所依赖的库装载进JVM,每一个应用程序都应该有自己的部署描述符,如果没有继承默认的((web.xml);一般仅关心.xml的配置文件,除非要改tomcat server的运行属性
[root@node2 tomcat]# vim conf/server.xml
<Server port="8005"shutdown="SHUTDOWN">(启动一个server instance,即一个JVM,tomcat有管理接口,可用telnet访问并进行管理;相关属性有:className(实现server的类名),ort(接收shutdown指令的端口,默认8005仅允许本机访问),shutdown(发往server实现关闭tomcat实例的命令字符串,默认为SHUTDOWN))
<Service name="Catalina">(service主要用于关联一个引擎和与此引擎相关的连接器,每个连接器通过一个特定的端口和协议接收入站请求并转至关联的引擎进行处理,因此service必须包含一个engine,一个或多个connector,相关属性:className(实现service的类名),name(此服务的名称,默认为Catalina))
<Connector port="8080" protocol="HTTP/1.1"
connectionTimeout="20000"
redirectPort="8443"/>(此例是HTTP连接器,连接器类型有四种:HTTP连接器、SSL连接器、AJP1.3连接器、proxy连接器;在定义HTTP连接器时必须定义的属性是port,定义AJP连接器必须定义的属性是protocol,因为默认协议连接器是HTTP类型的;相关属性有:address(指定监听的地址,默认为所有地址0.0.0.0),maxThreads(支持的最大并发连接数,默认为200),port(监听的端口,默认为0),protocol(连接器使用的协议,默认是HTTP/1.1,定义AJP协议时为AJP/1.3),redirectPort(如果连接器是HTTP协议类型的,当接收客户端发来的HTTPS请求时,则转发至此属性定义的端口),connectionTimeout(等待client请求的超时时间,单位毫秒,默认60000即1min),enableLookups(是否通过request.getRemoteHost()进行DNS查询以获取客户端的主机名,默认为true),acceptCount(等待队列的最大长度,通常在tomcat所有处理线程均处于繁忙状态时,新发来的请求将被放置于等待队列中))
<Engine name="Catalina" defaultHost="localhost">(servlet引擎,engine容器中可包含Realm,Host,Listener,Valve等子容器,相关属性有:name(engine组件的名称),defaultHost(tomcat支持FQDN的虚拟主机,若此引擎的连接器收到一个非明确定义的虚拟主机的请求时这时就要通过默认的虚拟主机进行处理))
<Host name="www.magedu.com" appBase="webapps"
unpackWARs="true" autoDeploy="true">(用于接收请求并进行处理的主机或虚拟主机,相关属性有:appBase(此host的存放归档的web应用程序的目录或归档后的WAR文件的目录路径,此例使用的是相对路径$CATALINA_HOME),autoDeploy(在tomcat处于运行状态时放置于appBase目录中的应用程序文件是否自动进行deploy,默认true),unpackWars(在启用此web应用程序时是否对WAR格式的归档文件先进行展开,默认true)
<Context path="" docBase="/usr/local/tomcat/webapps" reloadable="true"/>(定义类似路径别名,一个context用于标识tomcat实例中的一个web应用程序,可使用单独的xml文件来定义$CATALINA_HOME/conf/ENGINE_NAME/HOST_NAME,相关属性有:docBase(web应用程序存放位置,也可用相对路径从appBase定义的作为起始路径),path(相对于web server根路径的URI,如果为空表示web应用程序的根路径,若context定义在单独的xml文件中,此属性不需要定义;reLoadable(是否允许重新加载此context相关的web应用程序的类,默认true))
</Host>
</Engine>
</Service>
</Server>
[root@node2 tomcat]# vim /etc/profile.d/tomcat.sh
export CATALINA_HOME=/usr/local/tomcat
export PATH=$PATH:$CATALINA_HOME/bin
[root@node2 tomcat]# . !$
. /etc/profile.d/tomcat.sh
[root@node2 tomcat]# catalina.sh --help(start|stop|configtest)
[root@node2 tomcat]# catalina.sh version(使用核心脚本调用version.sh)
Using CATALINA_BASE: /usr/local/tomcat
Using CATALINA_HOME: /usr/local/tomcat
Using CATALINA_TMPDIR:/usr/local/tomcat/temp
Using JRE_HOME: /usr
Using CLASSPATH: /usr/local/tomcat/bin/bootstrap.jar:/usr/local/tomcat/bin/tomcat-juli.jar
Server version: Apache Tomcat/7.0.67
Server built: Dec 7 2015 13:07:11 UTC
Server number: 7.0.67.0
OS Name: Linux
OS Version: 2.6.32-358.el6.x86_64
Architecture: amd64
JVM Version: 1.8.0_51-b16
JVM Vendor: Oracle Corporation
[root@node2 tomcat]# version.sh(可单独运行bin/下的脚本)
[root@node2 tomcat]# catalina.sh configtest(语法测试要在服务停止时进行)
……
[root@node2 tomcat]# catalina.sh start
Using CATALINA_BASE: /usr/local/tomcat
Using CATALINA_HOME: /usr/local/tomcat
Using CATALINA_TMPDIR: /usr/local/tomcat/temp
Using JRE_HOME: /usr
Using CLASSPATH: /usr/local/tomcat/bin/bootstrap.jar:/usr/local/tomcat/bin/tomcat-juli.jar
Tomcat started.
[root@node2 tomcat]# netstat -tnlp | grep java
tcp 0 0 :::8080 :::* LISTEN 45083/java
tcp 0 0::ffff:127.0.0.1:8005 :::* LISTEN 45083/java
tcp 0 0 :::8009 :::* LISTEN 45083/java
[root@node2 tomcat]# jps
44788 Bootstrap
44806 Jps
[root@node2 tomcat]# ls logs(catalina.out是当前正在使用的日志,catalina-2016-01-15.log则是catalina.out滚动过的)
catalina.2016-01-15.log host-manager.2016-01-15.log localhost_access_log.2016-01-15.txt
catalina.out localhost.2016-01-15.log manager.2016-01-15.log
[root@node2 tomcat]# ls work/Catalina/localhost/_(_表示ROOT,只有用户访问过http://192.168.41.134:8080(tomcat提供的默认页面)后才开始编译,该目录下才有内容,第一次访问会很慢,之后再访问由于已编译过就快多了)
[root@node2 tomcat]# ls work/Catalina/localhost/_/org/apache/jsp/
index_jsp.class index_jsp.java
[root@node2 tomcat]# vim /etc/init.d/tomcat(tomcat启动脚本)
------------script start--------------
#!/bin/sh
# Tomcat init script for Linux.
#
# chkconfig: 2345 96 14
# description: The Apache Tomcatservlet/JSP container.
export JAVA_HOME=/usr/java/latest
export CATALINA_HOME=/usr/local/tomcat
exec $CATALINA_HOME/bin/catalina.sh $*
--------------script end---------------
[root@node2 tomcat]# chmod +x !$
chmod +x /etc/init.d/tomcat
[root@node2 tomcat]# chkconfig --add tomcat
[root@node2 tomcat]# chkconfig tomcat on
[root@node2 tomcat]# chkconfig --list tomcat
tomcat 0:关闭 1:关闭 2:启用 3:启用 4:启用 5:启用 6:关闭
[root@node2 tomcat]# service tomcat stop
……
[root@node2 tomcat]# service tomcat start
……
[root@node2 ~]# cd $CATALINA_HOME/bin
[root@node2 bin]# tar xf tomcat-native.tar.gz
[root@node2 bin]# cd tomcat-native-1.1.33-src/jni/native/
[root@node2 native]# yum -y install apr-devel apr-util-devel
[root@node2 native]# ./configure --prefix=/usr/local/tomcat --with-apr=/usr --with-java-home=/usr/java/latest
[root@node2 native]# make && make install
2、整合httpd+tomcat(在node1、node2上操作)
node2-side:
[root@node2 tomcat]# vim conf/server.xml(修改engine行,添加一Host)
<Engine name="Catalina" defaultHost="www.magedu.com" jvmRoute="TomcatA">
<Host name="www.magedu.com" appBase="/web"
unpackWARs="true" autoDeploy="true">
<Context path="" docBase="webapps" reloadable="true"/>
</Host>
[root@node2 tomcat]# mkdir /web/webapps -pv
mkdir: 已创建目录"/web"
mkdir: 已创建目录"/web/webapps"
[root@node2 tomcat]# cd /web/webapps
[root@node2 webapps]# vim index.jsp(node3上此脚本内容改为TomcatB)
-----------------test pagestart-------------------
<%@ page language="java" %>
<html>
<head><title>TomcatA</title></head>
<body>
<h1><font color="red">TomcatA</font></h1>
<table border="1">
<tr>
<td>Session ID</td>
<% session.setAttribute("abc","abc"); %>
<td><%= session.getId() %></td>
</tr>
<tr>
<td>Created on</td>
<td><%= session.getCreationTime() %></td>
</tr>
</table>
</body>
</html>
-----------------test page end------------------------
[root@node2 tomcat]# service tomcat stop
[root@node2 tomcat]# service tomcat start
node1-side:
[root@node1 ~]# tar xf apr-1.5.2.tar.gz
[root@node1 apr-1.5.2]# ./configure --prefix=/usr/local/apr
[root@node1 apr-1.5.2]# make && make install
[root@node1 apr-1.5.2]# cd
[root@node1 ~]# tar xf apr-util-1.5.4.tar.gz
[root@node1 ~]# cd apr-util-1.5.4
[root@node1 apr-util-1.5.4]# ./configure --prefix=/usr/local/apr-util --with-apr=/usr/local/apr
[root@node1 apr-util-1.5.4]# make && make install
[root@node1 apr-util-1.5.4]# cd
[root@node1 ~]# tar xf httpd-2.4.18.tar.gz
[root@node1 ~]# cd httpd-2.4.18
[root@node1 httpd-2.4.18]# ./configure --prefix=/usr/local/apache --sysconfdir=/etc/httpd --enable-so --enable-ssl --enable-cgi --enable-rewrite --with-zlib --with-pcre --with-apr=/usr/local/apr --with-apr-util=/usr/local/apr-util --enable-mpms-shared=all --with-mpm=event --enable-proxy --enable-proxy-http --enable-proxy-ajp --enable-proxy-balancer --enable-lbmethod-heartbeat --enable-heartbeat --enable-slotmem-shm --enable-slotmem-plain --enable-watchdog
[root@node1 httpd-2.4.18]# make && make install
[root@node1 ~]# vim /etc/init.d/httpd(脚本内容见Linux运维第三阶段(八)理解LAMP
)
[root@node1 ~]# chmod +x !$
chmod +x /etc/init.d/httpd
[root@node1 ~]# chkconfig --add httpd
[root@node1 ~]# chkconfig httpd on
[root@node1 ~]# service httpd configtest
Syntax OK
[root@node1 ~]# service httpd start
正在启动 httpd: [确定]
[root@node1 ~]# /usr/local/apache/bin/httpd -D DUMP_MODULES(重点关注以下模块是否有,若使用mod_proxy模块作代理,确保有proxy_module和proxy_http_module;若使用mod_jk模块作代理,确保有proxy_module和proxy_ajp_module;无论使用这两个模块中的任何一个,若要用到LB功能,要确保有proxy_balancer_moudle)
proxy_module (shared)
proxy_connect_module (shared)
proxy_ftp_module (shared)
proxy_http_module (shared)
proxy_fcgi_module (shared)
proxy_scgi_module (shared)
proxy_wstunnel_module (shared)
proxy_ajp_module (shared)
proxy_balancer_module (shared)
proxy_express_module (shared)
lbmethod_byrequests_module (shared)
lbmethod_bytraffic_module (shared)
lbmethod_bybusyness_module (shared)
lbmethod_heartbeat_module (shared)
[root@node1 ~]# cd /etc/httpd
[root@node1 httpd]# ls
extra httpd.conf magic mime.types original
[root@node1 httpd]# cp httpd.conf httpd.conf.bak
(1)使用mod_proxy
[root@node1 httpd]# vim httpd.conf
PidFile "/var/run/httpd.pid"(添加此行)
LoadModule slotmem_shm_module modules/mod_slotmem_shm.so(启用shm,共享内存模块,负载均衡模块会依赖这个模块)
LoadModule slotmem_plain_module modules/mod_slotmem_plain.so
#DocumentRoot"/usr/local/apache/htdocs"(将中心主机注释掉,使用虚拟主机中定义的)
Include /etc/httpd/extra/httpd-proxy.conf(添加此行)
[root@node1 httpd]# cd extra
[root@node1 extra]# vim httpd-proxy.conf
<VirtualHost *:80>
ProxyVia On(添加响应首部,使client知道是谁代理的)
ProxyRequests Off(很关键,此项是httpd的正向代理必须关掉,正反向代理不允许同时使用)
ProxyPass / http://192.168.41.134:8080/(反向代理;格式:ProxyPasspath URL [KEY=VALUE KEY=VALUE……];path表示虚拟路径,URL是后端服务器地址,注意若path以/结尾,那URL也要以/结尾,前后要保持一致,如ProxyPass /forum http://192.168.41.134:8080/forum,否则会怪样百出;常用的KEY有:min(连接池的最小容量),max(连接池的最大容量),loadfactor(LB集群中定义后端server的权重,范围1-100),retry(httpd在得到后端server错误响应时等待多长时间后再重试);也可使用ajp协议,如ProxyPass / ajp://192.168.41.134:8080/)
ProxyPassReverse / http://192.168.41.134:8080/(用于重定向后仍然使用代理向后端请求,在反向代理环境中必须使用此指令避免重定向报文绕过前端代理)
<Proxy *>(负载均衡时使用<Proxy balancer://CLUSTER_NAME>可使用的指令有:lbmethod(调度方法,默认使用byrequests即rr,bytraffic根据流量大小,bybusyness根据后端server的负载情况),maxattempts(放弃请求之前实现故障转移的次数,默认1,最大不应大于后端总node数),nofailover(设为On时表示后端server故障,用户的session将破坏),stickysession(可用值有JSESSIONID或PHPSESSIONID),这些指令可用于balancer://和ProxyPass,也可使用ProxySet指令设置)
Require all granted
</Proxy>
<Location />
Require all granted
</Location>
</VirtualHost>
注:ProxyPreserveHost On|Off,在反向代理中后端若有多个虚拟主机,每个虚拟主机都希望被独立访问的话,要启用此项
[root@node1 httpd]# service httpd restart
停止 httpd: [确定]
正在启动 httpd: [确定]
(2)使用mod_jk
[root@node1 ~]# tar xf tomcat-connectors-1.2.41-src.tar.gz
[root@node1 ~]# cd tomcat-connectors-1.2.41-src/native/
[root@node1 native]# ./configure --with-apxs=/usr/local/apache/bin/apxs(所有httpd的模块都要使用此命令进行编译)
[root@node1 native]# make && make install
[root@node1 native]# ls /usr/local/apache/modules/ | grep mod_jk
mod_jk.so
[root@node1 native]# cd
[root@node1 ~]# vim /etc/httpd/httpd.conf
#Include /etc/httpd/extra/httpd-proxy.conf(此行注释)
Include /etc/httpd/extra/httpd-jk.conf(添加此行)
[root@node1 ~]# vim /etc/httpd/extra/httpd-jk.conf
LoadModule jk_module modules/mod_jk.so(加载此模块)
JkworkersFile /etc/httpd/extra/workers.properties(jk使用子进程worker与后端tomcat通信,这些worker进程要启动几个、如何与后端连接、要不要LB等这些信息都通过这个文件定义)
JkLogFile logs/mod_jk.log
JkLogLevel debug(正常工作后就不要使用debug了)
JkMount /* TomcatA(JkMount这项很关键,格式:JkMount<URL to match> <tomcat workername>,把哪个uri路径送到哪个后端tomcat上,此项中的内容TomcatA必须要在workers.properties中定义才行)
JkMount /status/ stat1(用于查看状态,stat1是jk模块自带的不用考虑后端)
[root@node1 ~]# vim /etc/httpd/extra/workers.properties(此文件包括两类指令,一类是worker.list=<a comma seperated list of worker name>表示各worker名称列表,多个用逗号分隔;一类是worker.<worker name>.<property>=<property value>表示某个worker的属性配置信息,workername是后端tomcat配置文件server.xml中engine上定义的jvmRoute值)
worker.list=TomcatA,stat1
worker.TomcatA.host=192.168.41.134(worker实例所在主机)
worker.TomcatA.port=8009(ajp1.3连接器的端口)
worker.TomcatA.type=ajp13(根据工作机制的不同,type有:ajp13(表示当前一个运行着的tomcat实例),lb(loadbalancing,专用于LB场景的worker,此worker并不真正负责处理用户请求,而是将请求调度给ajp13类型的worker),status(显示分布式环境中各worker的工作状态,不处理任何请求,也不关联到实际工作的tomcat实例)
worker.TomcatA.lbfactor=1
worker.stat1.type=status
注:property除以上列出的host,port,type,lbfactor外,还有:connection_pool_minsize(最少要保存在连接池中的连接个数),connection_pool_timeout,mount(已用指令JkMount代替),retries(错误发生时的重试次数),socket_timeout(等待worker响应的时长,默认0无限等待),socket_keepalive(是否启用keepalive功能,1启用0禁用);在负载均衡中还有:balance_workers(LB模式下worker的名称列表),method(值有R、T、B,R(byrequests根据请求的个数进行调度,类似rr),T(bytraffic根据流量大小调度),B(bybysyness根据LB情况调度),T和B是根据后端server的状况作出决策进行调度可理解为是动态方法,R可理解为是静态方法),sticky_session(会话绑定,默认1启用,在一段时间内始终定向至同一后端tomcat,若后端各worker之间支持session复制则可将此项改为0)
[root@node1 ~]# service httpd restart
停止 httpd: [确定]
正在启动 httpd: [确定]
3、tomcat集群——httpd负载均衡LB、sticky session、查看状态功能(在以上操作的基础上,确保node3与node2配置相同,唯一区别是node3的测试脚本/web/webapps/index.jsp的内容为改为TomcatB及核心配置文件中jvmRoute=”TomcatB”):
(1)使用mod_proxy实现:
node1-side:
[root@node1 ~]# cd /etc/httpd/extra
[root@node1 extra]# vim httpd-proxy.conf(配置负载均衡)
<VirtualHost*:80>
ServerName localhost
ProxyVia On
ProxyRequests Off
<Proxy balancer://lbcluster>
BalancerMember ajp://192.168.41.134:8009 loadfactor=1
BalancerMember ajp://192.168.41.135:8009 loadfactor=1
ProxySet lbmethod=byrequests
</Proxy>
ProxyPass / balancer://lbcluster/
ProxyPassReverse / balancer://lbcluster/
<Location />
Require all granted
</Location>
</VirtualHost>
[root@node1 extra]# vim ../httpd.conf
Include/etc/httpd/extra/httpd-proxy.conf
#Include /etc/httpd/extra/httpd-jk.conf
[root@node1 extra]# service httpd restart
停止 httpd: [确定]
正在启动 httpd: [确定]
注意:每次刷新sessionid都不一样
[root@node1 extra]# vim httpd-proxy.conf(实现会话绑定、状态监控)
<VirtualHost *:80>
ServerName localhost
ProxyVia On
ProxyRequests Off
<Proxy balancer://lbcluster>
BalancerMember ajp://192.168.41.134:8009 loadfactor=1 route=TomcatA
BalancerMember ajp://192.168.41.135:8009 loadfactor=1 route=TomcatB
ProxySet lbmethod=byrequests
</Proxy>
ProxyPass / balancer://lbcluster/ stickysession=JSESSIONID
ProxyPassReverse / balancer://lbcluster/ stickysession=JSESSIONID
<Location /balancer-manager>
SetHandler balancer-manager
ProxyPass !
Require all granted
</Location>
<Location />
Require all granted
</Location>
</VirtualHost>
[root@node1 extra]# service httpd restart
停止 httpd: [确定]
正在启动 httpd: [确定]
再次刷新始终在TomcatA上
(2)使用mod_jk实现LB、stickysession、状态监控:
[root@node1 extra]# vim ../httpd.conf
#Include /etc/httpd/extra/httpd-proxy.conf
Include /etc/httpd/extra/httpd-jk.conf
[root@node1 extra]# vim httpd-jk.conf
LoadModule jk_module modules/mod_jk.so
JkworkersFile /etc/httpd/extra/workers.properties
JkLogFile logs/mod_jk.log
JkLogLevel debug
JkMount /* lbcluster
JkMount /status/ stat
[root@node1 extra]# vim workers.properties
worker.list=lbcluster,stat
worker.lbcluster.balanced_workers=TomcatA,TomcatB
worker.lbcluster.sticky_session=1
worker.lbcluster.type=lb
worker.TomcatA.host=192.168.41.134
worker.TomcatA.port=8009
worker.TomcatA.type=ajp13
worker.TomcatA.lbfactor=1
worker.TomcatB.host=192.168.41.135
worker.TomcatB.port=8009
worker.TomcatB.type=ajp13
worker.TomcatB.lbfactor=1
worker.stat.type=status
[root@node1 extra]# service httpd restart
停止 httpd: [确定]
正在启动 httpd: [确定]
4、使用manager组件中的DeltaManager实现各node间的会话复制:
会话管理器是一种规范,通过类将规范实现,不同公司开发的类是不一样的,实现的功能也会不一样,以下几个管理器是tomcat自身提供的
StandardManager(标准会话管理器,每隔一段时间会将内存中的会话信息同步至$CATALINA_HOME/WORK/Catalina/HOSTNAME/WEBAPPNAME/SESSION.ser文件中;好处tomcat服务重启后会话还在;坏处某node的tomcat崩溃会话将不存在(在该node的本地文件中,不能被其它node访问))
PersistentManager(持久会话管理器,将会话直接保存在FS(FileStore)上或DB(JDBCStore)中,服务重启会加载会话信息)
DeltaManager(将多个node通过网络连接建立HA集群,各node间可互传心跳信息,任何用户通过前端创建的会话信息,在后端tomcat通过心跳信息识别active node,任何会话在后端每个tomcat server上都有;好处后端任意一台tomcat server挂掉其它node都有其会话信息,坏处node若过多的话需传送大量会话信息影响带宽,若所有node都挂掉那所有会话也没有了(解决方法:单独用一台共享存储NFS server保存所有node的会话信息)
BackManager(某node在其它node中选择一个作为备用,仅将本地会话信息传给备用node,一旦这个node故障,将请求转至备用node即可,若后端有4个tomcat node,node1选node2作为备用,node2选node3作为备用,node3选node4作为备用,node4上将有所有node的会话信息,这不理想,将node{1,2},node{3,4}两两建集群,两两互备(类似HA集群中的failover domain)
总结:session同步可通过三种方式实现:sticky模式、session复制、terracotta软件实现
sticky模式,利用LB把所有同一session的请求发送到相同的tomcatserver node,这样不同用户的请求被平均分配到集群中各个tomcat server上实现LB的能力,缺陷:没有灾难恢复能力,一旦某个node故障,这个node的所有session信息将全部丢失;
session复制,所有session信息在所有的tomcatserver上保持一致,当某个tomcat server node修改一个session数据时,会将所有session内容全部序列化serialize再广播给所有tomcat server,不管其它node需不需要,缺陷:导致大量网络通信,当后端tomcat server超过4个时,整个集群的吞吐量将受限
terracotta,开源软件实现,基本原理是把集群间数据共享的机制用在session同步上,当某个node的session信息发生变化时,这个node的tomcat只把变化的部分发给terracotta服务器,由terracotta转发给真正需要这个数据的node,这样对网络的压力就变小,各个节点也不用浪费CPU和内存进行大师的serialize操作,这样可达到LB和灾难恢复的效果,采用terracotta搭建的tomcat集群,在node数达到8个的时候,吞吐量还呈线性增长
实现:(1)改配置文件,加入以下红色字体部分;(2)在相应的web应用程序私有目录中更改web.xml文件(没有此文件复制默认的),让其实现集群功能
(1)node{2,3}-side:
[root@node2 tomcat]# vim conf/server.xml(注意node2,node3的address要更改)
<Engine name="Catalina" defaultHost="www.magedu.com"jvmRoute="TomcatA">
<Cluster className="org.apache.catalina.ha.tcp.SimpleTcpCluster" channelSendOptions="8">
<Manager className="org.apache.catalina.ha.session.DeltaManager"
expireSessionsOnShutdown="false"
notifyListenersOnReplication="true"/>
<Channel className="org.apache.catalina.tribes.group.GroupChannel">
<Membership className="org.apache.catalina.tribes.membership.McastService"
address="228.0.0.4"port="45564" frequency="500" dropTime="3000"/>
<Receiver className="org.apache.catalina.tribes.transport.nio.NioReceiver"
address="192.168.41.134" port="4000" autoBind="100" selectorTimeout="5000" maxThreads="6"/>
<Sender className="org.apache.catalina.tribes.transport.ReplicationTransmitter">
<Transport className="org.apache.catalina.tribes.transport.nio.PooledParallelSender"/>
</Sender>
<Interceptor className="org.apache.catalina.tribes.group.interceptors.TcpFailureDetector"/>
<Interceptor className="org.apache.catalina.tribes.group.interceptors.MessageDispatch15Interceptor"/>
</Channel>
<Valve className="org.apache.catalina.ha.tcp.ReplicationValve"filter="/"/>
<Valve className="org.apache.catalina.ha.session.JvmRouteBinderValve"/>
<Deployer className="org.apache.catalina.ha.deploy.FarmWarDeployer" tempDir="/tmp/war-temp/"
deployDir="/tmp/war-deploy/" watchDir="/tmp/war-listen/" watchEnabled="false"/>
<ClusterListener className="org.apache.catalina.ha.session.JvmRouteSessionIDBinderListener"/>
<ClusterListener className="org.apache.catalina.ha.session.ClusterSessionListener"/>
</Cluster>
(2)node{2,3}-side:
[root@node2 tomcat]# cd /web/webapps/
[root@node2 webapps]# mkdir WEB-INF
[root@node2 webapps]# cp /usr/local/tomcat/conf/web.xml WEB-INF/
[root@node2 webapps]# vim WEB-INF/web.xml(两node都要有此操作)
<web-appxmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
version="3.0">
<distributable/>
[root@node2 tomcat]# catalina.sh stop(两node均重启)
[root@node2 tomcat]# catalina.sh start
[root@node2 tomcat]# netstat -tnulp | grep java
tcp 0 0 :::8080 :::* LISTEN 7994/java
tcp 0 0::ffff:192.168.41.134:4000 :::* LISTEN 7994/java
tcp 0 0::ffff:127.0.0.1:8005 :::* LISTEN 7994/java
tcp 0 0 :::8009 :::* LISTEN 7994/java
udp 0 0 :::45564 :::* 7994/java
[root@node2 tomcat]# tail -20 logs/catalina.2016-01-16.log
一月 16, 2016 10:50:47 上午 org.apache.catalina.ha.session.DeltaManager waitForSendAllSessions
信息: Manager [www.magedu.com#]; session state send at 1/16/16 10:50 AMreceived in 124 ms.
……
信息: Manager [www.magedu.com#/webapps], requesting session state fromorg.apache.catalina.tribes.membership.MemberImpl[tcp://{192, 168, 41,135}:4000,{192, 168, 41, 135},4000, alive=12597, securePort=-1, UDP Port=-1,id={-31 70 -122 -70 94 -105 76 91 -72 -9 33 106 121 -24 14 -112 }, payload={},command={}, domain={}, ]. This operation will timeout if no session state hasbeen received within 60 seconds.
一月 16, 2016 10:50:47 上午 org.apache.catalina.ha.session.DeltaManager waitForSendAllSessions
信息: Manager [www.magedu.com#/webapps]; session state send at 1/16/1610:50 AM received in 102 ms.
一月 16, 2016 10:50:47 上午 org.apache.catalina.ha.session.JvmRouteBinderValve startInternal
信息: JvmRouteBinderValve started
一月 16, 2016 10:50:47 上午 org.apache.coyote.AbstractProtocol start
信息: Starting ProtocolHandler ["http-bio-8080"]
一月 16, 2016 10:50:47 上午 org.apache.coyote.AbstractProtocol start
信息: Starting ProtocolHandler ["ajp-bio-8009"]
一月 16, 2016 10:50:47 上午 org.apache.catalina.startup.Catalina start
信息: Server startup in 3766 ms
node1-side:
[root@node1 httpd]# vim extra/workers.properties
worker.lbcluster.sticky_session=0(关闭此功能,再测试)
[root@node1 httpd]# service httpd restart
停止 httpd: [确定]
正在启动 httpd: [确定]
5、利用nginx实现前端代理:
[root@node1 httpd]# service httpd stop
停止 httpd: [确定]
[root@node1 httpd]# cd
[root@node1 ~]# vim /etc/nginx/nginx.conf(nginx的安装参考《理解LNMP》)
upstream tomcatsrvs {
server 192.168.41.134:8080 weight=1 max_fails=2 fail_timeout=2;
server 192.168.41.135:8080 weight=1 max_fails=2 fail_timeout=2;
}
server {
listen 80;
server_name localhost;
location / {
proxy_pass http://tomcatsrvs;
}
……
}
[root@node1 ~]# service nginx start
正在启动 nginx: [确定]
注:配置文件server.xml注解
<!--
Cluster(集群,族) 节点,如果你要配置tomcat集群,则需要使用此节点.
className 表示tomcat集群时,之间相互传递信息使用那个类来实现信息之间的传递.
channelSendOptions可以设置为2、4、8、10,每个数字代表一种方式
2= Channel.SEND_OPTIONS_USE_ACK(确认发送)
4= Channel.SEND_OPTIONS_SYNCHRONIZED_ACK(同步发送)
8= Channel.SEND_OPTIONS_ASYNCHRONOUS(异步发送)
在异步模式下,可以通过加上确认发送(Acknowledge)来提高可靠性,此时channelSendOptions设为10
-->
<Cluster className="org.apache.catalina.ha.tcp.SimpleTcpCluster"channelSendOptions="8">
<!--
Manager决定如何管理集群的Session信息。Tomcat提供了两种Manager:BackupManager和DeltaManager
BackupManager-集群下的所有Session,将放到一个备份节点。集群下的所有节点都可以访问此备份节点
DeltaManager-集群下某一节点生成、改动的Session,将复制到其他节点。
DeltaManager是Tomcat默认的集群Manager,能满足一般的开发需求
使用DeltaManager,每个节点部署的应用要一样;使用BackupManager,每个节点部署的应用可以不一样.
className-指定实现org.apache.catalina.ha.ClusterManager接口的类,信息之间的管理.
expireSessionsOnShutdown-设置为true时,一个节点关闭,将导致集群下的所有Session失效
notifyListenersOnReplication-集群下节点间的Session复制、删除操作,是否通知sessionlisteners
maxInactiveInterval-集群下Session的有效时间(单位:s)。
maxInactiveInterval内未活动的Session,将被Tomcat回收。默认值为1800(30min)
-->
<Manager className="org.apache.catalina.ha.session.DeltaManager"
expireSessionsOnShutdown="false"
notifyListenersOnReplication="true"/>
<!--
Channel是Tomcat节点之间进行通讯的工具。
Channel包括5个组件:Membership、Receiver、Sender、Transport、Interceptor
-->
<Channel className="org.apache.catalina.tribes.group.GroupChannel">
<!--
Membership维护集群的可用节点列表。它可以检查到新增的节点,也可以检查到没有心跳的节点
className-指定Membership使用的类
address-组播地址
port-组播端口
frequency-发送心跳(向组播地址发送UDP数据包)的时间间隔(单位:ms)。默认值为500
dropTime-Membership在dropTime(单位:ms)内未收到某一节点的心跳,则将该节点从可用节点列表删除。默认值为3000
注: 组播(Multicast):一个发送者和多个接收者之间实现一对多的网络连接。
一个发送者同时给多个接收者传输相同的数据,只需复制一份相同的数据包。
它提高了数据传送效率,减少了骨干网络出现拥塞的可能性
相同组播地址、端口的Tomcat节点,可以组成集群下的子集群
-->
<Membership className="org.apache.catalina.tribes.membership.McastService"
address="228.0.0.4"
port="45564"
frequency="500"
dropTime="3000"/>
<!--
Receiver : 接收器,负责接收消息
接收器分为两种:BioReceiver(阻塞式)、NioReceiver(非阻塞式)
className-指定Receiver使用的类
address-接收消息的地址
port-接收消息的端口
autoBind-端口的变化区间
如果port为4000,autoBind为100,接收器将在4000-4099间取一个端口,进行监听
selectorTimeout-NioReceiver内轮询的超时时间
maxThreads-线程池的最大线程数
-->
<Receiver className="org.apache.catalina.tribes.transport.nio.NioReceiver"
address="auto"
port="4000"
autoBind="100"
selectorTimeout="5000"
maxThreads="6"/>
<!--
Sender : 发送器,负责发送消息
Sender内嵌了Transport组件,Transport真正负责发送消息
-->
<Sender className="org.apache.catalina.tribes.transport.ReplicationTransmitter">
<!--
Transport分为两种:bio.PooledMultiSender(阻塞式)、nio.PooledParallelSender(非阻塞式)
-->
<Transport className="org.apache.catalina.tribes.transport.nio.PooledParallelSender"/>
</Sender>
<!--
Interceptor : Cluster的拦截器
TcpFailureDetector-网络、系统比较繁忙时,Membership可能无法及时更新可用节点列表,
此时TcpFailureDetector可以拦截到某个节点关闭的信息,
并尝试通过TCP连接到此节点,以确保此节点真正关闭,从而更新集群可以用节点列表
-->
<Interceptor className="org.apache.catalina.tribes.group.interceptors.TcpFailureDetector"/>
<!--
MessageDispatch15Interceptor-查看Cluster组件发送消息的方式是否设置为
Channel.SEND_OPTIONS_ASYNCHRONOUS(Cluster标签下的channelSendOptions为8时)。
设置为Channel.SEND_OPTIONS_ASYNCHRONOUS时,
MessageDispatch15Interceptor先将等待发送的消息进行排队,然后将排好队的消息转给Sender
-->
<Interceptor className="org.apache.catalina.tribes.group.interceptors.MessageDispatch15Interceptor"/>
</Channel>
<!--
Valve : 可以理解为Tomcat的拦截器
ReplicationValve-在处理请求前后打日志;过滤不涉及Session变化的请求
vmRouteBinderValve-Apache的mod_jk发生错误时,保证同一客户端的请求发送到集群的同一个节点
-->
<Valve className="org.apache.catalina.ha.tcp.ReplicationValve"filter=""/>
<Valve className="org.apache.catalina.ha.session.JvmRouteBinderValve"/>
<!--
Deployer : 同步集群下所有节点的一致性。Deployer没试验成功过。。。
-->
<Deployer className="org.apache.catalina.ha.deploy.FarmWarDeployer"
tempDir="/tmp/war-temp/"
deployDir="/tmp/war-deploy/"
watchDir="/tmp/war-listen/"
watchEnabled="false"/>
<!--
ClusterListener : 监听器,监听Cluster组件接收的消息
使用DeltaManager时,Cluster接收的信息通过ClusterSessionListener传递给DeltaManager
-->
<ClusterListener className="org.apache.catalina.ha.session.ClusterSessionListener"/>
</Cluster>
以上是学习《马哥运维课程》做的笔记。
本文出自 “Linux运维重难点学习笔记” 博客,请务必保留此出处http://jowin.blog.51cto.com/10090021/1739400
原文地址:http://jowin.blog.51cto.com/10090021/1739400