-
Notifications
You must be signed in to change notification settings - Fork 1
/
index.html
363 lines (225 loc) · 11.4 KB
/
index.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
<!DOCTYPE html>
<!--[if IEMobile 7 ]><html class="no-js iem7"><![endif]-->
<!--[if lt IE 9]><html class="no-js lte-ie8"><![endif]-->
<!--[if (gt IE 8)|(gt IEMobile 7)|!(IEMobile)|!(IE)]><!--><html class="no-js" lang="en"><!--<![endif]-->
<head>
<meta charset="utf-8">
<title>BlackHole开发日记-使用三种不同IO模型实现一个DNS代理服务器 - 代码工匠</title>
<meta name="author" content="code4craft">
<meta name="description" content="BlackHoleJ是一个DNS服务器。他的一个功能是,对于它解析不了的DNS请求,它将请求转发到另外一台DNS服务器,然后再将其响应返回给客户端,起到一个DNS代理的作用。 这个功能的实现经历了三个版本,也对应了三个经典的IO模型。 BIO模型(Blocking I/O) …">
<!-- http://t.co/dKP3o1e -->
<meta name="HandheldFriendly" content="True">
<meta name="MobileOptimized" content="320">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="canonical" href="http://code4craft.github.com/blog/2013/04/12/blackhole-io-model/">
<link href="/favicon.png" rel="icon">
<link href="/stylesheets/screen.css" media="screen, projection" rel="stylesheet" type="text/css">
<script src="/javascripts/modernizr-2.0.js"></script>
<script src="/javascripts/ender.js"></script>
<script src="/javascripts/octopress.js" type="text/javascript"></script>
<link href="/atom.xml" rel="alternate" title="代码工匠" type="application/atom+xml">
<!--Fonts from Google"s Web font directory at http://google.com/webfonts -->
<link href="http://fonts.googleapis.com/css?family=PT+Serif:regular,italic,bold,bolditalic" rel="stylesheet" type="text/css">
<link href="http://fonts.googleapis.com/css?family=PT+Sans:regular,italic,bold,bolditalic" rel="stylesheet" type="text/css">
<script type="text/javascript">
var _gaq = _gaq || [];
_gaq.push(['_setAccount', 'UA-41279861-2']);
_gaq.push(['_trackPageview']);
(function() {
var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
})();
</script>
</head>
<body >
<header role="banner"><hgroup>
<h1><a href="/">代码工匠</a></h1>
<h2>Walking The Long Road.</h2>
</hgroup>
</header>
<nav role="navigation"><ul class="subscription" data-subscription="rss">
<li><a href="/atom.xml" rel="subscribe-rss" title="subscribe via RSS">RSS</a></li>
</ul>
<form action="http://google.com/search" method="get">
<fieldset role="search">
<input type="hidden" name="q" value="site:code4craft.github.com" />
<input class="search" type="text" name="q" results="0" placeholder="Search"/>
</fieldset>
</form>
<ul class="main-navigation">
<li><a href="/">Blog</a></li>
<li><a href="/blog/archives">Archives</a></li>
</ul>
</nav>
<div id="main">
<div id="content">
<div>
<article class="hentry" role="article">
<header>
<h1 class="entry-title">BlackHole开发日记-使用三种不同IO模型实现一个DNS代理服务器</h1>
<p class="meta">
<time datetime="2013-04-12T22:16:00+08:00" pubdate data-updated="true">Apr 12<span>th</span>, 2013</time>
</p>
</header>
<div class="entry-content"><p>BlackHoleJ是一个DNS服务器。他的一个功能是,对于它解析不了的DNS请求,它将请求转发到另外一台DNS服务器,然后再将其响应返回给客户端,起到一个DNS代理的作用。</p>
<p><img src="/images/posts/forward.png" alt="image" /></p>
<p>这个功能的实现经历了三个版本,也对应了三个经典的IO模型。</p>
<!-- more -->
<h3>BIO模型(Blocking I/O)</h3>
<p>BlackHoleJ代理模式最开始的IO模型,实现很简单,当client请求过来时,新建一个线程处理,然后再线程中调用DatagramChannel发送UDP包,同时阻塞等待,最后接收到结果后返回。</p>
<pre><code>public byte[] forward(byte[] query) throws IOException {
DatagramChannel dc = null;
dc = DatagramChannel.open();
SocketAddress address = new InetSocketAddress(configure.getDnsHost(),
Configure.DNS_PORT);
dc.connect(address);
ByteBuffer bb = ByteBuffer.allocate(512);
bb.put(query);
bb.flip();
dc.send(bb, address);
bb.clear();
dc.receive(bb);
bb.flip();
byte[] copyOfRange = Arrays.copyOfRange(bb.array(), 0, 512);
return copyOfRange;
}
</code></pre>
<p>其中dc.receive(bb)一步是阻塞的。因为请求外部DNS服务器往往耗时较长,所以为了达到快速响应,不得不开很多线程进行处理。同时每个线程都需要进行轮询dc.receive(bb)是否可用,会消耗更多CPU资源。</p>
<h3>Select模型(I/O multiplexing)</h3>
<p>BlackHoleJ 1.1开始使用的IO模型。因为DNS使用UDP协议,而UDP其实是无连接的,所以所有请求以及响应复用一个DatagramChannel也毫无问题。同时预先使用DatagramChannel.bind(port)绑定某端口,那么对外部DNS服务器的转发和接收都可以使用这个端口。唯一需要做的就是通过DNS包的特征,来判断到底是哪个客户端的请求!而这个特征也很好选择,DNS包的headerId和question域即可满足需求。</p>
<p>发送方的伪代码大概是这样:</p>
<pre><code>public byte[] forward(byte[] queryBytes) {
multiUDPReceiver.putForwardAnswer(query, forwardAnswer);
forward(queryBytes);
forwardAnswer.getLock.getCondition().await();
return answer.getAnswer();
}
</code></pre>
<p>接收方是一个独立的线程,代码大概是这样的:</p>
<pre><code>public void receive() {
ByteBuffer byteBuffer = ByteBuffer.allocate(512);
while (true) {
datagramChannel.receive(byteBuffer);
final byte[] answer = Arrays.copyOfRange(byteBuffer.array(), 0, 512);
getForwardAnswer(answer).setAnswer(answer);
getForwardAnswer(answer).getLock.getCondition().notify();
}
}
</code></pre>
<p>这里forwardAnswer是一个包含了响应结果和一个锁的对象(这里用到了Java的Condition.wait&notify机制,从而使阻塞线程交出控制权,避免更多CPU轮询)。还有一部分是multiUDPReceiver。这里multiUDPReceiver.putForwardAnswer(query, forwardAnswer)实际上是把forwardAnswer注册到一个Map里。</p>
<p>这样做的好处是仅仅在一个线程检查原本的多路I/O是否就绪,也就是I/O multiplexing。这跟Linux下select模型是一样的。</p>
<h3>AIO模型(Asynchronous I/O)</h3>
<p>BlackHoleJ 1.1.3开始,使用了基于回调的AIO模型。这里建立了UDPConnectionResponser对象,里面封装了client的IP和来源端口号。每次收到外部DNS响应时,再根据响应内容找到这个client的IP和来源端口号,重新发送即可。</p>
<p>这实际上就是封装了callback的异步IO。</p>
<p><img src="/images/posts/aio.png" alt="image" /></p>
<p>发送方的伪代码大概是这样:</p>
<pre><code>public void forward(byte[] queryBytes) {
multiUDPReceiver.putForwardAnswer(query, forwardAnswer);
forward(queryBytes);
}
</code></pre>
<p>接收方的代码大概是这样:</p>
<pre><code>public void receive() {
ByteBuffer byteBuffer = ByteBuffer.allocate(512);
while (true) {
datagramChannel.receive(byteBuffer);
final byte[] answer = Arrays.copyOfRange(byteBuffer.array(), 0, 512);
getForwardAnswer(answer).getResponser().response(answer);
}
}
</code></pre>
<p>这里getResponser().response()直接将结果返回给客户端。</p>
<h3>测试:</h3>
<p>使用queryperf进行了测试,使用AIO模型之后,仅仅单线程就达到了40000qps,比1.1.2效率高出了25%,而CPU开销却有了降低。</p>
</div>
<footer>
<p class="meta">
<span class="byline author vcard">Posted by <span class="fn">code4craft</span></span>
<time datetime="2013-04-12T22:16:00+08:00" pubdate data-updated="true">Apr 12<span>th</span>, 2013</time>
<span class="categories">
<a class='category' href='/blog/categories/blackholej/'>BlackHoleJ</a>
</span>
</p>
<div class="sharing">
<a href="http://twitter.com/share" class="twitter-share-button" data-url="http://code4craft.github.com/blog/2013/04/12/blackhole-io-model/" data-via="" data-counturl="http://code4craft.github.com/blog/2013/04/12/blackhole-io-model/" >Tweet</a>
</div>
<p class="meta">
<a class="basic-alignment left" href="/blog/2013/02/25/2012-summary/" title="Previous Post: 2012年总结">« 2012年总结</a>
<a class="basic-alignment right" href="/blog/2013/04/17/intellijshi-yong-xin-de/" title="Next Post: IntelliJ使用心得">IntelliJ使用心得 »</a>
</p>
</footer>
</article>
<section imagesd="comment">
<h1>Add a comment</h1>
<!-- Duoshuo Comment BEGIN -->
<div class="ds-thread"></div>
<script type="text/javascript">
var duoshuoQuery = {short_name:"code4craft"};
(function() {
var ds = document.createElement('script');
ds.type = 'text/javascript';ds.async = true;
ds.src = 'http://static.duoshuo.com/embed.js';
ds.charset = 'UTF-8';
(document.getElementsByTagName('head')[0]
|| document.getElementsByTagName('body')[0]).appendChild(ds);
})();
</script>
<!-- Duoshuo Comment END -->
</section>
</div>
<aside class="sidebar">
<section>
<h1>新浪微博</h1>
<ul id="weibo">
<li>
<iframe
width="100%"
height="550"
class="share_self"
frameborder="0"
scrolling="no"
src="http://widget.weibo.com/weiboshow/index.php?width=0&height=550&ptype=1&speed=0&skin=&isTitle=0&noborder=1&isWeibo=1&isFans=&uid=1487828712&verifier=a3843d95">
</iframe>
</li>
</ul>
</section>
<section>
<h1>Recent Posts</h1>
<ul id="recent_posts">
<li class="post">
<a href="/blog/2016/11/26/chuang-ye-gong-si-zhi-nan-zhi-ban-gong-shi-wang-luo-checklist/">创业公司指南之办公室网络checklist</a>
</li>
<li class="post">
<a href="/blog/2016/11/07/chuang-ye-ri-zhi-%28si-%29-man-xia-lai-de-tuan-dui/">创业日志(四)——慢下来的技术团队</a>
</li>
<li class="post">
<a href="/blog/2016/10/30/chuang-ye-ri-zhi-%28san-%29%5Byi%5D-nian-hou-%5Byi%5D-xie-ji-zhu-shang-de-zong-jie/">创业日志(三)一年来一些技术上的总结</a>
</li>
<li class="post">
<a href="/blog/2016/06/27/yao-you-dian-wei-ji-gan/">要有点危机感</a>
</li>
<li class="post">
<a href="/blog/2016/03/31/xie-zai-webmagickuai-yao-san-nian-de-shi-hou/">写在WebMagic快要三岁的时候</a>
</li>
</ul>
</section>
</aside>
</div>
</div>
<footer role="contentinfo"><p>
Copyright © 2016 - code4craft -
<span class="credit">Powered by <a href="http://octopress.org">Octopress</a></span>
</p>
</footer>
<script type="text/javascript">
(function(){
var twitterWidgets = document.createElement('script');
twitterWidgets.type = 'text/javascript';
twitterWidgets.async = true;
twitterWidgets.src = 'http://platform.twitter.com/widgets.js';
document.getElementsByTagName('head')[0].appendChild(twitterWidgets);
})();
</script>
</body>
</html>