存档

‘Tomcat’ 分类的存档

[转]:Tomcat学习之Connector

2013年7月11日 没有评论

转载自:http://blog.csdn.net/aesop_wubo/article/details/7617416

如下图所示,Tomcat服务器主要有两大核心模块组成:连接器和容器,本节只分析连接器的实现。

连接器主要是接收用户的请求,然后封装请求传递给容器处理,tomcat中默认的连接器是Coyote.首先来看连接器的类图:

protocol

我们发现这个类里面有很多与protocol有关的属性和方法,tomcat中支持两种协议的连接器:HTTP/1.1与AJP/1.3,查看tomcat的配置文件server.xml可以看到如下配置:

  1. <Connector port=”8080″ protocol=”HTTP/1.1″
  2.                connectionTimeout=”20000″
  3.                redirectPort=”8443″ URIEncoding=”utf-8″/>
  4. <Connector port=”8009″ protocol=”AJP/1.3″ redirectPort=”8443″ />

HTTP/1.1协议负责建立HTTP连接,web应用通过浏览器访问tomcat服务器用的就是这个连接器,默认监听的是8080端口;

AJP/1.3协议负责和其他HTTP服务器建立连接,监听的是8009端口,比如tomcat和apache或者iis集成时需要用到这个连接器。

协议上有三种不同的实现方式:JIO、APR、NIO。

JIO(java.io):用java.io纯JAVA编写的TCP模块,这是tomcat默认连接器实现方法;

APR(Apache Portable Runtime):有C语言和JAVA两种语言实现,连接Apache httpd Web服务器的类库是在C中实现的,同时用APR进行网络通信;

NIO(java.nio):这是用纯Java编写的连接器(Conector)的一种可选方法。该实现用java.nio核心Java网络类以提供非阻塞的TCP包特性。

ProtocolHandler接口是对这些协议的抽象,其类层次结构图如下呼所示:

前面提到tomcat默认采用的是Http11Protocol,要么要怎么更换默认的protocolHandler呢,先看看setProtocol方法的源码:

  1. public void setProtocol(String protocol) {
  2.     if (AprLifecycleListener.isAprAvailable()) {
  3.         if (“HTTP/1.1”.equals(protocol)) {
  4.             setProtocolHandlerClassName
  5.                 (“org.apache.coyote.http11.Http11AprProtocol”);
  6.         } else if (“AJP/1.3”.equals(protocol)) {
  7.             setProtocolHandlerClassName
  8.                 (“org.apache.coyote.ajp.AjpAprProtocol”);
  9.         } else if (protocol != null) {
  10.             setProtocolHandlerClassName(protocol);
  11.         } else {
  12.             setProtocolHandlerClassName
  13.                 (“org.apache.coyote.http11.Http11AprProtocol”);
  14.         }
  15.     } else {
  16.         if (“HTTP/1.1”.equals(protocol)) {
  17.             setProtocolHandlerClassName
  18.                 (“org.apache.coyote.http11.Http11Protocol”);
  19.         } else if (“AJP/1.3”.equals(protocol)) {
  20.             setProtocolHandlerClassName
  21.                 (“org.apache.coyote.ajp.AjpProtocol”);
  22.         } else if (protocol != null) {
  23.             setProtocolHandlerClassName(protocol);
  24.         }
  25.     }
  26. }

从以上代码可以看出只要protocol不为HTTP/1.1也不为AJP/1.3就会调用setProtocolHandlerClassName来设置protocolHandler,也就是说要替换默认的protocolHandler,只需要修改server.xml文件Connector中的protocol属性即可!

 

 

Service

连接器和容器一起才能对外提供服务,Service里面包含了一个容器和多个连接器,连接器是怎么加入到Service中的,可以看一下StandardService中的代码:
  1. public void addConnector(Connector connector) {
  2.     synchronized (connectors) {
  3.         connector.setService(this);
  4.         Connector results[] = new Connector[connectors.length + 1];
  5.         System.arraycopy(connectors, 0, results, 0, connectors.length);
  6.         results[connectors.length] = connector;
  7.         connectors = results;
  8.         if (getState().isAvailable()) {
  9.             try {
  10.                 connector.start();
  11.             } catch (LifecycleException e) {
  12.                 log.error(sm.getString(
  13.                         “standardService.connector.startFailed”,
  14.                         connector), e);
  15.             }
  16.         }
  17.         // Report this property change to interested listeners
  18.         support.firePropertyChange(“connector”, null, connector);
  19.     }
  20. }

