Ceryx:一个动态 NGINX

反向代理包含数百甚至上千个微服务是一个很有意思的事情,也是我们在 Sourcelair 每天都要面对的事情。这也是为什么我们今天要很高兴地宣布 Ceryx,一个动态反向代理,使用 OpenResty,Lua 和 Flask,可以代理主机上任意多的服务。它的配置是即时生效。在过去几个月里我们一直在做 Ceryx 项目。现在我们将它开源。

在 SourceLair,我们快速提供开发环境并努力让 web 开发可以无障碍,并使用云的力量。我们提供的一个优秀服务之一是每个项目有一个公共的 URL,它始终可用,并自动刷新你的代码。这样我们需要每小时启动和停止多用户容器,并在不停机的情况下路由每个用户的公共 URL 到当前容器。

以前的解决方案

Ceryx 是去年我们开发的内部项目。为了找到解决我们问题的最佳方案,我们尝试了各种技术。在这条路上,我们保持 API 的稳定不受变化的影响。有一个很好的话题没有放到这里,你可以在 API Meetup Athens 找到我们的 slides。

Twisted,MongoDB 和 Redis

首先,我们使用基于 Twisted 自定义反向代理,Twisted 非常好用,是使用 Python 写的事件驱动的网络引擎。如果在 Redis 缓存中没有查到,服务就以 MongoDB 查询做为路由。不管是使用数据库查询,还是调用 API 添加,更新或删除路由,缓存都是流行的方式。对我们来说,它工作得很好,很快 Twisted 有了一个很好用的反向代理 API 可以使用。这个应用让我们错失的一件事情是,Twisted 在默认情况下,每个反向代理头部不能设置成你想要的,在某些情况会导致一些无效的转发。但是用它工作是很方便的。

tproxy和Redis

在 Twisted 之后,我们又尝试了 tproxy。tproxy 是一个使用 Gevent 创建的 TCP 路由代理(第七层),Gevent 深受 Ruby 的 ProxyMachine 影响由著名的 Gunicorn 创建。我们创建了一个查找层用来替代静态路由和文件,它会查找后端的 Redis。我们从 MongoDB 彻底地分离了服务,因为路由是短暂且易于重建的。同样我们将 API 分离出来用 Flask 写了独立的服务。主要的问题是 tproxy 的开发有点被遗弃。最后一次提交是一年前。我们需要重新做一些性能优化,还有一个有趣的 bug,响应没有包含响应长度,并保持开放直到超时。

NGINX 和 etcd

我们已经决定将代理作为一个服务而不是一个盒子,我们调研了 NGINX 和 HAProxy。因为我们非常熟悉前者并且对它的表现很满意。我们所有的前端都是一直使用 NGINX。我们创建了一个监控脚本,它监控etcd 的重要变更,并自动加载 NGINX 的配置。我们还修改了我们的 API,让它和 etcd 一起做为后端工作。这大大改善了性能。但过了几周我们发现配置的变更没有我们想像得快。NGINX 的重新加载几乎是瞬间的。但配置要花一段时间接收人工配置才能生效。导致路由更新变得缓慢。对我们来说最重要的问题是这种重新加载时间超过10秒,导致多次出现“服务已停止"页面,直到新的配置生效。

OpenResty 和 Lua 就是我们的救星
当前,Github 记录关于他们在 NGINX 使用 Lua 脚本对 GitHub pages 进行更频繁的重载 - 之前他们大概 30 分钟重载一次。

当我们在寻找另外一个替代品的时候,对这篇博文非常感兴趣,就开始深入研究 OpenResty - NGINX 的风情版本,使用 Lua JIT 编译,还有其他第三方 NGINX 模块。

我们决定继续用回 Redis 作为后端,因为我们已经准备好 API ,Redis 也已经在内存中,也因此查询速度非常快。我们同时使用 Redis 进行其他服务和缓存,所以不需要再考虑其他集群。

解决方案就是 Ceryx,现在已经开源,提供给每个人使用。这包括了 NGINX lua 脚本和 API,可以轻松的使用 Docker Compose 部署。

把所有的一切都缝合在一起
NGINX 提供几个钩子,可以在请求的几个阶段执行 Lua 代码。Ceryx 只会在代理阶段之前采取行动 -在 "access_by_lua_file" 阶段,即是 Lua 路由器。

此路由器会查询 NGINX 和 Redis 后端的本地内存缓存,以此确定目标主机和将会到来的主机端口,如果没找到就返回一个通配符目标。一个 Redis 查询返回一个结果时,这个结果会被缓存 5 秒,所以后续请求不会影响 Redis - 当需要静态文件(比如 CSS,JavaScript 和图像)的时候这会是一个很好的改进,这时候会同时抛出多个请求。可以通过增加缓存超时来对 Ceryx 进行量身定做,适应各个应用的需求。

与此同时,提供一个简单的 Flask 服务,为路由和使用新路由更新 Redis 后端提供一个 CRUD API。代理和 API 服务共享相同的环境变量,用来配置 Redis,保持一致性。

第一印象

最新的 Ceryx 发布第一周后,我们看到了其在反向代理和大幅减少休眠服务页上的巨大改进。在更多地细节上,升级前的 Ceryx,每个开发会话平均有 10 个页面浏览量,而现在我们仅仅需要 2.5 个。

下一步

