0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

HTTP反向代理服务器的编写程序

我快闭嘴 作者:工程师李察 2018-10-06 10:00 次阅读

如果你经常使用 Node.js 编写 Web 服务端程序,一定对使用 Nginx 作为 反向代理 服务并不陌生。在生产环境中,我们往往需要将程序部署到内网多台服务器上,在一台多核服务器上,为了充分利用所有 CPU 资源,也需要启动多个服务进程,它们分别监听不同的端口。然后使用 Nginx 作为反向代理服务器,接收来自用户浏览器的请求并转发到后端的多台 Web 服务器上。大概工作流程如下图:

HTTP反向代理服务器的编写程序

在 Node.js 上实现一个简单的 HTTP 代理程序还是非常简单的,本文章的例子的核心代码只有 60 多行,只要理解 内置 http 模块 的基本用法即可,具体请看下文。

接口设计与相关技术

使用 http.createServer() 创建的 HTTP 服务器,处理请求的函数格式一般为 function (req, res) {}(下文简称为 requestHandler),其接收两个参数,分别为 http.IncomingMessage 和 http.ServerResponse 对象,我们可以通过这两个对象来取得请求的所有信息并对它进行响应。

主流的 Node.js Web 框架的中间件(比如 connect)一般都有两种形式:

中间件不需要任何初始化参数,则其导出结果为一个 requestHandler

中间件需要初始化参数,则其导出结果为中间件的初始化函数,执行该初始化函数时,传入一个 options 对象,执行后返回一个 requestHandler

为了使代码更规范,在本文例子中,我们将反向代理程序设计成一个中间件的格式,并使用以上第二种接口形式:


HTTP反向代理服务器的编写程序

说明:

上面的代码中,reverseProxy 是反向代理服务器中间件的初始化函数,它接受一个对象参数,servers 是后端服务器地址列表,每个地址为 IP地址:端口 这样的格式

执行 reverseProxy() 后返回一个 function (req, res) {} 这样的函数,用于处理 HTTP 请求,可作为 http.createServer() 和 connect 中间件的 app.use() 的处理函数

当接收到客户端请求时,按顺序循环从 servers 数组中取出一个服务器地址,将请求代理到这个地址的服务器上

服务器在接收到 HTTP 请求后,首先需要发起一个新的 HTTP 请求到要代理的目标服务器,可以使用 http.request() 来发送请求:

HTTP反向代理服务器的编写程序

要将客户端的请求体(Body 部分,在 POST、PUT 这些请求时会有请求体)转发到另一个服务器上,可以使用 Stream 对象的 pipe() 方法,比如:

说明:

req 对象是一个 Readable Stream(可读流),通过 data 事件来接收数据,当收到 end 事件时表示数据接收完毕

res 对象是一个 Writable Stream (可写流),通过 write() 方法来输出数据,end() 方法来结束输出

为了简化从 Readable Stream 监听 data 事件来获取数据并使用 Writable Stream 的 write() 方法来输出,可以使用 Readable Stream 的 pipe() 方法

以上只是提到了实现 HTTP 代理需要的关键技术,相关接口的详细文档可以参考这里:https://nodejs.org/api/http.html#http_http_request_options_callback

当然为了实现一个接口友好的程序,往往还需要很多 额外 的工作,具体请看下文。

简单版本

以下是实现一个简单 HTTP 反向代理服务器的各个文件和代码(没有任何第三方库依赖),为了使代码更简洁,使用了一些最新的 ES 语法特性,需要使用 Node v8.x 最新版本来运行:

文件 proxy.js:


文件 log.js:

const util = require("util"); /** 打印日志 */ module.exports = function log(...args) { const time = new Date().toLocaleString(); console.log(time, util.format(...args)); };

说明:

log.js 文件实现了一个用于打印日志的函数 log(),它可以支持 console.log()一样的用法,并且自动在输出前面加上当前的日期和时间,方便我们浏览日志

reverseProxy() 函数入口使用 assert 模块来进行基本的参数检查,如果参数格式不符合要求即抛出异常,保证可以第一时间让开发者知道,而不是在运行期间发生各种 不可预测 的错误

getTarget() 函数用于循环返回一个目标服务器地址

bindError() 函数用于监听 error 事件,避免整个程序因为没有捕捉网络异常而崩溃,同时可以统一返回出错信息给客户端

为了测试我们的代码运行的效果,我编写了一个简单的程序,文件 server.js:

执行以下命令启动:

nodeserver.js

然后可以通过 curl 命令来查看返回的结果:

curlhttp://127.0.0.1:3000/hello/world

连续执行多次该命令,如无意外输出结果应该是这样的(输出内容端口部分按照顺序循环):

注意:如果使用浏览器来打开该网址,看到的结果顺序可能是不一样的,因为浏览器会自动尝试请求/favicon,这样刷新一次页面实际上是发送了两次请求。

单元测试

上文我们已经完成了一个基本的 HTTP 反向代理程序,也通过简单的方法验证了它是能正常工作的。但是,我们并没有足够的测试,比如只验证了 GET 请求,并没有验证 POST 请求或者其他的请求方法。而且通过手工去做更多的测试也比较麻烦,很容易遗漏。所以,接下来我们要给它加上自动化的单元测试。

在本文中我们选用在 Node.js 界应用广泛的 mocha 作为单元测试框架,搭配使用 supertest 来进行 HTTP 接口请求的测试。由于 supertest 已经自带了一些基本的断言方法,我们暂时不需要 chai 或者 should 这样的第三方断言库。

首先执行 npm init 初始化一个 package.json 文件,并执行以下命令安装 mocha和 supertest:

npminstallmochasupertest--save-dev