StandardService中还有setContainer方法,正是Service把容器和连接器关联在一起的

mapperListener

在连接器初始化的时候会初始化mapperListener,mapperListener在初始化的时候会调用mapper对象的addXXX方法

Mapper对象在tomcat中存在于两个地方:

1、每个context容器对象中,它只记录了此context内部的访问资源与相对应的wrapper子容器的映射;

2、connector模块中,这是tomcat全局的变量,它记录了一个完整的映射对应关系,即根据访问的完整URL如何定位到哪个host下的哪个context的哪个wrapper容器。
Servlet中forward跳转会用到第一种mapper,也就是说forward是服务器内部的重定向。

初始化与启动

Connector的初始化过程如下:
1、Tomcat初始化时会调用Bootstrap的Load方法,这里会解析XML文件,Digester解析的过程中,会调用Connector的构造方法
  1. public Connector(String protocol) {
  2.        setProtocol(protocol);
  3.        // Instantiate protocol handler
  4.        try {
  5.            Class<?> clazz = Class.forName(protocolHandlerClassName);
  6.            this.protocolHandler = (ProtocolHandler) clazz.newInstance();
  7.        } catch (Exception e) {
  8.            log.error(sm.getString(
  9.                    “coyoteConnector.protocolHandlerInstantiationFailed”), e);
  10.        }
  11.    }

这个构造方法首先设置protocol,然后初始化protocolhandler

2、紧接着初始化Server,一个Server可能有多个Service,在初始化Server的过程中会初始化多个Service.Service由一个容器和多个连接器组成,会先初始化容器,然后初始化化两个连接器,连接器的初始化是调用的Connector的initInternal方法
  1. protected void initInternal() throws LifecycleException {
  2.     super.initInternal();
  3.     // Initialize adapter
  4.     adapter = new CoyoteAdapter(this);
  5.     protocolHandler.setAdapter(adapter);
  6.     // Make sure parseBodyMethodsSet has a default
  7.     if( null == parseBodyMethodsSet ) {
  8.         setParseBodyMethods(getParseBodyMethods());
  9.     }
  10.     try {
  11.         protocolHandler.init();
  12.     } catch (Exception e) {
  13.         throw new LifecycleException
  14.             (sm.getString
  15.              (“coyoteConnector.protocolHandlerInitializationFailed”), e);
  16.     }
  17.     // Initialize mapper listener
  18.     mapperListener.init();
  19. }

(1)首先为protocolHandler设置一个adapter,然后初始化protocolHandler

(2)初始化mapperListener,啥都没做,只是调用了父类(LifecycleMBeanBase)的initInternal方法!
由于Tomcat的生命周期控制,连接器的启动过程和初始化过程几乎一样,也是由Catalina的start方法开始,Server启动,Service启动,Container启动,Connector启动。Connector启动调用了它的startInternal方法,这个方法只做了两件事:启动protocolHandler和mapperListener,连接器的启动过程就是这样!下面分别来看看protocolHandler和mapperListener启动时都做了哪些事?
protocolHandler的初始化是在其父类AbstractProtocol的start方法中完成的,
  1. public void start() throws Exception {
  2.     if (getLog().isInfoEnabled())
  3.         getLog().info(sm.getString(“abstractProtocolHandler.start”,
  4.                 getName()));
  5.     try {
  6.         endpoint.start();
  7.     } catch (Exception ex) {
  8.         getLog().error(sm.getString(“abstractProtocolHandler.startError”,
  9.                 getName()), ex);
  10.         throw ex;
  11.     }
  12. }

调用了endpoint的start方法,这个方法里面做了以下几件事:

(1)设置接收线程数和最大连接数,创建socket
(2)创建线程池,启动监听的线程监听用户请求
(3)启动一个线程来负责异步请求
mapperListener也继承了LifecycleMBeanBase类,也是受生命周期控制的。所以它的启动是在startInternal方法中完成的
  1. public void startInternal() throws LifecycleException {
  2.        setState(LifecycleState.STARTING);
  3.        // Find any components that have already been initialized since the
  4.        // MBean listener won’t be notified as those components will have
  5.        // already registered their MBeans
  6.        findDefaultHost();
  7.        Engine engine = (Engine) connector.getService().getContainer();
  8.        addListeners(engine);
  9.        Container[] conHosts = engine.findChildren();
  10.        for (Container conHost : conHosts) {
  11.            Host host = (Host) conHost;
  12.            if (!LifecycleState.NEW.equals(host.getState())) {
  13.                // Registering the host will register the context and wrappers
  14.                registerHost(host);
  15.            }
  16.        }
  17.    }