Ceryx 是在 MIT 协议下的开源项目,因此我们乐见用户贡献或者提出 bug 以及新功能请求。我们计划添加 StatsD 测量功能到 Ceryx,这样我们可以更好改进和优化一些相应的部分。

文章转载自 开源中国社区 [http://www.oschina.net]

时间: 2024-11-17 05:43:02

Ceryx:一个动态 NGINX的相关文章

如何用PHP来实现一个动态Web服务器_php实例

要是现实一个 web 服务器,那么就需要大概了解 web 服务器的运行原理.先从静态的文本服务器开始,以访问 web 服务器的1.html为例 1.客户端通过发送一个 http 请求到服务器,如果服务器监听的端口号是9002,那么在本机自身测试访问的地址就是http://localhost:9002/1.html. 2.服务器监听着9002端口,那么在收到请求了请求之后,就能从 http head 头中获取到请求里需要访问的 uri 资源在web 目录中的位置. 3.服务器读取需要访问的资源文件

android一个动态添加删除网格视图的demo,可把状态保存在数据库中

转自: http://www.apkbus.com/forum.php?mod=viewthread&tid=166579 一个动态添加删除网格视图的demo,可把状态保存在数据库中,用于下次进来继续用.里面还有对不同item进行跳转,动画效果...总之十分好用,希望给大家带来帮助.有图有真相,亲们 ,赶快行动吧!! 数据库, 动态 本主题由 jnhoodlum 于 2014-4-1 16:32 添加图标 原创 a2.png(158.32 KB, 下载次数: 5) 点击添加进入添加应用页面,打钩

javascrip-jquery如何设置一个动态创建的 $('#').append('')中的ID?并利用它

问题描述 jquery如何设置一个动态创建的 $('#').append('')中的ID?并利用它 就比如像这样的: <script type="text/javascript"> var number=0; function next(){ $('#showdata').append('<input class="number_showdata" id="showdataid'+number+'" type="tex

如何快速实现一个基于Nginx的网站监控场景----需求篇

一切从应用服务监控说起 小明所在的一家小型互联网创业公司一直将应用运行在国内某A云上.该应用采用通用的分布式Nginx+App架构为用户提供电商数据统计的webservice 服务.应用运行至今除偶发各类Bug, 性能问题以外,情况还算良好.   最近,小明的老板给小明布置了一个任务,希望把应用服务监控起来,以提高应用运行质量.老板的需求有三点: 1.    先以应用服务监控为抓手,能 a)     实时统计应用各类服务的调用次数 b)     基于a,实时统计各类服务各类返回值的次数,如200

java动态定时任务-java有关一个动态定时任务的需求求助

问题描述 java有关一个动态定时任务的需求求助 每天都要在某个时间执行一个任务 这个时间是 动态的 从数据库取的 比如今天是要在22点45分15秒执行一个任务 明天的这个时间是 23点45分11秒 以此类推 每天都会有一个动态的时间任务请问这个怎么用java实现 貌似quartz实现不了 解决方案 用java的定时器可以实现,但是需要用两个定时器.一个每日执行,一个每秒执行.实例代码:Task是最终的任务:SheduledTask接收任务时间,每隔一秒匹配一下当前时间,如果匹配就执行Task:

指针-一个动态内存分配的问题,函数执行后p指向哪里?

问题描述 一个动态内存分配的问题,函数执行后p指向哪里? 一下是我的代码: #include<stdio.h> #include<stdlib.h> #include<string.h> char *strcnp(char *sd,char *ds) { char *q=sd; while((*ds++=*sd++)!='') NULL; return q; } int main() { char *a[4]={"abc","def&quo

java-如何获得一个动态创建的textview的ID?

问题描述 如何获得一个动态创建的textview的ID? 我想要在一个android app中动态添加和删除TextView,我现在正在做,但是我在设置和获得TextView的ID的时候碰到了困难.在最后两行代码中我好想得到了空指针异常(et settext和ll.removeView).有人知道我怎么可以动态的设置和获得一个textview的ID么?setId似乎没有工作,还是就是我错了? //删除无关的代码 EditText et = (EditText) view.findViewById

makefile把一个 .cpp文件和它所依赖的几个 .a(静态库文件 ) 生成一个动态库文件.so

问题描述 makefile把一个 .cpp文件和它所依赖的几个 .a(静态库文件 ) 生成一个动态库文件.so 假设编译交叉编译工具路径为dir1(arm-linux-androideabi-gcc-4.9 ),存放生成的.so文件的路径为dir2,test.cpp,依赖1.a,2.a,3.a这三个静态文件库,Makefile文件应该怎么写 解决方案 arm-linux-androideabi-gcc-4.9 -fPIC -shared -o dir2/xxx.so test.cpp 1.a 2

Swift语言实战晋级-第9章 游戏实战-跑酷熊猫-3 显示一个动态的熊猫

原文:Swift语言实战晋级-第9章 游戏实战-跑酷熊猫-3 显示一个动态的熊猫     一个静态的熊猫明显不能满足我们的欲望,接下来我们就让熊猫跑起来.序列帧动画的原理就是不停的切换不同的图片.当我们将一张一张的切换Panda类的跑动文理后,熊猫就跑起来了.那么首先我们需要一个数组常量来储存跑动动画文理,还有一个变量来记录熊猫当前的动作状态. let runFrames = [SKTexture]()   //动作状态,默认值为枚举中的跑 var status = Status.run