stone2083

#

Hosts绑定新思路之DNS代理服务器实现篇

     摘要: 背景 详见《Hosts绑定新思路之DNS代理篇》 核心内容 1. DNS协议解析 2. 启动UDP服务,监听53端口 3. 根据DB或者文本,进行Hosts解析 DNS协议 DNS Protocol Overview (推荐) 非强详细,但是不怎么看得懂的长篇大论 如果没有耐心的同学,可以看看我通过wireshark分析之后制作的两张gif图片。大概能知道DNS协议的...  阅读全文

posted @ 2011-05-23 21:24 stone2083 阅读(2895) | 评论 (0)编辑 收藏

Hosts绑定新思路之DNS代理篇

前言

此文摘自2011年5月23日邮件分享,为《Hosts绑定新思路之HTTP代理篇》续集



电视有续集,电影也有续集,Hosts绑定思路同样有续集.

 

我们先用一句话来回顾下,上集中关于Hosts绑定的思路:

原理:利用Http代理的方式,将分散在各个客户端的Hosts绑定,集中绑定在Http代理服务器上

优点:集中管理

缺点:一台Http代理服务器,只能绑定一组Hosts信息

(详细内容,请见之前的邮件)

 

在当时描述方案邮件的时候,也意识到了方案存在的不足,所以一直在思考改进方案(详见之前邮件中最后一节改进方案思路).

经过一段时间的思考,改进方案有了大概的雏形将之前的HTTP代理方案 替换成 DNS代理方案

 

俗话说得好:有图有真相.先贴上一张架构图,之后再用文字慢慢解 


 

架构中核心组件是:DNS BackOffice服务器 DNS代理服务器

DNS BackOffice服务器的作用有:

1.       开发/测试管理员通过BackOffice服务维护各自项目的绑定信息,BackOffice服务将之持久化                (图中 蓝色虚线)

2.       开发/测试人员通过BackOffice服务,告知需要哪个项目的绑定信息,BackOffice服务将之持久化           (图中 黑色虚线)

 

DNS代理服务器的作用有:

1.       拦截Domain Name的解析.通过来源IP判断需要绑定的Hosts信息,File/DB得到对应的IP,通过DNS协议返回  (图中 红色实线  黑色实线)

2.       如果不在绑定之列,则请求上级DNS服务器,返回其Response.

 

此方案的优势:

1.       本地Hosts绑定优先.

只要本地Hosts有绑定IP,则不会请求DNS代理服务器.请求本地Hosts文件.能满足个性化需求.

2.       DNS代理服务器支持多种绑定方式,如通配符,正则等

对于目前旺铺,完全可以使用通配符, *.cn.alibaba.com,简化配置工作量

3.       操作简单

只要将DNS服务器设置成DNS代理服务器IP即可 (附录中有详细说明)

4.       有效利用现有成果

目前测试同学已经集中维护了Hosts绑定信息,只要部署DNS代理服务器,并做简单的集成即可

5.       DNS代理服务器代码轻量小巧,易于修改扩展

目前一共只有212行代码,其中DNS协议部分130,DNS代理部分82.

 

 

附录

I.                    客户端如何设置DNS服务器

Windows用户,见图:


Linux用户,见图:


修改 /etc/resolv.conf文件即可

posted @ 2011-05-23 19:42 stone2083 阅读(1802) | 评论 (0)编辑 收藏

Hosts绑定新思路之HTTP代理篇

前言

此文摘自2011年3月22日邮件分享


现状

平时开发,测试,功能预演阶段,为了能够正常访问应用,需要做Hosts绑定.随着应用数量的不断增多,绑定量也是急剧上升.例如最近工作平台三期项目,需要绑定的环境多达44个.一旦有变动,需要通知所有人员做本地Hosts的调整,维护成本那是相当地大.

 

用一张图,来描述下目前我们的方案:


 

如果站在面向对象编程的角度,来思考这张图,我们会发现.

1.       利用客户端本地Hosts绑定来实现,并且客户端数量不可控—利用客户端解决需求,但客户端维护不在可控范围内

