由于厂里爬虫业务需要,我一直想复制国外的初创公司 luminati.io 的代理方案,魔改一下可以应用到厂里的一些业务上。这玩意儿也没啥大不了的,本质上是就是个服务器端转发了 1 次+客户端反向连接转发 1 次的代理隧道之类的东东,我断断续续研究了几个月以后终于打通了。和一般的 http 代理服务器原理一样,服务器端和客户端本质上都是异步并发的 tcp 操作,它们用一个随机数字相互 tcp 握手以后爬虫(浏览器或者 httpclient )设置服务器端为代理,并且在 header 里面加上这个随机数字(为了支持浏览器+https ,这个随机数字似乎只能放在 Proxy-Authorization 中),最后通过爬虫<-->服务器端<-->客户端<-->互联网这样来访问网站。 demo 都是用 php 来实现的,虽然服务器端可以继续用 php ,但是客户端我需要用 java 重写。本来只有 70 行的 php 客户端代码,结果硬生生的花了我几个星期的时间才翻译成了 java 。也许是我 java 水平不够,也许是 NIO 太坑了,总之今天要来记录这些个坑。
由于必须同时保持几十条的 tcp 连接,所以客户端必须是异步的、单线程的和并发的,我在 github 上翻了很久终于找了个安卓的代理的 Demo : https://github.com/dawsonice/KissProxy 。看他的介绍很不错: NIO based 就可以不依赖 netty 之类的(我的业务需要尽量不依赖第三方的库)、 Single Thread 单线程(这是必然的,我肯定不接受线程池方案)、支持 HTTPS 那是必须的,总之我觉得这个 demo 不错于是就打算照着他的例子用 NIO 来写了,然后开启了漫漫的填坑之旅。
我照着这个 KissProxy 就慢慢魔改起来,结果遇到 2 个坑。第一个就是在发起 TCP 连接的时候用了同步的方式: https://github.com/dawsonice/KissProxy/blob/master/src/me/dawson/proxyserver/core/ChannelPair.java#L177 ,单线程情况下这就阻塞了,所以这个代理服务器实现是不对的。解决方法当然是把发起 tcp 请求的 SocketChannel 操作弄成异步的,可是这个 NIO 并没有办法直接对 SocketChannel 设置回调,需要通过 Selector 机制来注册 OP_CONNECT 和 OP_READ 之类的,搞起了虽然麻烦了点不过还是搞定了。
第二个就是 NIO 的 SocketChannel 在写的时候写缓存可能是满的写不进去,需要注册 OP_WRITE 事件等待写缓存可写,他没有考虑这一情况就会导致数据丢失: https://github.com/dawsonice/KissProxy/blob/master/src/me/dawson/proxyserver/core/ChannelPair.java#L235 。我在实际使用的时候就因为 SocketChannel 的写缓存经常满导致出错(因为我的代理相当于经过了 2 次转发,服务器端接收数据包缓存满了的话客户端也发不出去,导致客户端写缓存满容易触发)。总之又注册上了 OP_WRITE 事件,把缓存满的情况考虑进去,但是这个 OP_WRITE 的触发条件是“只要写缓存没满就触发”,而不是“写缓存从满的状态到可以写才触发”这样,这就导致每次 select 就立刻返回了。然后我就怒了这 NIO 居然暴露这么底层的细节给开发者就算了,这 API 设计太反人类了,搞定了之后现在代码已经成了一锅粥了。
然后问题又来了,我发现整个事件循环是吃满 CPU 的, select 如果没有事件返回不是可以阻塞么(我把 OP_WRITE 事件去掉了,因为这个事件总是触发的,然后设置一个超时时间),一看似乎是 JDK 的一个 bug : http://stackoverflow.com/questions/35858537/selector-selecttimeout-returns-0-before-timeout 。为了保险我稍微魔改了一下 select 的机制,如果 select 到的事件为空(排除 OP_WRITE )就 sleep 一小会儿,虽然比较 dirty 不过能 work 就好了。
半个月前的问题: https://www.v2ex.com/t/346155 ,我终于搞定了
这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。
V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。
V2EX is a community of developers, designers and creative people.