Chapter Three:Connector
tomcat的Connector名字叫做Coyote,我之前也写了几篇关于coyote的博客,不过在看了第三章后,才对tomcat的Connector有了更加深入的认识。需要说明的是,这一章的Connector只是一个简化版,而第四章介绍的也只是“默认”(旧版本)的Tomcat的Connector,正因为“默认”的Connector性能不佳,才产生了后来的coyote,这是后话。
StringManager
在讲述连接器前,首先介绍一个tomcat内部使用频率非常高的工具类——StringManager,简称sm(O(∩_∩)O有点歧义~)。我们知道tomcat是一个大项目,里面的package很多,而每个package内的类都需要输出很多信息,包括错误信息、调试信息,等等。为了降低耦合性,tomcat开发人员专门设计了这个sm类,用来存取相关的输出信息。每个package都有一个“LocalStrings.properties”文件,就像ini文件那样保存了这些信息。我们只需要像下面这样:
StringManager sm = StringManager.getManager("ex03.pyrmont.connector.http");
就可以获得一个特定于某个package输出信息的sm,然后直接getString即可。为了便于支持多语言,一般还包括了LocalStrings_es.properties和LocalStrings_ja.properties,sm会自动根据本地语言设置来选择相应的语种,这和Struts非常像(兴许Struts就是模仿tomcat的)。可惜,没有LocalStrings_cn.properties
Bootstrap
像我们看到的tomcat6一样,在这一章的小例子中,也专门将Bootstrap类提取出来,用于启动tomcat,当然这里还是非常简单的new了一个Connector,以后的章节会陆续添加功能。在这里,Connector实现了Runnable接口
public final class Bootstrap {
public static void main(String[] args) {
HttpConnector connector = new HttpConnector();
connector.start();
}
}
Connector
终于可以一睹Connector的“芳容”了!很遗憾,这一章的Connector还非常简陋,只是把前一章中监听Socket的部分代码copy了过来,略微有点不同的是:
// Hand this socket off to an HttpProcessor
HttpProcessor processor = new HttpProcessor(this);
processor.process(socket);
很明显,这里多了一个HttpProcessor ,其实就相当于“容器”的角色。tomcat发展到现在,其架构也还是:Connector+Container
HttpProcessor
可以说,HttpProcessor 是这一章的重头戏,大部分功能都是通过这个类,直接或间接地实现的。
照旧,在HttpProcessor 的process方法中,首先获取Socket的输入输出流、new 一个Request和Response,然后调用:
parseRequest(input, output);
parseHeaders(input);
这两个方法是本类的核心,parseRequest是处理http请求行(就是类似“GEThttp://xxx.xxx/xxx?name=xxx”,位于http请求的第一行),parseHeaders则处理请求行之后的一堆header,比如content-length、cookie等。别看只有两个方法,深入进去其实调用了很多其他类的方法,看来解析一个http请求也不是那么容易的。
SocketInputStream
这个类转自tomcat源代码,它对Socket的原始inputstream进行了封装,负责将以下字段分离出来:
http schema:请求行中的GET,POST等等
URI:例如,http://xxx.xxx/xxx,这里还要区分是绝对路径还是相对路径
查询字符串:就是“?”后面的那些键值对
header:http 请求的headers
当然,分离出来的字段都是以char数组的形式保存的,因为生成String的开销很大,通常都是“lazy load”,不到不得已不会随便new string
Populate Request
所谓populate,即是“赋值”的意思,就是调用Request的那些set方法,把之前解析出来的那些字段一个个放进Request对象中,供后续使用。考虑到解析、分割字符串的开销很大,tomcat的原则是把查询字符串(query string)和cookie 的解析工作放到Servlet中,因为这些字段未必一定会用到,要的时候在生成也不迟,从而节省了系统资源。
具体的解析过程很啰嗦,基本上都是字符串处理,在一堆字节中摸爬滚打,这里就不赘述了。
Create Response
相比Request,Response的工作量就小了一些。但也有不少改进之处。首先,之前的Response.getWriter方法,单纯的返回一个包装了Socket的OutputStream的PrintWriter,但这是JAVA自带的PrintWriter,功能上不能完全满足tomcat的需要,例如它的print方法不能自动flush(具体可以参考JavaDoc)。所以,在这里通过两个类:ResponseStream和ResponseWriter,分别拓展了原始的ServletOutputStream和PrintWriter。下面摘取其中一部分代码:
public void write(String s, int off, int len) {
super.write(s, off, len);
super.flush();
}
这是ResponseWriter的write方法,可见就是在PrintWriter的方法上多了一个flush而已
其他
搞定Request后,还是像上一章那样,把Request交给ServletProcessor或者StaticResourceProcessor,基本没有大的变动
总结
首先,这一章的服务器架构如下:(图片源自原书)
处理流程为:
Bootstrap启动HttpConnector
HttpConnector监听Socket端口,将得到的Socket对象交给HttpProcessor
HttpProcessor通过调用parseRequest和parseHeaders方法,解析底层Socket流中的字节,生成Request对象和Response对象
把Request对象和Response对象交给“容器”处理,即ServletProcessor或者StaticResourceProcessor
载入Servlet对象,利用Facade模式,将Request对象和Response对象传入Servlet的service方法,处理,然后通过Response对象的writer,把响应内容返回给客户端
运维网声明
1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网 享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com