2.       Hosts绑定是非常不稳定的—需求易变

 

这样的设计,违反了”封装变化”的设计原则,故一旦有变动,维护成本非常大.

 

新方案思路

按照”封装变化”的设计原则,我们就应该把”域名绑定”这个易变需求,进行统一管理.

看上图,我们会发现,DNS的职责就是做域名解析的,并且DNS管理比较可控.

于是第一反应,我们可以使用内部域名解析服务器来绑定这些域名.

但是问题又来了,DNS来做测试环境域名解析,太重量级了.同一个域名,对应测试服务器IP有多个,绑定哪一个好呢?并且域名对应IP不断变化,IT DNS负责人不被我们累死啊? 

 

既然DNS上做文章不可行,又需要统一管理的地方,那么我们只能再抽象出一个新的概念来.

同样,我们利用一张图,来描述下整体架构.



与上图相对,此图多了一个”代理服务器”的概念,即Hosts绑定动作在此概念上完成.

流程如下:

1.       客户端浏览器设置代理服务器,将所有请求发送到代理服务器上

2.       代理服务器检查本地Hosts绑定,如绑定则直接解析,反之进入流程3

3.       代理服务器通过内部域名服务器解析域名

4.       代理服务器发送请求到测试服务器上,并且将响应内容返回给客户端

 

具体尝试性实施方案如下(在XX项目过程中有成功案例)

1.       利用squid搭建代理服务器 (代理地址: 10.20.131.207:3128)

备注:

Squid配置介绍见附录I

 

2.       浏览器配置代理

全局代理: 代理服务器上,直接填写 10.20.131.207 3128

局部代理: 通过pac实现,选择”使用自动配置脚本”,脚本格式内容如下:



备注:

Pac脚本详细介绍见附录II

为了防止将配置工作带给PD,销售等,我们可以使用配置好的绿色浏览器提供直接使用.

推荐一款:GreenBrowser: http://www.morequick.com/indexen.htm

 

IE具体配置,见下图:



Firefox同样支持代理和pac脚本

Chrome需要安装proxy switchy插件来支持.

 

改进方案思路

上述的方案中,有两个比较大的缺陷

1.       代理服务器没有多实例概念

代理服务器通过hosts绑定.hosts是全局性的,意味着一台代理服务器只能服务一组需求.而事实上,我们不同的项目需要的绑定都是不一样的.

2.       特性化需求不能满足

绑定全在代理服务器上做了,客户端本地个性化需求无法支持

 

所以,我理想中整体架构是这样的,见图:


1.       优先查看本地hosts文件

2.       代理服务器支持多实例部署,不同实例有不同的hosts绑定配置.

 

目前具体实现方案,还在构思中.欢迎大家提供实现方案思路.

 

附录I

Squid权威指南(中文版): http://home.arcor.de/pangj/squid/chap01.html

附录II

Pac介绍:               http://en.wikipedia.org/wiki/Proxy_auto-config

Pac函数介绍:        http://findproxyforurl.com/pac_functions_explained.html

 

posted @ 2011-05-23 19:41 stone2083 阅读(2321) | 评论 (1)编辑 收藏

django框架的几个扩展点

前段时间替朋友做了一个物业管理系统,使用了python+django技术,对django有了一些了解。
作为一个一直来使用java的人来说,初次使用django,真正体会到了简单美学。(一共13个功能,不到500行代码)
此文,主要总结下django框架的一些扩展点:
MIDDLEWARE_CLASSES
在request请求之前,或者response请求之后,做拦截,允许自定义逻辑。有些类似J2EE Servlet中的Filter概念。
TEMPLATE_CONTEXT_PROCESSORS
进入模板渲染之前,允许放入一组用于模板渲染的Key-Value属性。
TEMPLATE FILTER
模板中的管道语法,通过自定义行为,添加用于显示的一些逻辑。
TEMPLATE TAG
模板tag,添加一组行为。有些类似Velocity中的ToolSet功能。
模板tag+指定模板,充当页面组件(widgets)功能

