基于 Nginx 的动态代理

在实际应用中,遇到了这样一个场景: 已有一个手机 APP 客户端,需要在该 APP 客户端中实现通过 Web 的形式接入其他的应用页面。按照常规的流程,在 APP 中为应用设置入口链接按钮,当用户点击应用入口按钮时,APP 启动 WebView 并打开设置的应用链接即可。 但在该场景中,接入 APP 的应用均部署在内网服务器,外网无法直接访问,因此在 APP 中配置的链接是内网地址,当用户通过外网使用 APP 时,将无法访问接入的 Web 应用。 针对如上场景中遇到的问题,本文中提出了基于 Nginx 实现动态代理的解决方案。

使用代理

在前面的场景中,要实现内网应用能够被外网访问,一般有两种方式:

  • 将应用部署到可被外网访问的服务器,通常为 DMZ 区服务器
  • 使用反向代理服务器,将外网请求代理转发到内网的应用服务器

其中,将内网应用部署到可被外网访问的服务器上的方法,通常受限于可提供的硬件环境、安全控制等方面的问题,并不是解决该类问题的首选方案。因此,通常会在可被外网访问的服务器上部署反向代理服务器,使用代理转发来解决。

目前最常用的软件反向代理服务器有 Apache 和 Nginx 。通过配置文件设置,就可以将特定的链接向应用服务器转发。例如 Nginx 可通过以下简单的配置,即可实现代理转发:

server
{
    listen 80;
    server_name domain.com;
    location /app1 {
        proxy_redirect off;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_pass http://192.168.10.38:3000/app1;
    }
    access_log logs/domain.com.access.log;
}

在以上实例中,Nginx 配置完成后,即可将 http://domain.com/app 的所有请求代理转发到 http://192.168.10.38:3000/app1 地址,这样就可以实现针对 app1 的从外网访问内网应用的代理转发功能。此时 app1 对于外网的访问地址就变成了 http://domain.com/app

通过使用代理服务器, 所有的应用将拥有统一域名 ,而通过 二级目录区分 不同的应用。

在 Apache 中同样可以采用类似的策略进行代理转发,本文中主要以 Nginx 作为实例。

在多个接入多个应用的情况下,只要按照上述实例中的配置,为不同的应用分配不同的二级目录,即可实现向不同应用进行代理转发。这样就可以解决文章开始提出的问题。

但是这一解决方案也存在问题:

  • 当需要新增/修改/删除应用的代理转发配置时,需要人工修改配置文件,并重启代理转发服务器,这将导致服务的暂时不可用。
  • 当接入应用较多时,配置文件将越来越大,对用人工维护造成极大的不便。
  • 当代理转发服务器进行集群化部署时,每次对配置文件的更新,都需要更新所有代理转发服务器,并进行重启,这将增大维护的风险。

针对以上的问题,需要对该访问进行进一步改进。

使用动态代理

如果能够使反向代理服务器动态的通过集中的配置数据更新针对应用的代理配置,就可以解决上述方案中存在的问题。

经过研究分析,本文中提出 动态代理 方案,流程如下:

当请求进入反向代理服务器时,反向代理服务器将分析进入的请求 URL ,识别 URL 中的二级目录(用于区分不同的应用),然后使用该二级目录作为应用标识,到代理配置数据数据中进行查询,获得代理地址的返回结果,然后将该请求转发到对应的应用服务器。

同时,管理人员可以通过特定的管理端,对代理配置数据进行 CRUD 操作,方便管理人员对代理应用配置的实时管理。

通过以上流程,即可实现在不中断服务的情况下,动态修改代理配置。同时也可以确保集群化的反向代理服务器同步更新,都可以获得最新的配置数据。

使用动态代理方案,即可以解决在文章开头提出的问题。

基于 Nginx 实现动态代理

为了实现动态代理方案,需要在反向代理服务器中增加定制的功能。针对这一目的,研究了目前主流的反向代理服务器 Apache 和 Nginx ,结论如下:

Apache 和 Nginx 均可以增加定制的模块以实现定制的功能。但是 Apache 目前必须使用 C 语言按照 Apache 的要求编写模块,这对于开发者要求相对较高。而 Nginx 同样可以使用 C 语言开发扩展模块,但除此之外,目前已有针对 Nginx 开发的 Lua 语言解释器模块,即可以在 Nginx 的配置文件中直接调用 Lua 语言开发的脚本程序,这种方式极大的降低了定制功能开发的难度。因此,采用 Nginx 作为反向代理服务器,使用 Lua 语言作为定制功能开发语言,进行动态代理功能实现。

同时,由于反向代理服务器需要处理大量的代理请求,因此会频繁的读取反向代理配置数据。基于这一情况,选用 Redis 作为数据库,利用其高性能的数据读写,支撑代理配置数据的频繁访问。

