继续浏览精彩内容
慕课网APP
程序员的梦工厂
打开
继续
感谢您的支持,我会继续努力的
赞赏金额会直接到老师账户
将二维码发送给自己后长按识别
微信支付
支付宝支付

从前端工程谈资源代理

阿里聚划算技术团队
关注TA
已关注
手记 4
粉丝 89
获赞 714

作者:聚划算资深前端开发工程师 李辰号(花名:良田)


注:文中代码大部分为闭源代码,所以只有摘录,仅供参考。本篇主要以提供思路为主,因涉及内容有专利等相关法律问题,禁止一切形式的转载。

前端工程这个篇幅,社区讨论已经泛滥,也不想过多的赘述,收集了几篇非常不错的文章分享给大家
前端工程-张云龙
前端工程化知识要点回顾
前端工程化:云构建

工程化的一些思考

工程化解决了什么?

相信前面几篇长篇大论已经看的是眼花缭乱了。那么工程化解决的到底是什么问题呢?正如云龙文章中提到的:大规模的、大批量的、以团队协作为基础的应用开发中面临的效率、一致性、和性能问题。

是否需要工程化

是否需要工程化,取决于团队大小和协作方式。你的团队是你一个人维护所有代码,又不要求什么访问性能。工程化只会增加你的负担,对你丝毫没有价值,反而是系统的优化可能更适合。

如果团队只有10个人不到。那么选择基本的打包发布就足够应付日常工作。也不必要引入像 FIS Webpack 这种庞大复杂的工程化工具,因为它带来的维护成本几乎是你不愿意看到的。

如果你的团队已经有超过20人,那么考虑到协作,分治,重用等问题,工程化、组件化思想是非常值得借鉴学习的。

工程化还可以做什么?

在现有工程化解决方案中,大部分都忽视了一个点,那就是代理。类似的产品工具层出不穷,比如 fiddler、Charles。 然而在夸终端和多协议的大无线时代,这些工具有时候略显不足。尤其是在结合业务定制方面。比如:只将线上的某个资源的一个片段代理到本地的某个文件,并且对这个文件进行一定的修改。

如何实现一个代理服务

首先,我们要对 HTTP,SSL/HTTPS,TCP,UDP这些协议有个基本的了解,其次,要知道 DNS 以及整个 web 是如何工作的。并且要了解一个重要的知识点SNICallback

启动一个服务器在 node 中是非常简单的。这里不过多的描述。参考 node 官方示例即可。

但是如果希望可以承载多个协议,多个域名,多个服务的时候,只使用 HTTP/HTTPS 则不能满足需求。这个时候,需要net 模块来承担更多的转发能力。

摘录部分代码如下:


function (client) {
  client.setTimeout(TIMEOUT);
  util.log('%s:%s', client.remoteAddress, client.remotePort);
  client.on('error', function (e) {
    client.destroy();
    util.error(e);
  }).once('data', function (chunk) {
    client.pause();
    if (chunk[0] === 0x05 && chunk[1] === 0x01) {
      // socks5 proxy
      _socks5(client);
      return;
    }
    var req = chunk.toString().split('\r\n')[0].split(' ');
    // http proxy
    if (req[2] === 'HTTP/1.1') {
      var reqInfo = require('url').parse(req[1], true);
      var protocol = 'http';
      var resData = null;
      // ssl confirm
      if (reqInfo.port === 443  req[0] === 'CONNECT') {
        chunk = null;
        protocol = 'https';
        resData = new Buffer("HTTP/1.1 200 Connection established\r\nConnection: close\r\n\r\n");
      }
      // request debug server
      if (protocol === 'http' && reqInfo.pathname && reqInfo.pathname.match(/^\/\~\//)) {
        return _debug(client, chunk);
      }
      // http proxy
      return _proxy(client, protocol, chunk, resData);
    }
    util.error(req);
  });
}

这样可以将HTTP/HTTPS 请求的首次确认通过 TCP 代理拦截(注:这里不是直接请求哦,而是通过设置设备代理转发而来的请求。如 IOS 设备的wifi http 代理,IOS 支持 PAC,其他设备则使用普通http代理方式),然后实施分流(rootCA 需要手动安装,也就是为什么会有一个调试服务器存在的原因,用于生成根证书并提供在线服务用的)

有了基本的 TCP 拦截,HTTPS 服务也在 http 代理的基础之上通过 HTTP 隧道建立起来。这个时候,另外一个知识点就需要发挥作用了。

http 属于明文协议,可以轻松转发,然而 SSL 的数据都是加密数据。所以在 TCP 的代理方面我们也无可奈何,于是我们需要另外一个知识点:SNICallback。

摘录代码如下:


this.server = this.options.protocol === 'https' ? https.createServer({
  SNICallback: function SNI(servername,cb) {
    debug('SNICallback', servername)
    certMgr.getCert(servername, function(certDir, keyContent, certContent){
      var ctx = require('tls').createSecureContext({
        key : keyContent,
        cert: certContent
      });
      cb(null,ctx);
    });
  },
  ca: certMgr.getCA()
}, app.handle('https', this.options.ip)) : http.createServer(app.handle('http', this.options.ip));
this.server.listen(address);

SNICallback 是在单一端口提供多个服务的可延展功能。在代理服务中是非常适合的一个应用场景。(如果你是线上 node 服务,TLS 部分,建议都用 nginx 搞定,这样大部分 EDS 的负载均衡,稳定性都能够得到保障。)
到这一步,我们所有的数据都转化成为了明文,也就是说:我们可以随便折腾数据来回吞吐了。=_=!

最后一步,当然就是线上线下内容替换了。尤其是在 html 片段替换中颇为有用。通过一些常规的类库,如:request 都可以快速进行资源的远程代理。而本地代理服务则需要我们自己来实现。这个时候,开篇文章中的工程化中,大部分都提到过本地服务器的概念。我们可以通过端口转发,将某些文件直接转发到本地,以实现我们线上问题,线下调试的复杂型开发调试需求。

啰嗦的说了很多,很多知识点也只是一带而过。毕竟资源代理涉及的知识,也不是短短的一千多字可以描述的清楚的。如果大家对于资源代理或者 node 有浓厚兴趣。欢迎微博 @jserli 。我们可以再进行深度讨论。

打开App,阅读手记
24人推荐
发表评论
随时随地看视频慕课网APP