您的位置:首页 > 理论基础 > 计算机网络

httpcore和httpclient的源码一点点(1)

2015-11-01 08:32 806 查看
因为需要重写自己github上以前的项目(“eat my dog food”),进行一次彻底的技术上的”寻根究底”.

项目中使用了爬虫获取数据,后台接入了多个爬虫的数据源,这前写这些爬虫的时候,有大量的逻辑是重复的,于是,第一件事情便是抽象出一个简单的java爬虫框架(后期可以使用go和python),采用spring类似的机制: 获取配置文件,扫描项目,获取注解,整个框架只需要用户处理response的页面(html的解析、url抽取,然后决定是否进行存储,其中,url可以扔进调度器, 用户也可以自定义拦截插件) , 整体逻辑可能倾向scrapy。

众所周知,java se自身并没有完整的http包(只有简单的java.net包), j2ee的javax有相关实现(记得 javax.servlet.http.HTTPServletRequest和javax.servlet.http.HTTPServletResponse),如果看过jetty和tomcat容器你会发现他们都有自己的实现.而在其他的语言中,python有httplib包,go有http包,在实际的java开发过程中,用的最多的就是httpclient了吧,除了android多了一个选择。

httpclient是在httpcore上进行开发的,于是先看httpcore。

httpcore中,分成了如下几个包 :

entity : 响应体和请求体的抽象, buffered bytearray file inputstream类型的实现,序列化化和反序列化 -> 中规中矩

io -> messe的读取和写入(官方注释给出了data source 和 data sink的短语,有点flume的意思,这里就不误导了),还提供了两个类似jdk的InputStream和OutputStream的实现 : SessionInoutStream和SessionOutputStream, 区别在于提供了写入和读取行(readLine和writeLine的方法),别被Session的前缀欺骗了,还有一个HttpTransportMetrics类

message->提供 requestLine statusLine header HTTPRequest HTTPResponse的实现,有iterator迭代器、(value/line)formatter格式化器、namevaluePair、LineParser解析器等

params->HTTPPrams、HTTPConnectionParams、HTTPProtocolParams这几个参数的类为主

protocol-> HTTPContext、HTTPProcessor(合并了HTTPRequestProcessor和HTTPResponseProcessor) , Rquest相关的处理器(useragent、TargetHost、ExpectContinue、Date、Content、ConnControl、组合的列表->个人感觉命名Composite比List好), Response相关的处理器(ConnControl、Content、Date、Server、组合的列表->个人感觉命名Composite比List好), HTTP类中存放了常见的常量,有几个比较有意思的东西 : HTTPRequestexecutor(客户端使用的阻塞io的 HTTP 协议处理,根据请求获取响应)、HttpRequestHandler(根据request产生response的接口:服务器端使用)、HttpRequestHandlerResolver(维护HttpRequesthandler的管理的接口)、HttpRequestHandlerRegistry(相应的实现)和UriPatternMatcher(内部使用map实现注册、注销、查询)

util->常用的工具

imp - >entity、io的实现, io 还是比较有趣的

直接实现 :http协议的常见抽象(看过jetty实现的同学会很清楚),像HTTPEntry、 HTTPRequest 、HTTPResponse等,都是根据rfc 2613等文件写的,没有太多的惊艳的地方

测试 :

官方的example目录中的代码在请求中文网站会有问题,问题在EntityUtil上,我后面给成了BufferedReader进行输出,没有乱码问题。

@Test
public void testMethod(){
// params有点怪怪的
//
System.out.println("开始了");
HttpParams params = new BasicHttpParams();
HttpProtocolParams.setVersion(params, HttpVersion.HTTP_1_1);
HttpProtocolParams.setContentCharset(params, "UTF-8");
HttpProtocolParams.setUserAgent(params, "Mozilla/5.0 (Windows; U; Windows NT 6.1; en-us) AppleWebKit/534.50 (KHTML, like Gecko) Version/5.1 Safari/534.50");
HttpProtocolParams.setUseExpectContinue(params, true);

// 可以添加自定义的处理器
BasicHttpProcessor httpproc = new BasicHttpProcessor();
// Required protocol interceptors
httpproc.addInterceptor(new RequestContent());
httpproc.addInterceptor(new RequestTargetHost());
// Recommended protocol interceptors
httpproc.addInterceptor(new RequestConnControl());
// httpproc.addInterceptor(new RequestUserAgent());
httpproc.addInterceptor(new RequestExpectContinue());

HttpRequestExecutor httpexecutor = new HttpRequestExecutor();

HttpContext context = new BasicHttpContext(null);
// 需要存放ip地址, HttpHost host = new HttpHost("http://51voa.com", 80);
HttpHost host = new HttpHost("110.76.43.78",80);

DefaultHttpClientConnection conn = new DefaultHttpClientConnection();
ConnectionReuseStrategy connStrategy = new DefaultConnectionReuseStrategy();

context.setAttribute(ExecutionContext.HTTP_CONNECTION, conn);
context.setAttribute(ExecutionContext.HTTP_TARGET_HOST, host);

try {
// 第一种是官方的,第二种竟没有resolveURL的类似解决方案
// String[] targets = {
//         "/",
//         "/servlets-examples/servlet/RequestInfoExample",
//         "/somewhere%20in%20pampa"};

// String[] targets = {
//         "/",
//         "/VOA_Standard_English/microscope-brings-tiny-worlds-closer-researchers-66090.html",
//         "/Technology_Report_1.html"};

String[] targets = {
"http://www.51voa.com/VOA_Standard_1.html",
"http://www.51voa.com/VOA_Standard_English/microscope-brings-tiny-worlds-closer-researchers-66090.html",
"http://www.51voa.com/Technology_Report_1.html"};

for (int i = 0; i < targets.length; i++) {

if (!conn.isOpen()) {

Socket socket = new Socket(host.getHostName(), host.getPort());
conn.bind(socket, params);

}

BasicHttpRequest request = new BasicHttpRequest("GET", targets[i]);
System.out.println(">> Request URI: " + request.getRequestLine().getUri());

request.setParams(params);

httpexecutor.preProcess(request, httpproc, context);

HttpResponse response = httpexecutor.execute(request, conn, context);

response.setParams(params);
httpexecutor.postProcess(response, httpproc, context);

System.out.println("<< Response: " + response.getStatusLine());
// 这里需要进行处理
BufferedReader reader = new  BufferedReader(new InputStreamReader(response.getEntity().getContent()));
String str = null;
while((str=reader.readLine())!=null){
System.out.println(" line => " + str);
}
// 使用这个方法会导致 乱码
// System.out.println(EntityUtils.toString(response.getEntity()));

System.out.println("==============");
if (!connStrategy.keepAlive(response, context)) {
conn.close();
} else {
System.out.println("Connection kept alive...");
}
}
}catch(Exception ex){

}finally {
System.out.println("结束");
try{
conn.close();
}catch(Exception e){

}
}
}


从上面可以看到,最大的特点 : httpproc.addInterceptor, 添加拦截器,是不是有点回到struts的赶脚,别冲动,这里讨论的是爬虫, 拦截器Interceptor 可以作为爬虫的预处理器middleware、ProcessChain,这些只是名字上的差异,本质就是针对404、5xx、3xx redirect 、auth、useragent 等情况进行处理,貌似浮出了爬虫的影子。

而且,除此之外,还提供了nio的实现,还是很贴心的,准备放入项目中进行测试。会不会跟新到aio呢?还是很期待的。

夜深了,学校断网了,明天再发帖吧。

[align=right]by 徐建海[/align]
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息