middleware演示
 1 from django.db import connection
 2 from django.http import HttpResponseRedirect
 3 
 4 #拦截response请求之后,打印请求中的所有sql
 5 class SqlLogMiddleware(object):
 6     def process_response(self, req, res):
 7         for sql in connection.queries:
 8             print sql
 9         return res
10 
11 #拦截request请求之前,做权限校验
12 class Auth(object):
13     def process_request(self, req):
14         if req.path == '/admin/':
15             return
16         if not req.user.is_authenticated():
17             return HttpResponseRedirect('/admin/')
18 
1 MIDDLEWARE_CLASSES = (
2     'django.middleware.common.CommonMiddleware',
3     'django.contrib.sessions.middleware.SessionMiddleware',
4     'django.contrib.auth.middleware.AuthenticationMiddleware',
5     'finance.middleware.SqlLogMiddleware',
6     'finance.middleware.Auth',
7 )


template context processor演示
1 def version(request):
2     return {'name':'Stone.J',
3             'version':'1.0-beata',
4             'date':'2011-03-20'}
1 TEMPLATE_CONTEXT_PROCESSORS = (
2     'django.core.context_processors.request',
3     'django.core.context_processors.auth',
4     'django.core.context_processors.debug',
5     'django.core.context_processors.i18n',
6     'django.core.context_processors.media',
7     'finance.example.context_processors.version',
8 )

template filter演示
 1 def row(value):
 2     if not value:
 3         return 'row1'
 4     if value % 2 == 1:
 5         return 'row1'
 6     else:
 7         return 'row2'
 8     
 9 def math_mul(value, num):
10     return value * num
11 
12 def math_add(value, num):
13     return value + num
14 
15 register = template.Library()    
16 register.filter('row', row)
17 register.filter('math_add', math_add)
18 register.filter('math_mul', math_mul)
1 {% load my_filter %}
2 {% for c in page.object_list %}
3 <tr class="{{ forloop.counter|row }}">
4 <td>{{ c.amount | math_add:c.amount2}}</td>
5 <td>{{ c.amount | math_mul:12}}</td>
6 </tr>
7 {% endfor %}
通过约定的方式,在任意一个app下,建立一个templatetags目录,会自动寻找到。(不过没有命名空间,是一个比较猥琐的事情,容易造成不同app下的冲突)

template tag演示
 1 register = template.Library()
 2 
 3 class AccountNode(template.Node):
 4     def __init__(self, name):
 5         self.name = name
 6         
 7     def render(self, context):
 8         context[self.name] = Account.objects.get()
 9         return ''
10     
11 def get_account(parser, token):
12     try:
13         tag_name, name = token.split_contents()
14     except ValueError:        
15         raise template.TemplateSyntaxError, "%s tag requires argument" % tag_name
16     return AccountNode(name)
17 
18 register.tag('get_account', get_account)

1 {% load my_tag %}
2 {% get_account account %}<!-- 通过tag取到内容赋值给account变量 -->
3 {{ account.amount }}

template tag + template file演示
1 from django import template
2 register = template.Library()
3 
4 def version(context):
5     return {'name':'Stone.J',
6             'version':'1.0-beata',
7             'date':'2011-03-20'}
8 
9 register.inclusion_tag('example/version.html', takes_context=True)(version)
1 <!-- 这份内容可以被当成widget复用 -->
2 <table>
3     <tr>
4         <td>{{ name }}</td>
5         <td>{{ version }}</td>
6         <td>{{ data }}</td>
7     </tr>
8 </table>
9 
tag寻找模式等同于filter。

posted @ 2011-04-20 22:54 stone2083 阅读(2560) | 评论 (0)编辑 收藏

命令行下翻译工具