然后新建文件 test.js:

说明:

在单元测试开始前,需要通过 before() 来注册回调函数,以便在开始执行测试用例时先把服务器启动起来

同理,通过 after() 注册回调函数,以便在执行完所有测试用例后把服务器关闭以释放资源(否则 mocha 进程不会退出)

使用 supertest 发送请求时,代理服务器不需要监听端口,只需要将 server 实例作为调用参数即可

接着修改 package.json 文件的 scripts 部分:

如果一切正常,我们应该会看到这样的输出结果,其中 passing 这样的提示表示我们的测试完全通过了:

当然以上的测试代码还远远不够,剩下的就交给读者们来实现了。

接口改进

如果要设计成一个比较通用的反向代理中间件,我们还可以通过提供一个生成 http.ClientRequest 的函数来实现在代理时动态修改请求:

然后在原来的 http.request(info, (res2) => {}) 部分可以改为监听 response 事件:

constreq2=http.request(options.request(info));

req2.on("response",res2=>{});

同理,我们也可以通过提供一个函数来修改部分的响应内容:

此处只发散一下思路,具体实现方法和代码就不再赘述了。

总结

本文主要介绍了如何使用内置的 http 模块来创建一个 HTTP 服务器,以及发起一个 HTTP 请求,并简单介绍了如何对 HTTP 接口进行测试。在实现 HTTP 请求代理的过程中,主要是运用了 Stream 对象的 pipe() 方法,关键部分代码只有区区几行。Node.js 中的很多程序都运用了 Stream 这样的思想,将数据当做一个流,使用 pipe 将一个流转换成另一个流,可以看出 Stream 在 Node.js 的重要性。

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • Web
    Web
    +关注

    关注

    2

    文章

    1238

    浏览量

    68452
  • HTTP
    +关注

    关注

    0

    文章

    466

    浏览量

    30312
  • 代理服务器
    +关注

    关注

    0

    文章

    4

    浏览量

    7932
收藏 人收藏

    评论

    相关推荐

    10个关于linux中Squid代理服务器的实用面试问答

    (常被视作代理服务的代名词)就是这样一个应用程序,它不但可以被作为代理服务器,其同时也是在该类工具中比较被广泛使用的一种。本文旨在提高你在遇到关于
    发表于 09-28 10:19

    linux通过代理服务器来上网

    有时,某个服务器无法联网。想联网的话,可以通过代理服务器。下载一个ccproxy,安装完成后,打开该软件,点击‘设置’:
    发表于 07-24 06:41

    网络代理服务器的内存

    网络代理服务器的内存              网络加速产品多采用SDRAM 和DDR SDRAM两种内存。  &nb
    发表于 01-07 14:06 551次阅读

    网络代理服务器的网络接口

    网络代理服务器的网络接口         
    发表于 01-07 14:08 580次阅读

    网络代理服务器

    网络代理服务器              网络代理服务器有别于传统的网络加速软件,它是专门指高速缓存服务器产品,即Cache
    发表于 01-07 14:09 939次阅读

    Apache代理服务器配置说明

    Apache代理服务器 1. 安装apache。 2. 修改\Apache\conf\httpd.conf配置文件, 首先要添加代理服务器模块。找到下面这几行: #LoadModule
    发表于 04-29 16:49 0次下载

    vpn代理服务器_VPN代理软件下载

    vpn代理服务器使用,通过vpn代理登录谷歌电子市场,本文提供了vpn代理软件下载,供vpn代理使用的朋友下载
    发表于 09-14 09:50 111次下载

    反向代理:Web服务器的经纪人

    反向代理:Web服务器的经纪人
    发表于 09-05 10:59 8次下载
    <b class='flag-5'>反向</b><b class='flag-5'>代理</b>:Web<b class='flag-5'>服务器</b>的经纪人

    怎样在树莓派中设置代理服务器

    代理服务器的主机名或IP地址和端口
    的头像 发表于 09-24 16:59 2.5w次阅读

    正向代理反向代理的区别

    Nginx作为时下最流行的HTTP服务器之一,同时它是一个反向代理服务器,提到反向代理服务器,有
    的头像 发表于 05-03 17:42 3311次阅读
    正向<b class='flag-5'>代理</b>和<b class='flag-5'>反向</b><b class='flag-5'>代理</b>的区别

    基于高斯混合模型的Web代理服务器缓存

    基于高斯混合模型的Web代理服务器缓存
    发表于 06-23 16:46 17次下载

    代理服务器IP如何使用,这几点需要注意了

    作为信息的中转站,伪装自己的真实IP,保障上网安全。那么,如何安全且合理的使用代理IP技术呢? 代理服务器是介于客户端和Web服务器之间的另一台服务器,有了它之后,浏览器不是直接到We
    的头像 发表于 07-22 17:19 3871次阅读

    详解Nginx高性能的HTTP反向代理服务器

    Nginx 是一个高性能的 HTTP反向代理服务器,特点是占用内存少,并发能力强,事实上 Nginx 的并发能力确实在同类型的网页服务器中表现较好。
    的头像 发表于 03-16 11:23 2043次阅读

    python代码中如何使用HTTP代理

    HTTP代理就是介于浏览器和web服务器之间的一台服务器,连接代理后,浏览器不再直接向web服务器
    的头像 发表于 09-13 09:24 1051次阅读

    nginx使用学习之正、反向代理

    ,则需要通过代理服务器来访问,这种代理服务就称为正向代理。 简单一点:通过代理服务器来访问服务器
    的头像 发表于 11-13 10:54 237次阅读
    nginx使用学习之正、<b class='flag-5'>反向</b><b class='flag-5'>代理</b>