(1)注册已初始化的组件

(2)为各种容器添加监听器
(3)为各种容器建立映射关系
分类: Tomcat 标签:

[转]Tomcat性能调校之连接器模块JIO、APR和NIO

2013年7月11日 没有评论

转自:http://www.gootry.com/wangzhuan/article/100928211150/252

Tomcat提供了三种不同的服务器设计实现方法,以服务于HTTP,并实现与服务于AJP三种设计方法相同的设计方法。


JIO(java.io)

除非在Tomcat启动时找到了APR Connector的libtcnative库,否则这是Tomcat的默认连接器实现方法,这也称为“Coyote”。

 

   它是一个使用java.io核心Java网络类的纯Java TCP包服务器实现。它也完全是HTTP和AJP的模块化实现

 

于是用纯Java编写的,所以对完全支持Java的所有操作系统来说,JIO是二进制可随处移植的(portable)。

 

许多人认为这种实现方法比主流的Apache httpd要慢,因为它是用Java编写的。假定Java总是比编译的C要慢。真的吗?请拭目以待!


APR(Apache Portable Runtime)

第二种连接服务器的方法是以Java类的方式实现,这些Java类包含了一个以C编程语言编写的、置于小的libtcnative库文件中的JNI压缩包,这些Java类轮流取决于Apache Portable Runtime(APR)库文件。

 

   连接Apache httpd Web服务器的类库也是在C中实现的,而且用APR进行网络通信。

 

这种可选的实现方法的目标包括:使用胜过JIO连接器的相同开源C代码,作为Apache httpd的服务器实现方法,并提供至少与Apache httpd同等的性能。

 

因为这种方法主要是在C中实现的,所以缺陷在于单个二进制版本的这种连接器不能像JIO连接器一样运行在所有平台上。

 

这意味着Tomcat管理员需要编译连接器,因此必须具备一定的开发环境,而且可能有编译问题。

 

但是,这种连接器的作者通过要求得到比这种连接器实现要快的Tomcat的Web性能,验证了这种特殊设置的效果。

 

笔者将通过基准调校,让您亲眼看到其实际效果。

 

   NIO(java.nio)
这是用纯Java编写的连接器(Conector)的一种可选方法。该实现用java.nio核心Java网络类以提供非阻塞的TCP包特性。

 

这种Connector设计的主要目标是用非阻塞(nonblocking)的方式部分实现Connectot,以达到使用很少的线程给Tomcat管理员提供比JIO Connector执行效果更好的Connectot实现。

 

另一方面,NIO Connector只需要一个线程就能分析众多连接器的请求,但每个请求随后必须运行自身线程(Java Servlet规范要求限制的)才能寻到servlet。

 

因为部分请求处理是以非阻塞的Java代码完成的,因此,部分请求处理所占用的时间是Java线程不需处于在用状态的时间,这意味着小线程池可用于处理相同数量的并发请求。

 

小线程池通常意味着低CPU占用,轮流使用该线程池意味着获得更好的性能。这样处理使速度更快的理论原因建立在假设高堆栈可以或不可以用于任何入的自身Web应用程序和交换负荷之上。

 

因此,对一些请求处理,NIO Connector会执行更好,而对另一些请求处理,则执行效果更差,这要视其他Connector设计的情况而定。
依靠这些Tomcat Connector,笔者在prefork及worker多线程(Multi-Process Model,缩写为MPM)编译配置下进行Apache httpd基准调校,井要求通过Apache httpd连接器模块,把基准调校的请求所在的httpd prefork和worker配置从Apache httpd发送到Tomcat。

分类: Tomcat 标签:

让后端tomcat日志获取真实的IP,而不是nginx 服务器的IP

2013年5月20日 2 条评论

今天有2个项目组的研发过来问我怎么设置tomcat日志显示真实ip,因为我们前面有个nginx反向代理,所有tomcat取到的是nginx的ip。需修改2个地方,nginx的配置文件和tomcat的server.xml

nginx nginx.conf中加

tomcat server.xml中加

当然你也可以diy日志文件里面的内容,顺序,打印项都可以调整。

测试结果:

 

分类: Tomcat 标签: ,