根据以上的技术选型,设计流程图如下:

在 Nginx 的配置文件中通过 Lua 解释器模块,调用 Lua 脚本。请求进入 Nginx 后,通过 Lua 脚本处理请求,并连接 Redis 获取当前 URL 对应的应用的代理地址,处理完成后,将代理地址回写到 Nginx 的配置块,由 Nginx 完成后续的代理转发工作。

在运行期间,管理人员可以使用代理管理端,对 Redis 中的代理配置数据进行操作,操作完成后,Nginx 代理服务器将及时读取到最新的配置数据进行转发。

经过调研,在具体开发过程中,采用了基于 Nginx 进行了模块扩展的 OpenResty。OpenResty 基于 Nginx 扩展了大量的模块,其中非常核心的特点就是在 Nginx 中集成了 Lua 解释器,实现了 Nginx 调用 Lua 脚本。同时 OpenResty 还提供了 Lua 语言实现的访问 Redis 的代码模块。

Nginx 动态代理优化

代理配置数据缓存

在实际测试过程中,当访问量较大时,由于 Nginx 服务器每次代理都会查询 Redis ,可能是导致 Redis 压力过大而无法响应,导致请求被阻塞。

为了应对这一问题,在 Nginx 中,使用 Lua 脚本设置内存缓存,从 Redis 获取的代理数据将在一定时间内保留在 Nginx 服务器的内存中,在内存缓存数据有效期内,将不会再重复向 Redis 请求数据。

经过测试,在进行数据缓存优化后,极大的提高了 Redis 访问的稳定性。

Redis 集群化

由于单点 Redis 一旦无法提供服务,将导致 Nginx 代理服务无法正常使用。针对这一问题,需要对 Redis 进行集群化。

目前比较成熟的解决方案是对 Redis 进行主从备份,一个主节点提供对外服务,多个从节点进行数据备份,并在主节点停止服务后产生新的主节点继续提供服务。使用 Redis 提供的 Redis Sentinel 进行主从节点监控,并向 Nginx 提供最新的主节点信息。

Nginx 集群化

随着访问量逐渐增大,单机的 Nginx 将无法再支持过大的访问量,同时单机 Nginx 一旦停止服务,将影响整个系统的正常运行。因此需要将 Nginx 进行集群化,部署多个 Nginx 反向代理服务器,提供同样的服务。

基于 Nginx 的动态代理方案,提供的代理服务为无状态服务,因此可以直接复制 Nginx 以实现集群化。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏冷冷

Spring Cloud Gateway 原生的接口限流该怎么玩

基于Spring Cloud、oAuth2.0开发基于Vue前后分离的开发平台,支持账号、短信、SSO等多种登录,提供配套视频开发教程。

22820
来自专栏流媒体人生

crtmpserver流媒体服务器的介绍与搭建

Adobe的FMS(Flash Media Server)是很好用。但对应着分级授权的是money和有限功能开放。商业的东西既然用不起,也阻碍了我...

34610
来自专栏luxixing

全功能web应用服务器Openresty介绍

wget http://openresty.org/download/ngx_openresty-1.7.10.2.tar.gz

17020
来自专栏Unity游戏开发

Lua基本语法、数据类型、变量

轻量级: 它用标准C语言编写并以源代码形式开放,编译后仅仅一百余K,可以很方便的嵌入别的程序里 可扩展: Lua提供了非常易于使用的扩展接口和机制:由宿主语言...

13340
来自专栏知识分享

1-STM32物联网开发WIFI+GPRS(GPRS入门篇)_简介

实现哪些功能呢!其实GPRS涉及的并不是挺多,官方也给了例子,我只是讲解,然后把我总结的一些东西提供给大家

15920
来自专栏王亚昌的专栏

Lua中的元表和元方法

Lua中每个值都可具有元表。 元表是普通的Lua表,定义了原始值在某些特定操作下的行为。你可通过在值的原表中设置特定的字段来改变作用于该值的操作的某些行为特征。...

27730
来自专栏Java学习录

为什么一线大厂面试必问redis,有啥好问的?

除了5种常用类型,还有bitmaps、hyperloglogs 、geospatial等类型。

14120
来自专栏京程一灯

可以用在 VS Code 中的正则表达式小技巧[每日前端夜话0x68]

你是不是一直都想学正则表达式,但是因为它的复杂性而被推迟了?在本文中,我将向你展示五个易于学习的正则技巧,你可以立即在自己喜欢的文本编辑器中使用它们。

17620
来自专栏人工智能头条

解决 Redis 的疑难杂症

显而易见,如今的 Redis 已经进入了成熟期,但依旧存在很多疑难杂症。数以千计的开发者都在开发和使用这个数据库,它拥有非常完善的文档。

16020

扫码关注云+社区

领取腾讯云代金券

年度创作总结 领取年终奖励