接上文,继续show下我命令行下的工具--翻译脚本
(利用了google 翻译 json api:http://translate.google.cn/translate_a/t?client=t&text=%s&hl=zh-CN&sl=%s&tl=%s

特性:
1. 自动识别中翻英/英翻中
2. 翻译

涉及技术:
1. python
2. urllib
3. json
4. re

截图:


对应代码:
 1 '''
 2 Created on 2010-11-28
 3 
 4 @author: stone
 5 '''
 6 import json
 7 import re
 8 import sys
 9 import urllib2
10 import types
11 
12 res = 'http://translate.google.cn/translate_a/t?client=t&text=%s&hl=zh-CN&sl=%s&tl=%s'
13 agent = 'Mozilla / 5.0 (X11; U; Linux i686; en - US) AppleWebKit / 534.7 (KHTML, like Gecko) Chrome / 7.0.517.44 Safari / 534.7'
14 
15 def get_data(text, sl='en', tl='zh-CN'):
16     req = urllib2.Request(res % (urllib2.quote(text), sl, tl))
17     req.add_header('user-agent', agent)
18     content = urllib2.urlopen(req).read()
19     return json.loads(to_standard_json(content))
20 
21 def show(data):
22     #step1
23     print u'翻译:\n  %s' % (data[4][0][0])
24     #step2
25     if types.ListType == type(data[1]):
26         print u'\n字典:'
27         for word in data[1]:
28             print word[0]
29             if len(word) > 1:
30                 for i, w in enumerate(word[1]):
31                     print '  %s.%s' % (i + 1, w) 
32 
33 def to_standard_json(json):
34     p = re.compile(r',([,\]])')
35     while(p.search(json)):
36         json = p.sub(lambda m:',null%s' % (m.group(1)), json)
37     return json
38 
39 def contains_cn(text):
40     for c in text:
41         if ord(c) > 127:
42             return True
43     return False
44 
45 if __name__ == '__main__':
46     if not len(sys.argv) == 2 or not sys.argv[1].strip():
47         print 'Useage:translate.py word'
48         sys.exit()
49     word = sys.argv[1].strip()
50     if contains_cn(word):
51         show(get_data(word, 'zh-CN''en'))
52     else:
53         show(get_data(word, 'en''zh-CN'))


posted @ 2011-04-17 19:49 stone2083 阅读(2482) | 评论 (3)编辑 收藏

Linux下基于命令行的音乐播放器

按照同事的话说,我是一个十足的命令控。
利用最近项目通宵发布的空闲时间中,写了一个命令行下的音乐播放器,以满足我在linux命令下的需求。

播放器利用技术:
Python+GST(http://gstreamer.freedesktop.org/modules/gst-python.html)+Console解析

播放器自持操作:
1. 播放
2. 下一首
3. 上一首
4. 暂停
5. 查看播放列表信息
6. 查看当前播放信息
7. 停止(退出)

看一张截图:


通过分析meliae dump出来的内存信息,差不做占用2.5M内存,算的上比较小巧了。

对应代码:(需要安装py-gst,ubuntu下:sudo apt-get install python-gst0.10)
  1 #!/usr/bin/env python
  2 
  3 import gst
  4 import gobject
  5 import sys
  6 #to avoid eclipse'warning
  7 eval('gobject.threads_init()'
  8 from threading import Thread
  9 
 10 class AudioPlayer:
 11     
 12     EVENT_PLAY_NEW = 1
 13     
 14     def __init__(self, advisor):
 15         self.main = gobject.MainLoop()
 16         self.player = gst.element_factory_make('playbin''player')
 17         self.index = -1
 18         self.list = None
 19         self.advisor = advisor
 20         
 21         bus = self.player.get_bus()
 22         bus.add_signal_watch()
 23         bus.connect('message', self.on_message)
 24         
 25         Thread(target=self.main.run).start()
 26        
 27     def add_list(self , list=[]):
 28         if list is None:
 29             list = []
 30         self.list = [(i, l.strip(), l[l.rfind('/'+ 1:]) for (i, l) in enumerate(list)]
 31         
 32     def play(self, index=None):
 33         #play specified tracks
 34         if 0 <= index < len(self.list):
 35             self.index = index
 36             self.player.set_state(gst.STATE_NULL)
 37             self.player.set_property('uri', self.list[index][1])
 38             self.player.set_state(gst.STATE_PLAYING)
 39             if self.advisor:
 40                 self.advisor.on_message(AudioPlayer.EVENT_PLAY_NEW, (self.index, self.get_title()))
 41         #resume playing
 42         if index is None:
 43             if self.index > -1:
 44                 self.player.set_state(gst.STATE_PLAYING)
 45     
 46     def pause(self):
 47         self.player.set_state(gst.STATE_PAUSED)
 48         
 49     def stop(self):
 50         self.player.set_state(gst.STATE_NULL)
 51         self.main.quit()
 52     
 53     def get_title(self):
 54         if self.index == -1 or len(self.list) == 0:
 55             return None
 56         return self.list[self.index][2
 57     
 58     def get_previous(self):
 59         if self.index == -1 or len(self.list) == 0:
 60             return - 1
 61         if self.index == 0:
 62             return 0
 63         return self.index - 1
 64     
 65     def get_next(self):
 66         if  len(self.list) == 0:
 67             return - 1
 68         if self.index + 1 == len(self.list):
 69             return 0
 70         return self.index + 1
 71     
 72     def on_message(self, bus, message):
 73         t = message.type
 74         if t == gst.MESSAGE_ERROR:
 75             self.play(self.get_next())
 76         elif t == gst.MESSAGE_EOS:
 77             self.play(self.get_next())
 78 
 79 class Console:
 80     
 81     def __init__(self, list):
 82         self.player = AudioPlayer(self)
 83         self.player.add_list(list)
 84         self.player.play(0)
 85 
 86         Thread(target=self.run).start()
 87         
 88     def run(self):
 89         while(True):
 90             self.on_cmd(raw_input())
 91     
 92     def on_cmd(self, cmd):
 93         if cmd is None:
 94             return
 95         if cmd.startswith('play'):
 96             self.player.play()
 97         elif cmd.startswith('next'):
 98             self.player.play(self.player.get_next())
 99         elif cmd.startswith('previous'):
100             self.player.play(self.player.get_previous())
101         elif cmd.startswith('pause'):
102             self.player.pause()
103         elif cmd.startswith('list'):
104             print '====================================='
105             for info in self.player.list:
106                 print '%s. %s' % (info[0], info[2])
107             print '====================================='
108         elif cmd.startswith('info'):
109             print '====================================='
110             print '%s. %s' % (self.player.index, self.player.get_title())
111             print '====================================='
112         elif cmd.startswith('stop'):
113             self.player.stop()
114             sys.exit(0)
115         elif cmd.startswith('dump'):
116             from meliae import scanner
117             scanner.dump_all_objects('./dump.txt')
118         else:
119             print '''=====================================
120 Usage:
121 play
122 next
123 previous
124 pause
125 list
126 info
127 stop
128 dump
129 ====================================='''
130     
131     def on_message(self, event, info):
132         if event == AudioPlayer.EVENT_PLAY_NEW:
133             print '====================================='
134             print 'Tracks: %s.%s' % (info[0], info[1])
135             print '====================================='
136 
137 
138 if len(sys.argv) != 2:
139     print 'player.py mp3.list'
140     sys.exit(-1)
141 list = [l.strip() for l in open(sys.argv[1]).readlines() if l.strip() != '']
142 Console(list)

下载

posted @ 2011-04-17 19:32 stone2083 阅读(4250) | 评论 (4)编辑 收藏

推荐:Eclipse全屏插件

推荐一个eclipse插件--全屏插件(显示器整个屏幕)。
发觉这个东东还是挺不错的,尤其对于本本的同学,特别实用。
在我自己的本本上,发现一旦使用全屏,能多显示8行代码,多了21%左右,挺可观的。

插件地址:http://code.google.com/p/eclipse-fullscreen/

给个图:


posted @ 2011-04-09 21:12 stone2083 阅读(3366) | 评论 (5)编辑 收藏

HttpClient使用过程中的安全隐患

HttpClient使用过程中的安全隐患,这个有些标题党。因为这本身不是HttpClient的问题,而是使用者的问题。

安全隐患场景说明:
一旦请求大数据资源,则HttpClient线程会被长时间占有。即便调用了org.apache.commons.httpclient.HttpMethod#releaseConnection()方法,也无济于事。
如果请求的资源是应用可控的,那么不存在任何问题。可是恰恰我们应用的使用场景是,请求资源由用户自行输入,于是乎,我们不得不重视这个问题。

我们跟踪releaseConnection代码发现:
org.apache.commons.httpclient.HttpMethodBase#releaseConnection()
 1 public void releaseConnection() {
 2     try {
 3         if (this.responseStream != null) {
 4             try {
 5                 // FYI - this may indirectly invoke responseBodyConsumed.
 6                 this.responseStream.close();
 7             } catch (IOException ignore) {
 8             }
 9         }
10     } finally {
11         ensureConnectionRelease();
12     }
13 }
org.apache.commons.httpclient.ChunkedInputStream#close()
 1 public void close() throws IOException {
 2     if (!closed) {
 3         try {
 4             if (!eof) {
 5                 exhaustInputStream(this);
 6             }
 7         } finally {
 8             eof = true;
 9             closed = true;
10         }
11     }
12 }
org.apache.commons.httpclient.ChunkedInputStream#exhaustInputStream(InputStream inStream)
1 static void exhaustInputStream(InputStream inStream) throws IOException {
2     // read and discard the remainder of the message
3     byte buffer[] = new byte[1024];
4     while (inStream.read(buffer) >= 0) {
5         ;
6     }
7 }
看到了吧,所谓的丢弃response,其实是读完了一次请求的response,只是不做任何处理罢了。

想想也是,HttpClient的设计理念是重复使用HttpConnection,岂能轻易被强制close呢。

怎么办?有朋友说,不是有time out设置嘛,设置下就可以下。
我先来解释下Httpclient中两个time out的概念:
1.public static final String CONNECTION_TIMEOUT = "http.connection.timeout";
即创建socket连接的超时时间:java.net.Socket#connect(SocketAddress endpoint, int timeout)中的timeout

2.public static final String SO_TIMEOUT = "http.socket.timeout";
即read data过程中,等待数据的timeout:java.net.Socket#setSoTimeout(int timeout)中的timeout

而在我上面场景中,这两个timeout都不满足,确实是由于资源过大,而占用了大量的请求时间。

问题总是要解决的,解决思路如下:
1.利用DelayQueue,管理所有请求
2.利用一个异步线程监控,关闭超长时间的请求

演示代码如下:
  1 public class Misc2 {
  2 
  3     private static final DelayQueue<Timeout> TIMEOUT_QUEUE = new DelayQueue<Timeout>();
  4 
  5     public static void main(String[] args) throws Exception {
  6         new Monitor().start(); // 超时监控线程
  7 
  8         new Request(4).start();// 模拟第一个下载
  9         new Request(3).start();// 模拟第二个下载
 10         new Request(2).start();// 模拟第三个下载
 11     }
 12 
 13     /**
 14      * 模拟一次HttpClient请求
 15      * 
 16      * @author <a href="mailto:li.jinl@alibaba-inc.com">Stone.J</a> 2011-4-9
 17      */
 18     public static class Request extends Thread {
 19 
 20         private long delay;
 21 
 22         public Request(long delay){
 23             this.delay = delay;
 24         }
 25 
 26         public void run() {
 27             HttpClient hc = new HttpClient();
 28             GetMethod req = new GetMethod("http://www.python.org/ftp/python/2.7.1/Python-2.7.1.tgz");
 29             try {
 30                 TIMEOUT_QUEUE.offer(new Timeout(delay * 1000, hc.getHttpConnectionManager()));
 31                 hc.executeMethod(req);
 32             } catch (Exception e) {
 33                 System.out.println(e);
 34             }
 35             req.releaseConnection();
 36         }
 37 
 38     }
 39 
 40     /**
 41      * 监工:监控线程,通过DelayQueue,阻塞得到最近超时的对象,强制关闭
 42      * 
 43      * @author <a href="mailto:li.jinl@alibaba-inc.com">Stone.J</a> 2011-4-9
 44      */
 45     public static class Monitor extends Thread {
 46 
 47         @Override
 48         public void run() {
 49             while (true) {
 50                 try {
 51                     Timeout timeout = TIMEOUT_QUEUE.take();
 52                     timeout.forceClose();
 53                 } catch (InterruptedException e) {
 54                     System.out.println(e);
 55                 }
 56             }
 57         }
 58 
 59     }
 60 
 61     /**
 62      * 使用delay queue,对Delayed接口的实现 根据请求当前时间+该请求允许timeout时间,和当前时间比较,判断是否已经超时
 63      * 
 64      * @author <a href="mailto:li.jinl@alibaba-inc.com">Stone.J</a> 2011-4-9
 65      */
 66     public static class Timeout implements Delayed {
 67 
 68         private long                  debut;
 69         private long                  delay;
 70         private HttpConnectionManager manager;
 71 
 72         public Timeout(long delay, HttpConnectionManager manager){
 73             this.debut = System.currentTimeMillis();
 74             this.delay = delay;
 75             this.manager = manager;
 76         }
 77 
 78         public void forceClose() {
 79             System.out.println(this.debut + ":" + this.delay);
 80             if (manager instanceof SimpleHttpConnectionManager) {
 81                 ((SimpleHttpConnectionManager) manager).shutdown();
 82             }
 83             if (manager instanceof MultiThreadedHttpConnectionManager) {
 84                 ((MultiThreadedHttpConnectionManager) manager).shutdown();
 85             }
 86         }
 87 
 88         @Override
 89         public int compareTo(Delayed o) {
 90             if (o instanceof Timeout) {
 91                 Timeout timeout = (Timeout) o;
 92                 if (this.debut + this.delay == timeout.debut + timeout.delay) {
 93                     return 0;
 94                 } else if (this.debut + this.delay > timeout.debut + timeout.delay) {
 95                     return 1;
 96                 } else {
 97                     return -1;
 98                 }
 99             }
100             return 0;
101         }
102 
103         @Override
104         public long getDelay(TimeUnit unit) {
105             return debut + delay - System.currentTimeMillis();
106         }
107 
108     }
109 
110 }


本来还想详细讲下DelayQueue,但是发现同事已经有比较纤细的描述,就加个链接吧 (人懒,没办法)
http://agapple.iteye.com/blog/916837
http://agapple.iteye.com/blog/947133

备注:
HttpClient3.1中,SimpleHttpConnectionManager才有shutdown方法,3.0.1中还存在 :)

posted @ 2011-04-09 20:46 stone2083 阅读(4743) | 评论 (0)编辑 收藏

HtmlParser疑似Bug

最近的项目中,使用到了HtmlParser(1.5版本).在使用过程中(如访问url为:http://athena2002.vip.china.alibaba.com/ ),遇到了异常:
Exception in thread "main" java.lang.IllegalArgumentException: invalid cookie name: Discard
    at org.htmlparser.http.Cookie.
<init>(Cookie.java:136)
    at org.htmlparser.http.ConnectionManager.parseCookies(ConnectionManager.java:
1126)
    at org.htmlparser.http.ConnectionManager.openConnection(ConnectionManager.java:
621)
    at org.htmlparser.http.ConnectionManager.openConnection(ConnectionManager.java:
792)
    at org.htmlparser.Parser.
<init>(Parser.java:251)
    at org.htmlparser.Parser.
<init>(Parser.java:261)
检查代码,发现:
org.htmlparser.http.Cookie
 1 public Cookie (String name, String value)
 2     {
 3         if (!isToken (name) || name.equalsIgnoreCase ("Comment"// rfc2019
 4                 || name.equalsIgnoreCase ("Discard"// 2019++
 5                 || name.equalsIgnoreCase ("Domain")
 6                 || name.equalsIgnoreCase ("Expires"// (old cookies)
 7                 || name.equalsIgnoreCase ("Max-Age"// rfc2019
 8                 || name.equalsIgnoreCase ("Path")
 9                 || name.equalsIgnoreCase ("Secure")
10                 || name.equalsIgnoreCase ("Version"))
11             throw new IllegalArgumentException ("invalid cookie name: " + name);
12         mName = name;
13         mValue = value;
14         mComment = null;
15         mDomain = null;
16         mExpiry = null// not persisted
17         mPath = "/";
18         mSecure = false;
19         mVersion = 0;
20     }
一旦发现name值为“Discard”,则抛异常。

而在org.htmlparser.http.ConnectionManager.parseCookies (URLConnection connection) 解析cookie的代码中,见代码片段
if (key.equals ("domain"))
                            cookie.setDomain (value);
                        
else
                            
if (key.equals ("path"))
                                cookie.setPath (value);
                            
else
                                
if (key.equals ("secure"))
                                    cookie.setSecure (
true);
                                
else
                                    
if (key.equals ("comment"))
                                        cookie.setComment (value);
                                    
else
                                        
if (key.equals ("version"))
                                            cookie.setVersion (Integer.parseInt (value));
                                        
else
                                            
if (key.equals ("max-age"))
                                            {
                                                Date date 
= new Date ();
                                                
long then = date.getTime () + Integer.parseInt (value) * 1000;
                                                date.setTime (then);
                                                cookie.setExpiryDate (date);
                                            }
                                            
else
                                            {   
// error,? unknown attribute,
                                                
// maybe just another cookie not separated by a comma
                                                cookie = new Cookie (name, value); //出问题的地方
                                                cookies.addElement (cookie);
                                            }
没有对Discard做特殊处理。
无奈之下,覆写了此方法,加上对Discard的处理--直接continue :)

今天在写blog的时候,拿了1.6的代码测试,发现没有问题,分析代码后发现
1. ConnectionManager parserCookie之前,加了条件判断
if (getCookieProcessingEnabled ())
  parseCookies (ret);
默认情况下,条件为false
2. parserCookie的时候,catch了异常
 1 // error,? unknown attribute,
 2 // maybe just another cookie
 3 // not separated by a comma
 4 try
 5 {
 6     cookie = new Cookie (name,
 7         value);
 8     cookies.addElement (cookie);
 9 }
10 catch (IllegalArgumentException iae)
11 {
12     // should print a warning
13     // for now just bail
14     break;
15 }
虽然解决了问题,但是明显还没有意识到Discard的问题。

从我的理解看,最合理的解决方案是:
1. org.htmlparser.http.Cookie中添加 boolean discard方法
2. org.htmlparser.http.ConnectionManager parserCookies()方法,对Discard做处理,如有值,则设置cookie.discard=true

关于discard的解释,见http://www.faqs.org/rfcs/rfc2965.html
Discard
OPTIONAL. The Discard attribute instructs the user agent to
discard the cookie unconditionally when the user agent terminates

posted @ 2011-04-08 20:50 stone2083 阅读(1847) | 评论 (2)编辑 收藏

语言杂谈(shell/python/java)

背景:
公司产品一直使用Java作为开发语言,平时常用Shell脚本进行文本的处理,最近自学了Python.
感觉这3门语言挺具代表性,故对其分析了自己对它们的理解。
由于是部门分享,考虑避免重复劳动力,就把PPT转成图片放上来。

内容:















结束语:
一家之言,属于个人观点

备注:
ubuntu下convert命令挺强悍的,支持PDF和图片的互转

附上PDF文档:
分享文档



posted @ 2010-11-08 17:24 stone2083 阅读(2145) | 评论 (2)编辑 收藏

仅列出标题
共10页: 上一页 1 2 3 4 5 6 7 8 9 下一页 Last